From 20a9c03a165ada998f580dcf4d315020d41f4d50 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 24 Apr 2025 15:11:42 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/reports.gitlab-ci.yml | 20 - .gitlab/ci/rules.gitlab-ci.yml | 12 - Gemfile | 2 +- Gemfile.checksum | 3 +- Gemfile.lock | 16 +- Gemfile.next.checksum | 3 +- Gemfile.next.lock | 16 +- .../javascripts/diffs/components/app.vue | 2 + .../diffs/components/diffs_file_tree.vue | 7 + .../diffs/components/tree_list.vue | 36 +- .../queries/member_groups.query.graphql | 2 +- .../groups/your_work/graphql/resolvers.js | 2 +- .../groups/your_work/graphql/utils.js | 3 + app/assets/javascripts/ide/stores/utils.js | 8 + .../fragments/base_group.fragment.graphql | 26 - .../graphql/fragments/group.fragment.graphql | 30 +- .../graphql/queries/groups.query.graphql | 2 +- .../pages/users/activity_calendar.js | 2 +- .../rapid_diffs/app/file_browser.vue | 13 + .../javascripts/rapid_diffs/app/index.js | 5 +- .../rapid_diffs/app/init_file_browser.js | 22 +- ...ist_item_delayed_deletion_modal_footer.vue | 48 + .../group_list_item_delete_modal.vue | 49 +- .../group_list_item_inactive_badge.vue | 41 + .../groups_list/groups_list_item.vue | 11 +- .../components/groups_list/utils.js | 28 +- .../work_items/pages/work_items_list_app.vue | 9 +- .../stylesheets/page_bundles/profile.scss | 2 +- .../rapid_diffs/app_component.html.haml | 2 +- app/components/rapid_diffs/app_component.rb | 2 + .../concerns/rapid_diffs/resource.rb | 2 +- app/controllers/users_controller.rb | 2 + app/events/groups/group_deleted_event.rb | 3 +- app/graphql/types/group_type.rb | 2 +- app/helpers/events_helper.rb | 10 +- app/services/draft_notes/publish_service.rb | 3 +- app/services/groups/destroy_service.rb | 13 +- app/services/todo_service.rb | 4 +- app/views/events/_event.html.haml | 43 +- app/views/events/event/_private.html.haml | 14 +- .../merge_requests/rapid_diffs.html.haml | 2 +- .../personal_access_tokens/_dpop.html.haml | 6 +- app/views/users/_overview.html.haml | 2 +- app/views/users/calendar_activities.html.haml | 31 +- doc/api/graphql/reference/_index.md | 1 + doc/integration/saml.md | 18 +- .../application_security/policies/_index.md | 23 + doc/user/profile/personal_access_tokens.md | 17 +- doc/user/project/codeowners/reference.md | 4 +- .../repository/code_suggestions/_index.md | 2 + .../databases/postgresql/executor.rb | 10 +- locale/gitlab.pot | 32 +- scripts/frontend/quarantined_vue3_specs.txt | 1 - .../rapid_diffs/app_component_spec.rb | 12 + .../rapid_diffs/user_views_diffs_spec.rb | 13 + spec/frontend/diffs/components/app_spec.js | 8 + .../diffs/components/diffs_file_tree_spec.js | 11 +- .../diffs/components/tree_list_spec.js | 118 +- .../shared/components/groups_view_spec.js | 4 +- spec/frontend/rapid_diffs/app/app_spec.js | 29 +- .../rapid_diffs/app/file_browser_spec.js | 9 + .../rapid_diffs/app/init_file_browser_spec.js | 7 + ...item_delayed_deletion_modal_footer_spec.js | 62 + .../group_list_item_delete_modal_spec.js | 115 +- .../group_list_item_inactive_badge_spec.js | 45 + .../groups_list/groups_list_item_spec.js | 16 +- .../components/groups_list/utils_spec.js | 76 +- .../graphql/mock_query.query.graphql | 5 + .../components/work_items_list_app_spec.js | 21 + spec/graphql/types/group_type_spec.rb | 17 +- spec/helpers/events_helper_spec.rb | 12 +- .../ast/logger_analyzer_spec.rb | 2 +- .../draft_notes/publish_service_spec.rb | 23 +- spec/services/groups/destroy_service_spec.rb | 15 +- spec/services/todo_service_spec.rb | 3 +- spec/workers/every_sidekiq_worker_spec.rb | 3 +- vendor/gems/graphql/.gitlab-ci.yml | 24 - vendor/gems/graphql/.yardopts | 5 - vendor/gems/graphql/CHANGELOG-enterprise.md | 198 - vendor/gems/graphql/CHANGELOG-pro.md | 1331 -- vendor/gems/graphql/CHANGELOG-relay.md | 176 - vendor/gems/graphql/CHANGELOG.md | 4646 ----- vendor/gems/graphql/CNAME | 1 - vendor/gems/graphql/Gemfile | 25 - vendor/gems/graphql/Gemfile.lock | 324 - vendor/gems/graphql/MIT-LICENSE | 20 - vendor/gems/graphql/Rakefile | 240 - .../benchmark/abstract_fragments.graphql | 41 - .../benchmark/abstract_fragments_2.graphql | 64 - .../gems/graphql/benchmark/batch_loading.rb | 138 - .../gems/graphql/benchmark/big_query.graphql | 476 - .../gems/graphql/benchmark/big_schema.graphql | 4202 ----- vendor/gems/graphql/benchmark/run.rb | 700 - vendor/gems/graphql/benchmark/schema.graphql | 118 - .../cop/development/context_is_passed_cop.rb | 44 - .../graphql/cop/development/no_eval_cop.rb | 18 - .../graphql/cop/development/no_focus_cop.rb | 21 - .../cop/development/none_without_block_cop.rb | 47 - .../cop/development/trace_methods_cop.rb | 93 - .../gems/graphql/gemfiles/mongoid_8.gemfile | 17 - .../gems/graphql/gemfiles/mongoid_9.gemfile | 15 - .../gems/graphql/gemfiles/rails_7.0.gemfile | 18 - .../gems/graphql/gemfiles/rails_7.1.gemfile | 17 - .../gemfiles/rails_7.1_postgresql.gemfile | 17 - .../graphql/gemfiles/rails_master.gemfile | 26 - .../graphql/graphql-c_parser/CHANGELOG.md | 43 - vendor/gems/graphql/graphql-c_parser/Rakefile | 10 - .../ext/graphql_c_parser_ext/extconf.rb | 4 - .../graphql_c_parser_ext.c | 22 - .../graphql_c_parser_ext.h | 8 - .../ext/graphql_c_parser_ext/lexer.c | 2040 --- .../ext/graphql_c_parser_ext/lexer.h | 6 - .../ext/graphql_c_parser_ext/lexer.rl | 459 - .../ext/graphql_c_parser_ext/parser.c | 3341 ---- .../ext/graphql_c_parser_ext/parser.h | 5 - .../ext/graphql_c_parser_ext/parser.y | 962 - .../graphql-c_parser/graphql-c_parser.gemspec | 27 - .../graphql-c_parser/lib/graphql-c_parser.rb | 2 - .../graphql-c_parser/lib/graphql/c_parser.rb | 158 - .../lib/graphql/c_parser/version.rb | 7 - vendor/gems/graphql/graphql-ruby.png | Bin 4320 -> 0 bytes vendor/gems/graphql/graphql-ruby.svg | 1 - vendor/gems/graphql/graphql.gemspec | 58 - vendor/gems/graphql/guides/CNAME | 1 - vendor/gems/graphql/guides/_config.yml | 42 - .../gems/graphql/guides/_layouts/default.html | 87 - .../graphql/guides/_layouts/doc_stub.html | 9 - .../gems/graphql/guides/_layouts/guide.html | 82 - .../gems/graphql/guides/_plugins/api_doc.rb | 249 - vendor/gems/graphql/guides/_sass/reset.scss | 48 - vendor/gems/graphql/guides/_tasks/site.rb | 195 - .../guides/authorization/authorization.md | 130 - .../authorization/can_can_integration.md | 387 - .../graphql/guides/authorization/overview.md | 137 - .../authorization/pundit_integration.md | 517 - .../graphql/guides/authorization/scoping.md | 68 - .../guides/authorization/visibility.md | 168 - .../graphql/guides/changesets/definition.md | 300 - .../graphql/guides/changesets/installation.md | 86 - .../graphql/guides/changesets/overview.md | 52 - .../graphql/guides/changesets/releases.md | 85 - vendor/gems/graphql/guides/css/main.scss | 746 - .../graphql/guides/dataloader/adopting.md | 99 - .../guides/dataloader/async_dataloader.md | 80 - .../graphql/guides/dataloader/dataloader.md | 42 - .../graphql/guides/dataloader/overview.md | 140 - .../graphql/guides/dataloader/parallelism.md | 105 - .../gems/graphql/guides/dataloader/sources.md | 176 - .../gems/graphql/guides/dataloader/testing.md | 87 - .../guides/defer/defer-graphiql-gif.gif | Bin 82127 -> 0 bytes vendor/gems/graphql/guides/defer/graphiql.md | 72 - vendor/gems/graphql/guides/defer/overview.md | 61 - vendor/gems/graphql/guides/defer/setup.md | 169 - vendor/gems/graphql/guides/defer/stream.md | 49 - vendor/gems/graphql/guides/defer/usage.md | 35 - vendor/gems/graphql/guides/development.md | 251 - .../graphql/guides/errors/error_handling.md | 50 - .../graphql/guides/errors/execution_errors.md | 83 - vendor/gems/graphql/guides/errors/overview.md | 65 - .../gems/graphql/guides/errors/type_errors.md | 35 - vendor/gems/graphql/guides/faq.md | 71 - .../gems/graphql/guides/fields/arguments.md | 176 - .../graphql/guides/fields/introduction.md | 275 - vendor/gems/graphql/guides/fields/limits.md | 30 - .../gems/graphql/guides/fields/resolvers.md | 212 - .../gems/graphql/guides/fields/validation.md | 79 - vendor/gems/graphql/guides/getting_started.md | 151 - .../gems/graphql/guides/graphql-ruby-dark.png | Bin 2287 -> 0 bytes .../gems/graphql/guides/graphql-ruby-icon.png | Bin 4106 -> 0 bytes vendor/gems/graphql/guides/graphql-ruby.png | Bin 5271 -> 0 bytes vendor/gems/graphql/guides/guides.html | 71 - vendor/gems/graphql/guides/index.html | 109 - .../guides/javascript_client/ably_key.png | Bin 123820 -> 0 bytes .../javascript_client/apollo_subscriptions.md | 248 - .../graphiql_subscriptions.md | 94 - .../guides/javascript_client/overview.md | 30 - .../javascript_client/relay_subscriptions.md | 209 - .../graphql/guides/javascript_client/sync.md | 329 - .../javascript_client/urql_subscriptions.md | 54 - vendor/gems/graphql/guides/js/search.js | 103 - .../graphql/guides/language_tools/c_parser.md | 25 - .../graphql/guides/language_tools/visitor.md | 143 - .../active_operation_limiter_dashboard.png | Bin 54581 -> 0 bytes .../guides/limiters/active_operations.md | 100 - .../graphql/guides/limiters/deployment.md | 94 - .../gems/graphql/guides/limiters/overview.md | 26 - vendor/gems/graphql/guides/limiters/redis.md | 18 - .../gems/graphql/guides/limiters/runtime.md | 114 - .../limiters/runtime_limiter_dashboard.png | Bin 53130 -> 0 bytes .../graphql/guides/limiters/soft_button.png | Bin 4735 -> 0 bytes .../mutations/mutation_authorization.md | 180 - .../guides/mutations/mutation_classes.md | 192 - .../guides/mutations/mutation_errors.md | 148 - .../graphql/guides/mutations/mutation_root.md | 44 - .../graphql/guides/object_cache/caching.md | 202 - .../graphql/guides/object_cache/memcached.md | 18 - .../graphql/guides/object_cache/overview.md | 45 - .../guides/object_cache/query-with-cache.png | Bin 13044 -> 0 bytes .../object_cache/query-without-cache.png | Bin 18208 -> 0 bytes .../gems/graphql/guides/object_cache/redis.md | 64 - .../object_cache/runtime_considerations.md | 67 - .../guides/object_cache/schema_setup.md | 107 - .../guides/operation_store/access_control.md | 31 - .../operation_store/active_record_backend.md | 119 - .../guides/operation_store/add_a_client.png | Bin 51797 -> 0 bytes .../guides/operation_store/client_workflow.md | 69 - .../guides/operation_store/getting_started.md | 118 - .../guides/operation_store/graphql_ui.png | Bin 50375 -> 0 bytes .../operation_store/operation_index.png | Bin 72173 -> 0 bytes .../guides/operation_store/overview.md | 119 - .../guides/operation_store/redis_backend.md | 22 - .../guides/operation_store/request_after.png | Bin 28830 -> 0 bytes .../guides/operation_store/request_before.png | Bin 23142 -> 0 bytes .../operation_store/server_management.md | 77 - .../guides/operation_store/sync_example.png | Bin 101088 -> 0 bytes .../guides/pagination/connection_concepts.md | 133 - .../gems/graphql/guides/pagination/cursors.md | 42 - .../guides/pagination/custom_connections.md | 134 - .../graphql/guides/pagination/overview.md | 16 - .../pagination/stable_relation_connections.md | 95 - .../guides/pagination/using_connections.md | 136 - .../guides/pro/checksums/graphql-1.26.1.txt | 1 - .../checksums/graphql-enterprise-1.0.0.txt | 1 - .../checksums/graphql-enterprise-1.0.1.txt | 1 - .../checksums/graphql-enterprise-1.1.0.txt | 1 - .../checksums/graphql-enterprise-1.1.1.txt | 1 - .../checksums/graphql-enterprise-1.1.10.txt | 1 - .../checksums/graphql-enterprise-1.1.11.txt | 1 - .../checksums/graphql-enterprise-1.1.12.txt | 1 - .../checksums/graphql-enterprise-1.1.13.txt | 1 - .../checksums/graphql-enterprise-1.1.14.txt | 1 - .../checksums/graphql-enterprise-1.1.2.txt | 1 - .../checksums/graphql-enterprise-1.1.3.txt | 1 - .../checksums/graphql-enterprise-1.1.4.txt | 1 - .../checksums/graphql-enterprise-1.1.5.txt | 1 - .../checksums/graphql-enterprise-1.1.6.txt | 1 - .../checksums/graphql-enterprise-1.1.7.txt | 1 - .../checksums/graphql-enterprise-1.1.8.txt | 1 - .../checksums/graphql-enterprise-1.1.9.txt | 1 - .../checksums/graphql-enterprise-1.2.0.txt | 1 - .../checksums/graphql-enterprise-1.3.0.txt | 1 - .../checksums/graphql-enterprise-1.3.1.txt | 1 - .../checksums/graphql-enterprise-1.3.2.txt | 1 - .../checksums/graphql-enterprise-1.3.3.txt | 1 - .../checksums/graphql-enterprise-1.3.4.txt | 1 - .../checksums/graphql-enterprise-1.4.0.txt | 1 - .../checksums/graphql-enterprise-1.4.1.txt | 1 - .../checksums/graphql-enterprise-1.4.2.txt | 1 - .../checksums/graphql-enterprise-1.5.0.txt | 1 - .../checksums/graphql-enterprise-1.5.1.txt | 1 - .../checksums/graphql-enterprise-1.5.2.txt | 1 - .../checksums/graphql-enterprise-1.5.3.txt | 1 - .../checksums/graphql-enterprise-1.5.4.txt | 1 - .../checksums/graphql-enterprise-1.5.5.txt | 1 - .../checksums/graphql-enterprise-1.5.6.txt | 1 - .../pro/checksums/graphql-pro-1.0.0.txt | 1 - .../pro/checksums/graphql-pro-1.0.1.txt | 1 - .../pro/checksums/graphql-pro-1.0.2.txt | 1 - .../pro/checksums/graphql-pro-1.0.3.txt | 1 - .../pro/checksums/graphql-pro-1.0.4.txt | 1 - .../pro/checksums/graphql-pro-1.1.0.txt | 1 - .../pro/checksums/graphql-pro-1.1.1.txt | 1 - .../pro/checksums/graphql-pro-1.10.0.txt | 1 - .../pro/checksums/graphql-pro-1.10.1.txt | 1 - .../pro/checksums/graphql-pro-1.10.2.txt | 1 - .../pro/checksums/graphql-pro-1.10.3.txt | 0 .../pro/checksums/graphql-pro-1.10.4.txt | 1 - .../pro/checksums/graphql-pro-1.10.5.txt | 1 - .../pro/checksums/graphql-pro-1.10.6.txt | 1 - .../pro/checksums/graphql-pro-1.10.7.txt | 1 - .../pro/checksums/graphql-pro-1.10.8.txt | 1 - .../pro/checksums/graphql-pro-1.11.0.txt | 1 - .../pro/checksums/graphql-pro-1.12.0.txt | 1 - .../pro/checksums/graphql-pro-1.12.1.txt | 1 - .../pro/checksums/graphql-pro-1.12.2.txt | 1 - .../pro/checksums/graphql-pro-1.13.0.txt | 1 - .../pro/checksums/graphql-pro-1.13.1.txt | 1 - .../pro/checksums/graphql-pro-1.13.2.txt | 1 - .../pro/checksums/graphql-pro-1.13.3.txt | 1 - .../pro/checksums/graphql-pro-1.13.4.txt | 1 - .../pro/checksums/graphql-pro-1.13.5.txt | 1 - .../pro/checksums/graphql-pro-1.13.6.txt | 1 - .../pro/checksums/graphql-pro-1.14.0.txt | 1 - .../pro/checksums/graphql-pro-1.14.1.txt | 1 - .../pro/checksums/graphql-pro-1.15.0.txt | 1 - .../pro/checksums/graphql-pro-1.15.2.txt | 1 - .../pro/checksums/graphql-pro-1.15.3.txt | 1 - .../pro/checksums/graphql-pro-1.15.4.txt | 1 - .../pro/checksums/graphql-pro-1.15.5.txt | 1 - .../pro/checksums/graphql-pro-1.15.6.txt | 1 - .../pro/checksums/graphql-pro-1.15.7.txt | 1 - .../pro/checksums/graphql-pro-1.16.0.txt | 1 - .../pro/checksums/graphql-pro-1.16.1.txt | 1 - .../pro/checksums/graphql-pro-1.16.2.txt | 1 - .../pro/checksums/graphql-pro-1.17.0.txt | 1 - .../pro/checksums/graphql-pro-1.17.1.txt | 1 - .../pro/checksums/graphql-pro-1.17.10.txt | 1 - .../pro/checksums/graphql-pro-1.17.11.txt | 1 - .../pro/checksums/graphql-pro-1.17.12.txt | 1 - .../pro/checksums/graphql-pro-1.17.13.txt | 1 - .../pro/checksums/graphql-pro-1.17.14.txt | 1 - .../pro/checksums/graphql-pro-1.17.15.txt | 1 - .../pro/checksums/graphql-pro-1.17.2.txt | 1 - .../pro/checksums/graphql-pro-1.17.3.txt | 1 - .../pro/checksums/graphql-pro-1.17.4.txt | 1 - .../pro/checksums/graphql-pro-1.17.5.txt | 1 - .../pro/checksums/graphql-pro-1.17.6.txt | 1 - .../pro/checksums/graphql-pro-1.17.7.txt | 1 - .../pro/checksums/graphql-pro-1.17.8.txt | 1 - .../pro/checksums/graphql-pro-1.17.9.txt | 1 - .../pro/checksums/graphql-pro-1.18.0.txt | 1 - .../pro/checksums/graphql-pro-1.18.1.txt | 1 - .../pro/checksums/graphql-pro-1.18.2.txt | 1 - .../pro/checksums/graphql-pro-1.18.3.txt | 1 - .../pro/checksums/graphql-pro-1.19.0.txt | 1 - .../pro/checksums/graphql-pro-1.19.1.txt | 1 - .../pro/checksums/graphql-pro-1.19.2.txt | 1 - .../pro/checksums/graphql-pro-1.2.0.txt | 1 - .../pro/checksums/graphql-pro-1.2.1.txt | 1 - .../pro/checksums/graphql-pro-1.2.2.txt | 1 - .../pro/checksums/graphql-pro-1.2.3.txt | 1 - .../pro/checksums/graphql-pro-1.20.0.txt | 1 - .../pro/checksums/graphql-pro-1.20.1.txt | 1 - .../pro/checksums/graphql-pro-1.20.2.txt | 1 - .../pro/checksums/graphql-pro-1.20.3.txt | 1 - .../pro/checksums/graphql-pro-1.20.4.txt | 1 - .../pro/checksums/graphql-pro-1.21.0.txt | 1 - .../pro/checksums/graphql-pro-1.21.1.txt | 1 - .../pro/checksums/graphql-pro-1.21.2.txt | 1 - .../pro/checksums/graphql-pro-1.21.3.txt | 1 - .../pro/checksums/graphql-pro-1.21.4.txt | 1 - .../pro/checksums/graphql-pro-1.21.5.txt | 1 - .../pro/checksums/graphql-pro-1.21.6.txt | 1 - .../pro/checksums/graphql-pro-1.22.0.txt | 1 - .../pro/checksums/graphql-pro-1.22.1.txt | 1 - .../pro/checksums/graphql-pro-1.22.2.txt | 1 - .../pro/checksums/graphql-pro-1.22.3.txt | 1 - .../pro/checksums/graphql-pro-1.23.0.txt | 1 - .../pro/checksums/graphql-pro-1.23.1.txt | 1 - .../pro/checksums/graphql-pro-1.23.2.txt | 1 - .../pro/checksums/graphql-pro-1.23.3.txt | 1 - .../pro/checksums/graphql-pro-1.23.4.txt | 1 - .../pro/checksums/graphql-pro-1.23.5.txt | 1 - .../pro/checksums/graphql-pro-1.23.6.txt | 1 - .../pro/checksums/graphql-pro-1.23.7.txt | 1 - .../pro/checksums/graphql-pro-1.23.8.txt | 1 - .../pro/checksums/graphql-pro-1.23.9.txt | 1 - .../pro/checksums/graphql-pro-1.24.0.txt | 1 - .../pro/checksums/graphql-pro-1.24.1.txt | 1 - .../pro/checksums/graphql-pro-1.24.10.txt | 1 - .../pro/checksums/graphql-pro-1.24.11.txt | 1 - .../pro/checksums/graphql-pro-1.24.12.txt | 1 - .../pro/checksums/graphql-pro-1.24.13.txt | 1 - .../pro/checksums/graphql-pro-1.24.14.txt | 1 - .../pro/checksums/graphql-pro-1.24.15.txt | 1 - .../pro/checksums/graphql-pro-1.24.2.txt | 1 - .../pro/checksums/graphql-pro-1.24.3.txt | 1 - .../pro/checksums/graphql-pro-1.24.4.txt | 1 - .../pro/checksums/graphql-pro-1.24.5.txt | 1 - .../pro/checksums/graphql-pro-1.24.6.txt | 1 - .../pro/checksums/graphql-pro-1.24.7.txt | 1 - .../pro/checksums/graphql-pro-1.24.8.txt | 1 - .../pro/checksums/graphql-pro-1.24.9.txt | 1 - .../pro/checksums/graphql-pro-1.25.0.txt | 1 - .../pro/checksums/graphql-pro-1.25.1.txt | 1 - .../pro/checksums/graphql-pro-1.25.2.txt | 1 - .../pro/checksums/graphql-pro-1.26.0.txt | 1 - .../pro/checksums/graphql-pro-1.26.2.txt | 1 - .../pro/checksums/graphql-pro-1.26.3.txt | 1 - .../pro/checksums/graphql-pro-1.26.5.txt | 1 - .../pro/checksums/graphql-pro-1.27.0.txt | 1 - .../pro/checksums/graphql-pro-1.27.1.txt | 1 - .../pro/checksums/graphql-pro-1.27.2.txt | 1 - .../pro/checksums/graphql-pro-1.27.4.txt | 1 - .../pro/checksums/graphql-pro-1.27.5.txt | 1 - .../pro/checksums/graphql-pro-1.27.6.txt | 1 - .../pro/checksums/graphql-pro-1.28.0.txt | 1 - .../pro/checksums/graphql-pro-1.28.1.txt | 1 - .../pro/checksums/graphql-pro-1.29.0.txt | 1 - .../pro/checksums/graphql-pro-1.29.1.txt | 1 - .../pro/checksums/graphql-pro-1.29.2.txt | 1 - .../pro/checksums/graphql-pro-1.29.3.txt | 1 - .../pro/checksums/graphql-pro-1.29.4.txt | 1 - .../pro/checksums/graphql-pro-1.3.0.txt | 1 - .../pro/checksums/graphql-pro-1.4.0.txt | 1 - .../pro/checksums/graphql-pro-1.4.1.txt | 1 - .../pro/checksums/graphql-pro-1.4.2.txt | 1 - .../pro/checksums/graphql-pro-1.4.3.txt | 1 - .../pro/checksums/graphql-pro-1.4.4.txt | 1 - .../pro/checksums/graphql-pro-1.4.5.txt | 1 - .../pro/checksums/graphql-pro-1.4.6.txt | 1 - .../pro/checksums/graphql-pro-1.4.7.txt | 1 - .../pro/checksums/graphql-pro-1.4.8.txt | 0 .../pro/checksums/graphql-pro-1.5.0.txt | 1 - .../pro/checksums/graphql-pro-1.5.1.txt | 1 - .../pro/checksums/graphql-pro-1.5.2.txt | 1 - .../pro/checksums/graphql-pro-1.5.3.txt | 1 - .../pro/checksums/graphql-pro-1.5.4.txt | 1 - .../pro/checksums/graphql-pro-1.5.5.txt | 1 - .../pro/checksums/graphql-pro-1.5.6.txt | 1 - .../pro/checksums/graphql-pro-1.5.7.txt | 1 - .../pro/checksums/graphql-pro-1.5.8.txt | 1 - .../pro/checksums/graphql-pro-1.5.9.txt | 1 - .../pro/checksums/graphql-pro-1.6.0.txt | 1 - .../pro/checksums/graphql-pro-1.6.1.txt | 1 - .../pro/checksums/graphql-pro-1.6.2.txt | 1 - .../pro/checksums/graphql-pro-1.6.3.txt | 1 - .../pro/checksums/graphql-pro-1.6.4.txt | 1 - .../pro/checksums/graphql-pro-1.6.5.txt | 1 - .../pro/checksums/graphql-pro-1.7.0.txt | 1 - .../pro/checksums/graphql-pro-1.7.1.txt | 1 - .../pro/checksums/graphql-pro-1.7.10.txt | 1 - .../pro/checksums/graphql-pro-1.7.11.txt | 1 - .../pro/checksums/graphql-pro-1.7.12.txt | 1 - .../pro/checksums/graphql-pro-1.7.13.txt | 1 - .../pro/checksums/graphql-pro-1.7.2.txt | 1 - .../pro/checksums/graphql-pro-1.7.3.txt | 1 - .../pro/checksums/graphql-pro-1.7.4.txt | 1 - .../pro/checksums/graphql-pro-1.7.5.txt | 1 - .../pro/checksums/graphql-pro-1.7.6.txt | 1 - .../pro/checksums/graphql-pro-1.7.7.txt | 1 - .../pro/checksums/graphql-pro-1.7.8.txt | 1 - .../pro/checksums/graphql-pro-1.7.9.txt | 1 - .../pro/checksums/graphql-pro-1.8.0.txt | 1 - .../pro/checksums/graphql-pro-1.8.1.txt | 1 - .../pro/checksums/graphql-pro-1.8.2.txt | 1 - .../pro/checksums/graphql-pro-1.9.0.txt | 1 - .../pro/checksums/graphql-pro-1.9.1.txt | 1 - .../pro/checksums/graphql-pro-1.9.10.txt | 1 - .../pro/checksums/graphql-pro-1.9.11.txt | 1 - .../pro/checksums/graphql-pro-1.9.12.txt | 1 - .../pro/checksums/graphql-pro-1.9.13.txt | 1 - .../pro/checksums/graphql-pro-1.9.2.txt | 1 - .../pro/checksums/graphql-pro-1.9.3.txt | 1 - .../pro/checksums/graphql-pro-1.9.4.txt | 1 - .../pro/checksums/graphql-pro-1.9.5.txt | 1 - .../pro/checksums/graphql-pro-1.9.6.txt | 1 - .../pro/checksums/graphql-pro-1.9.7.txt | 1 - .../pro/checksums/graphql-pro-1.9.8.txt | 1 - .../pro/checksums/graphql-pro-1.9.9.txt | 1 - .../guides/pro/checksums/pro-1.26.4.txt | 1 - .../guides/pro/checksums/pro-1.27.3.txt | 1 - .../guides/pro/checksums/pro-1.27.7.txt | 1 - vendor/gems/graphql/guides/pro/dashboard.md | 85 - vendor/gems/graphql/guides/pro/encoders.md | 142 - vendor/gems/graphql/guides/pro/home.md | 10 - .../gems/graphql/guides/pro/installation.md | 90 - vendor/gems/graphql/guides/pro/privacy.md | 54 - .../graphql/guides/queries/ast_analysis.md | 140 - .../guides/queries/backtrace_annotations.md | 75 - .../guides/queries/complexity_and_depth.md | 321 - .../guides/queries/executing_queries.md | 217 - vendor/gems/graphql/guides/queries/logging.md | 22 - .../gems/graphql/guides/queries/lookahead.md | 125 - .../gems/graphql/guides/queries/multiplex.md | 139 - .../guides/queries/perfetto_example.png | Bin 114553 -> 0 bytes .../guides/queries/phases_of_execution.md | 18 - .../guides/queries/response_extensions.md | 37 - vendor/gems/graphql/guides/queries/timeout.md | 81 - vendor/gems/graphql/guides/queries/tracing.md | 60 - .../gems/graphql/guides/related_projects.md | 63 - vendor/gems/graphql/guides/relay/range_add.md | 11 - .../gems/graphql/guides/schema/definition.md | 131 - .../graphql/guides/schema/dynamic_types.md | 314 - .../gems/graphql/guides/schema/generators.md | 101 - .../graphql/guides/schema/introspection.md | 206 - .../graphql/guides/schema/lazy_execution.md | 96 - .../guides/schema/object_identification.md | 88 - .../gems/graphql/guides/schema/root_types.md | 60 - vendor/gems/graphql/guides/schema/sdl.md | 107 - vendor/gems/graphql/guides/search.html | 74 - .../subscriptions/ably_implementation.md | 326 - .../action_cable_implementation.md | 19 - .../graphql/guides/subscriptions/broadcast.md | 118 - .../guides/subscriptions/implementation.md | 28 - .../guides/subscriptions/multi_tenant.md | 128 - .../graphql/guides/subscriptions/overview.md | 53 - .../subscriptions/pusher_implementation.md | 317 - .../pusher_webhook_configuration.png | Bin 42914 -> 0 bytes .../subscriptions/redis_dashboard_1.png | Bin 103296 -> 0 bytes .../subscriptions/redis_dashboard_2.png | Bin 166239 -> 0 bytes .../subscriptions/subscription_classes.md | 371 - .../guides/subscriptions/subscription_type.md | 62 - .../graphql/guides/subscriptions/triggers.md | 74 - vendor/gems/graphql/guides/testing/helpers.md | 63 - .../guides/testing/integration_tests.md | 125 - .../gems/graphql/guides/testing/overview.md | 18 - .../gems/graphql/guides/testing/profiling.md | 102 - .../guides/testing/schema_structure.md | 68 - .../guides/type_definitions/directives.md | 151 - .../graphql/guides/type_definitions/enums.md | 96 - .../guides/type_definitions/extensions.md | 178 - .../type_definitions/field_extensions.md | 166 - .../guides/type_definitions/input_objects.md | 154 - .../guides/type_definitions/interfaces.md | 271 - .../graphql/guides/type_definitions/lists.md | 177 - .../guides/type_definitions/non_nulls.md | 52 - .../guides/type_definitions/objects.md | 111 - .../guides/type_definitions/scalars.md | 104 - .../graphql/guides/type_definitions/unions.md | 76 - .../graphql/javascript_client/CHANGELOG.md | 306 - .../gems/graphql/javascript_client/LICENSE.md | 10 - .../graphql/javascript_client/jest.config.js | 12 - .../graphql/javascript_client/package.json | 55 - .../gems/graphql/javascript_client/readme.md | 17 - .../src/__generated__/Card_card.graphql.js | 50 - .../src/__generated__/GetStuff.graphql.js | 194 - .../__tests__/__snapshots__/syncTest.ts.snap | 1026 -- .../__tests__/apolloExample/apollo.config.js | 11 - .../src/__tests__/apolloExample/fragment.ts | 5 - .../__tests__/apolloExample/gen/output.json | 108 - .../src/__tests__/apolloExample/mutation.ts | 7 - .../src/__tests__/apolloExample/query.ts | 10 - .../__tests__/apolloExample/schema.graphql | 11 - .../src/__tests__/cliTest.ts | 57 - .../src/__tests__/documents/doc1.graphql | 3 - ...ample-apollo-android-operation-output.json | 30 - .../example-relay-persisted-queries.json | 9 - .../src/__tests__/indexTest.ts | 26 - .../src/__tests__/project/frag_1.graphql | 3 - .../src/__tests__/project/frag_2.graphql | 3 - .../src/__tests__/project/frag_3.graphql | 3 - .../src/__tests__/project/frag_4.graphql | 5 - .../src/__tests__/project/op_1.graphql | 3 - .../src/__tests__/project/op_2.graphql | 5 - .../src/__tests__/project/op_3.graphql | 7 - .../__tests__/project/op_isolated_1.graphql | 12 - .../__tests__/project/op_isolated_2.graphql | 6 - .../src/__tests__/syncTest.ts | 446 - .../gems/graphql/javascript_client/src/cli.ts | 96 - .../graphql/javascript_client/src/index.ts | 17 - .../src/subscriptions/AblyLink.ts | 116 - .../src/subscriptions/ActionCableLink.ts | 64 - .../subscriptions/ActionCableSubscriber.ts | 81 - .../src/subscriptions/PusherLink.ts | 169 - .../src/subscriptions/PusherSubscriber.ts | 83 - .../src/subscriptions/SubscriptionExchange.ts | 143 - .../subscriptions/__tests__/AblyLinkTest.ts | 136 - .../__tests__/ActionCableLinkTest.ts | 174 - .../subscriptions/__tests__/PusherLinkTest.ts | 293 - .../__tests__/SubscriptionExchangeTest.ts | 167 - .../__tests__/addGraphQLSubscriptionsTest.ts | 26 - .../__tests__/createAblyFetcherTest.ts | 105 - .../__tests__/createAblyHandlerTest.ts | 499 - .../__tests__/createActionCableFetcherTest.ts | 66 - .../__tests__/createActionCableHandlerTest.ts | 69 - .../__tests__/createPusherFetcherTest.ts | 99 - .../createRelaySubscriptionHandlerTest.ts | 81 - .../subscriptions/__tests__/registryTest.ts | 37 - .../subscriptions/addGraphQLSubscriptions.ts | 80 - .../src/subscriptions/createAblyFetcher.ts | 92 - .../src/subscriptions/createAblyHandler.ts | 200 - .../subscriptions/createActionCableFetcher.ts | 98 - .../subscriptions/createActionCableHandler.ts | 82 - .../src/subscriptions/createPusherFetcher.ts | 79 - .../src/subscriptions/createPusherHandler.ts | 56 - .../createRelaySubscriptionHandler.ts | 79 - .../src/subscriptions/registry.ts | 38 - .../__snapshots__/dumpPayloadTest.ts.snap | 14 - .../__snapshots__/generateClientTest.ts.snap | 101 - .../generateJsonClientTest.ts.snap | 15 - .../prepareIsolatedFilesTest.ts.snap | 64 - .../preparePersistedQueryListTest.ts.snap | 28 - .../__snapshots__/prepareProjectTest.ts.snap | 70 - .../addTypenameToSelectionSetTest.ts | 11 - .../src/sync/__tests__/dumpPayloadTest.ts | 33 - .../generate-persisted-query-manifest.json | 18 - .../src/sync/__tests__/generateClientTest.ts | 9 - .../sync/__tests__/generateJsClientTest.ts | 76 - .../sync/__tests__/generateJsonClientTest.ts | 21 - .../__tests__/prepareIsolatedFilesTest.ts | 21 - .../preparePersistedQueryListTest.ts | 7 - .../src/sync/__tests__/prepareProjectTest.ts | 40 - .../sync/__tests__/removeClientFieldsTest.ts | 102 - .../src/sync/__tests__/sendPayloadTest.ts | 92 - .../src/sync/addTypenameToSelectionSet.ts | 55 - .../javascript_client/src/sync/dumpPayload.ts | 14 - .../src/sync/generateClient.ts | 142 - .../javascript_client/src/sync/index.ts | 262 - .../javascript_client/src/sync/logger.ts | 52 - .../graphql/javascript_client/src/sync/md5.ts | 10 - .../src/sync/outfileGenerators/js.ts | 99 - .../src/sync/outfileGenerators/json.ts | 5 - .../src/sync/prepareIsolatedFiles.ts | 47 - .../src/sync/preparePersistedQueryList.ts | 15 - .../src/sync/prepareProject.ts | 121 - .../src/sync/prepareRelay.ts | 61 - .../src/sync/removeClientFields.ts | 123 - .../javascript_client/src/sync/sendPayload.ts | 119 - .../graphql/javascript_client/tsconfig.json | 68 - .../graphql/lib/generators/graphql/core.rb | 69 - .../lib/generators/graphql/enum_generator.rb | 27 - .../lib/generators/graphql/field_extractor.rb | 31 - .../lib/generators/graphql/input_generator.rb | 50 - .../install/mutation_root_generator.rb | 34 - .../install/templates/base_mutation.erb | 12 - .../install/templates/mutation_type.erb | 14 - .../generators/graphql/install_generator.rb | 246 - .../generators/graphql/interface_generator.rb | 27 - .../generators/graphql/loader_generator.rb | 21 - .../graphql/mutation_create_generator.rb | 22 - .../graphql/mutation_delete_generator.rb | 22 - .../generators/graphql/mutation_generator.rb | 30 - .../graphql/mutation_update_generator.rb | 22 - .../generators/graphql/object_generator.rb | 50 - .../generators/graphql/orm_mutations_base.rb | 40 - .../graphql/lib/generators/graphql/relay.rb | 66 - .../lib/generators/graphql/relay_generator.rb | 21 - .../generators/graphql/scalar_generator.rb | 22 - .../graphql/templates/base_argument.erb | 8 - .../graphql/templates/base_connection.erb | 10 - .../graphql/templates/base_edge.erb | 10 - .../graphql/templates/base_enum.erb | 8 - .../graphql/templates/base_field.erb | 9 - .../graphql/templates/base_input_object.erb | 9 - .../graphql/templates/base_interface.erb | 11 - .../graphql/templates/base_object.erb | 9 - .../graphql/templates/base_resolver.erb | 8 - .../graphql/templates/base_scalar.erb | 8 - .../graphql/templates/base_union.erb | 8 - .../lib/generators/graphql/templates/enum.erb | 11 - .../graphql/templates/graphql_controller.erb | 54 - .../generators/graphql/templates/input.erb | 9 - .../graphql/templates/interface.erb | 10 - .../generators/graphql/templates/loader.erb | 21 - .../generators/graphql/templates/mutation.erb | 18 - .../graphql/templates/mutation_create.erb | 20 - .../graphql/templates/mutation_delete.erb | 20 - .../graphql/templates/mutation_update.erb | 21 - .../graphql/templates/node_type.erb | 11 - .../generators/graphql/templates/object.erb | 10 - .../graphql/templates/query_type.erb | 17 - .../generators/graphql/templates/scalar.erb | 17 - .../generators/graphql/templates/schema.erb | 35 - .../generators/graphql/templates/union.erb | 9 - .../lib/generators/graphql/type_generator.rb | 135 - .../lib/generators/graphql/union_generator.rb | 33 - vendor/gems/graphql/lib/graphql.rb | 134 - vendor/gems/graphql/lib/graphql/analysis.rb | 93 - .../graphql/lib/graphql/analysis/analyzer.rb | 90 - .../lib/graphql/analysis/field_usage.rb | 82 - .../graphql/analysis/max_query_complexity.rb | 20 - .../lib/graphql/analysis/max_query_depth.rb | 20 - .../lib/graphql/analysis/query_complexity.rb | 183 - .../lib/graphql/analysis/query_depth.rb | 58 - .../graphql/lib/graphql/analysis/visitor.rb | 285 - .../graphql/lib/graphql/analysis_error.rb | 5 - vendor/gems/graphql/lib/graphql/autoload.rb | 38 - vendor/gems/graphql/lib/graphql/backtrace.rb | 41 - .../graphql/lib/graphql/backtrace/table.rb | 181 - .../lib/graphql/backtrace/traced_error.rb | 54 - .../graphql/lib/graphql/coercion_error.rb | 5 - vendor/gems/graphql/lib/graphql/current.rb | 57 - vendor/gems/graphql/lib/graphql/dashboard.rb | 142 - .../dashboard/statics/bootstrap-5.3.3.min.css | 6 - .../dashboard/statics/bootstrap-5.3.3.min.js | 7 - .../graphql/dashboard/statics/dashboard.css | 3 - .../graphql/dashboard/statics/dashboard.js | 78 - .../graphql/dashboard/statics/header-icon.png | Bin 7522 -> 0 bytes .../lib/graphql/dashboard/statics/icon.png | Bin 4976 -> 0 bytes .../graphql/dashboard/landings/show.html.erb | 18 - .../graphql/dashboard/traces/index.html.erb | 63 - .../graphql/dashboard/application.html.erb | 60 - vendor/gems/graphql/lib/graphql/dataloader.rb | 334 - .../active_record_association_source.rb | 64 - .../dataloader/active_record_source.rb | 26 - .../graphql/dataloader/async_dataloader.rb | 101 - .../lib/graphql/dataloader/null_dataloader.rb | 24 - .../graphql/lib/graphql/dataloader/request.rb | 24 - .../lib/graphql/dataloader/request_all.rb | 19 - .../graphql/lib/graphql/dataloader/source.rb | 199 - .../lib/graphql/date_encoding_error.rb | 16 - vendor/gems/graphql/lib/graphql/dig.rb | 19 - .../lib/graphql/duration_encoding_error.rb | 16 - vendor/gems/graphql/lib/graphql/execution.rb | 18 - .../lib/graphql/execution/directive_checks.rb | 37 - .../graphql/lib/graphql/execution/errors.rb | 93 - .../lib/graphql/execution/interpreter.rb | 176 - .../execution/interpreter/argument_value.rb | 32 - .../execution/interpreter/arguments.rb | 88 - .../execution/interpreter/arguments_cache.rb | 100 - .../execution/interpreter/execution_errors.rb | 29 - .../interpreter/handles_raw_value.rb | 18 - .../graphql/execution/interpreter/resolve.rb | 100 - .../graphql/execution/interpreter/runtime.rb | 888 - .../interpreter/runtime/graphql_result.rb | 182 - .../graphql/lib/graphql/execution/lazy.rb | 69 - .../graphql/execution/lazy/lazy_method_map.rb | 98 - .../lib/graphql/execution/lookahead.rb | 385 - .../lib/graphql/execution/multiplex.rb | 42 - .../graphql/lib/graphql/execution_error.rb | 58 - .../lib/graphql/integer_decoding_error.rb | 17 - .../lib/graphql/integer_encoding_error.rb | 36 - .../gems/graphql/lib/graphql/introspection.rb | 118 - .../lib/graphql/introspection/base_object.rb | 13 - .../introspection/directive_location_enum.rb | 15 - .../graphql/introspection/directive_type.rb | 31 - .../graphql/introspection/dynamic_fields.rb | 12 - .../lib/graphql/introspection/entry_points.rb | 28 - .../graphql/introspection/enum_value_type.rb | 23 - .../lib/graphql/introspection/field_type.rb | 28 - .../graphql/introspection/input_value_type.rb | 67 - .../introspection/introspection_query.rb | 7 - .../lib/graphql/introspection/schema_type.rb | 46 - .../graphql/introspection/type_kind_enum.rb | 13 - .../lib/graphql/introspection/type_type.rb | 108 - .../graphql/lib/graphql/invalid_name_error.rb | 11 - .../graphql/lib/graphql/invalid_null_error.rb | 44 - vendor/gems/graphql/lib/graphql/language.rb | 97 - .../lib/graphql/language/block_string.rb | 115 - .../graphql/lib/graphql/language/cache.rb | 50 - .../graphql/lib/graphql/language/comment.rb | 18 - .../lib/graphql/language/definition_slice.rb | 41 - .../document_from_schema_definition.rb | 386 - .../lib/graphql/language/generation.rb | 24 - .../graphql/lib/graphql/language/lexer.rb | 367 - .../graphql/lib/graphql/language/nodes.rb | 800 - .../graphql/lib/graphql/language/parser.rb | 845 - .../graphql/lib/graphql/language/printer.rb | 573 - .../lib/graphql/language/sanitized_printer.rb | 220 - .../lib/graphql/language/static_visitor.rb | 171 - .../graphql/lib/graphql/language/visitor.rb | 293 - .../load_application_object_failed_error.rb | 26 - .../graphql/lib/graphql/name_validator.rb | 11 - vendor/gems/graphql/lib/graphql/pagination.rb | 6 - .../active_record_relation_connection.rb | 77 - .../graphql/pagination/array_connection.rb | 79 - .../lib/graphql/pagination/connection.rb | 280 - .../lib/graphql/pagination/connections.rb | 135 - .../pagination/mongoid_relation_connection.rb | 24 - .../graphql/pagination/relation_connection.rb | 228 - .../pagination/sequel_dataset_connection.rb | 28 - .../gems/graphql/lib/graphql/parse_error.rb | 24 - vendor/gems/graphql/lib/graphql/query.rb | 513 - .../gems/graphql/lib/graphql/query/context.rb | 292 - .../graphql/query/context/scoped_context.rb | 101 - .../graphql/lib/graphql/query/fingerprint.rb | 26 - .../graphql/query/input_validation_result.rb | 52 - .../graphql/lib/graphql/query/null_context.rb | 33 - .../gems/graphql/lib/graphql/query/result.rb | 63 - .../lib/graphql/query/validation_pipeline.rb | 115 - .../query/variable_validation_error.rb | 44 - .../graphql/lib/graphql/query/variables.rb | 92 - vendor/gems/graphql/lib/graphql/railtie.rb | 23 - vendor/gems/graphql/lib/graphql/rake_task.rb | 164 - .../graphql/lib/graphql/rake_task/validate.rb | 63 - vendor/gems/graphql/lib/graphql/relay.rb | 3 - .../graphql/lib/graphql/relay/range_add.rb | 52 - vendor/gems/graphql/lib/graphql/rubocop.rb | 6 - .../lib/graphql/rubocop/graphql/base_cop.rb | 36 - .../rubocop/graphql/default_null_true.rb | 43 - .../rubocop/graphql/default_required_true.rb | 43 - .../rubocop/graphql/field_type_in_block.rb | 144 - .../rubocop/graphql/root_types_in_block.rb | 38 - .../graphql/lib/graphql/runtime_type_error.rb | 5 - vendor/gems/graphql/lib/graphql/schema.rb | 1836 -- .../graphql/lib/graphql/schema/addition.rb | 282 - .../lib/graphql/schema/always_visible.rb | 14 - .../graphql/lib/graphql/schema/argument.rb | 435 - .../lib/graphql/schema/base_64_encoder.rb | 19 - .../graphql/schema/build_from_definition.rb | 510 - .../build_from_definition/resolve_map.rb | 78 - .../resolve_map/default_resolve.rb | 47 - .../lib/graphql/schema/built_in_types.rb | 12 - .../graphql/lib/graphql/schema/directive.rb | 224 - .../graphql/schema/directive/deprecated.rb | 18 - .../lib/graphql/schema/directive/feature.rb | 66 - .../lib/graphql/schema/directive/flagged.rb | 57 - .../lib/graphql/schema/directive/include.rb | 25 - .../lib/graphql/schema/directive/one_of.rb | 24 - .../lib/graphql/schema/directive/skip.rb | 25 - .../graphql/schema/directive/specified_by.rb | 14 - .../lib/graphql/schema/directive/transform.rb | 60 - .../gems/graphql/lib/graphql/schema/enum.rb | 266 - .../graphql/lib/graphql/schema/enum_value.rb | 84 - .../gems/graphql/lib/graphql/schema/field.rb | 957 - .../schema/field/connection_extension.rb | 66 - .../graphql/schema/field/scope_extension.rb | 29 - .../lib/graphql/schema/field_extension.rb | 153 - .../graphql/schema/find_inherited_value.rb | 31 - .../gems/graphql/lib/graphql/schema/finder.rb | 155 - .../schema/has_single_input_argument.rb | 160 - .../lib/graphql/schema/input_object.rb | 308 - .../graphql/lib/graphql/schema/interface.rb | 125 - .../graphql/schema/introspection_system.rb | 155 - .../lib/graphql/schema/late_bound_type.rb | 43 - .../gems/graphql/lib/graphql/schema/list.rb | 86 - .../gems/graphql/lib/graphql/schema/loader.rb | 227 - .../gems/graphql/lib/graphql/schema/member.rb | 40 - .../graphql/schema/member/base_dsl_methods.rb | 143 - .../lib/graphql/schema/member/build_type.rb | 186 - .../schema/member/graphql_type_names.rb | 21 - .../graphql/schema/member/has_arguments.rb | 428 - .../lib/graphql/schema/member/has_ast_node.rb | 32 - .../graphql/schema/member/has_dataloader.rb | 60 - .../schema/member/has_deprecation_reason.rb | 24 - .../graphql/schema/member/has_directives.rb | 118 - .../lib/graphql/schema/member/has_fields.rb | 243 - .../graphql/schema/member/has_interfaces.rb | 143 - .../lib/graphql/schema/member/has_path.rb | 25 - .../member/has_unresolved_type_error.rb | 19 - .../graphql/schema/member/has_validators.rb | 57 - .../graphql/schema/member/relay_shortcuts.rb | 92 - .../lib/graphql/schema/member/scoped.rb | 40 - .../schema/member/type_system_helpers.rb | 54 - .../graphql/schema/member/validates_input.rb | 33 - .../graphql/lib/graphql/schema/mutation.rb | 92 - .../graphql/lib/graphql/schema/non_null.rb | 67 - .../gems/graphql/lib/graphql/schema/object.rb | 145 - .../graphql/lib/graphql/schema/printer.rb | 104 - .../graphql/schema/relay_classic_mutation.rb | 56 - .../graphql/lib/graphql/schema/resolver.rb | 413 - .../schema/resolver/has_payload_type.rb | 106 - .../gems/graphql/lib/graphql/schema/scalar.rb | 68 - .../lib/graphql/schema/subscription.rb | 201 - .../graphql/lib/graphql/schema/timeout.rb | 119 - .../lib/graphql/schema/type_expression.rb | 43 - .../lib/graphql/schema/type_membership.rb | 51 - .../gems/graphql/lib/graphql/schema/union.rb | 90 - .../lib/graphql/schema/unique_within_type.rb | 34 - .../graphql/lib/graphql/schema/validator.rb | 173 - .../graphql/schema/validator/all_validator.rb | 62 - .../schema/validator/allow_blank_validator.rb | 29 - .../schema/validator/allow_null_validator.rb | 26 - .../schema/validator/exclusion_validator.rb | 33 - .../schema/validator/format_validator.rb | 48 - .../schema/validator/inclusion_validator.rb | 35 - .../schema/validator/length_validator.rb | 59 - .../validator/numericality_validator.rb | 82 - .../schema/validator/required_validator.rb | 123 - .../graphql/lib/graphql/schema/visibility.rb | 299 - .../graphql/schema/visibility/migration.rb | 188 - .../lib/graphql/schema/visibility/profile.rb | 359 - .../lib/graphql/schema/visibility/visit.rb | 190 - .../gems/graphql/lib/graphql/schema/warden.rb | 634 - .../graphql/lib/graphql/schema/wrapper.rb | 24 - .../graphql/lib/graphql/static_validation.rb | 16 - .../graphql/static_validation/all_rules.rb | 42 - .../graphql/static_validation/base_visitor.rb | 201 - .../definition_dependencies.rb | 204 - .../lib/graphql/static_validation/error.rb | 46 - .../static_validation/interpreter_visitor.rb | 14 - .../static_validation/literal_validator.rb | 156 - .../rules/argument_literals_are_compatible.rb | 66 - .../argument_literals_are_compatible_error.rb | 48 - .../rules/argument_names_are_unique.rb | 31 - .../rules/argument_names_are_unique_error.rb | 30 - .../rules/arguments_are_defined.rb | 72 - .../rules/arguments_are_defined_error.rb | 37 - .../rules/directives_are_defined.rb | 29 - .../rules/directives_are_defined_error.rb | 29 - .../directives_are_in_valid_locations.rb | 67 - ...directives_are_in_valid_locations_error.rb | 31 - .../rules/fields_are_defined_on_type.rb | 40 - .../rules/fields_are_defined_on_type_error.rb | 32 - .../fields_have_appropriate_selections.rb | 81 - ...ields_have_appropriate_selections_error.rb | 31 - .../rules/fields_will_merge.rb | 426 - .../rules/fields_will_merge_error.rb | 53 - .../rules/fragment_names_are_unique.rb | 30 - .../rules/fragment_names_are_unique_error.rb | 29 - .../rules/fragment_spreads_are_possible.rb | 73 - .../fragment_spreads_are_possible_error.rb | 35 - .../rules/fragment_types_exist.rb | 49 - .../rules/fragment_types_exist_error.rb | 29 - .../rules/fragments_are_finite.rb | 21 - .../rules/fragments_are_finite_error.rb | 29 - .../rules/fragments_are_named.rb | 16 - .../rules/fragments_are_named_error.rb | 26 - .../rules/fragments_are_on_composite_types.rb | 37 - .../fragments_are_on_composite_types_error.rb | 30 - .../rules/fragments_are_used.rb | 32 - .../rules/fragments_are_used_error.rb | 29 - .../rules/input_object_names_are_unique.rb | 30 - .../input_object_names_are_unique_error.rb | 30 - .../rules/mutation_root_exists.rb | 17 - .../rules/mutation_root_exists_error.rb | 26 - .../rules/no_definitions_are_present.rb | 41 - .../rules/no_definitions_are_present_error.rb | 25 - .../rules/one_of_input_objects_are_valid.rb | 66 - .../one_of_input_objects_are_valid_error.rb | 29 - .../rules/operation_names_are_valid.rb | 36 - .../rules/operation_names_are_valid_error.rb | 28 - .../rules/query_root_exists.rb | 17 - .../rules/query_root_exists_error.rb | 26 - .../rules/required_arguments_are_present.rb | 39 - .../required_arguments_are_present_error.rb | 35 - ...red_input_object_attributes_are_present.rb | 59 - ...put_object_attributes_are_present_error.rb | 35 - .../rules/subscription_root_exists.rb | 17 - .../rules/subscription_root_exists_error.rb | 26 - .../rules/unique_directives_per_location.rb | 56 - .../unique_directives_per_location_error.rb | 29 - ...able_default_values_are_correctly_typed.rb | 37 - ...efault_values_are_correctly_typed_error.rb | 39 - .../rules/variable_names_are_unique.rb | 24 - .../rules/variable_names_are_unique_error.rb | 29 - .../rules/variable_usages_are_allowed.rb | 159 - .../variable_usages_are_allowed_error.rb | 38 - .../rules/variables_are_input_types.rb | 48 - .../rules/variables_are_input_types_error.rb | 32 - .../rules/variables_are_used_and_defined.rb | 155 - .../variables_are_used_and_defined_error.rb | 37 - .../static_validation/validation_context.rb | 69 - .../validation_timeout_error.rb | 25 - .../graphql/static_validation/validator.rb | 82 - .../lib/graphql/string_encoding_error.rb | 20 - .../gems/graphql/lib/graphql/subscriptions.rb | 315 - .../action_cable_subscriptions.rb | 255 - .../subscriptions/broadcast_analyzer.rb | 87 - .../default_subscription_resolve_extension.rb | 60 - .../lib/graphql/subscriptions/event.rb | 156 - .../lib/graphql/subscriptions/serialize.rb | 160 - vendor/gems/graphql/lib/graphql/testing.rb | 2 - .../graphql/lib/graphql/testing/helpers.rb | 155 - vendor/gems/graphql/lib/graphql/tracing.rb | 74 - .../active_support_notifications_trace.rb | 20 - .../active_support_notifications_tracing.rb | 21 - .../lib/graphql/tracing/appoptics_trace.rb | 259 - .../lib/graphql/tracing/appoptics_tracing.rb | 175 - .../lib/graphql/tracing/appsignal_trace.rb | 89 - .../lib/graphql/tracing/appsignal_tracing.rb | 53 - .../graphql/tracing/call_legacy_tracers.rb | 66 - .../lib/graphql/tracing/data_dog_trace.rb | 194 - .../lib/graphql/tracing/data_dog_tracing.rb | 88 - .../lib/graphql/tracing/detailed_trace.rb | 93 - .../tracing/detailed_trace/memory_backend.rb | 60 - .../tracing/detailed_trace/redis_backend.rb | 72 - .../lib/graphql/tracing/legacy_hooks_trace.rb | 75 - .../lib/graphql/tracing/legacy_trace.rb | 12 - .../lib/graphql/tracing/new_relic_trace.rb | 183 - .../lib/graphql/tracing/new_relic_tracing.rb | 53 - .../graphql/tracing/notifications_trace.rb | 49 - .../graphql/tracing/notifications_tracing.rb | 61 - .../graphql/lib/graphql/tracing/null_trace.rb | 9 - .../lib/graphql/tracing/perfetto_trace.rb | 737 - .../tracing/perfetto_trace/trace.proto | 141 - .../tracing/perfetto_trace/trace_pb.rb | 33 - .../lib/graphql/tracing/platform_trace.rb | 123 - .../lib/graphql/tracing/platform_tracing.rb | 136 - .../lib/graphql/tracing/prometheus_trace.rb | 120 - .../prometheus_trace/graphql_collector.rb | 36 - .../lib/graphql/tracing/prometheus_tracing.rb | 69 - .../lib/graphql/tracing/scout_trace.rb | 83 - .../lib/graphql/tracing/scout_tracing.rb | 56 - .../lib/graphql/tracing/sentry_trace.rb | 123 - .../lib/graphql/tracing/statsd_trace.rb | 71 - .../lib/graphql/tracing/statsd_tracing.rb | 44 - .../gems/graphql/lib/graphql/tracing/trace.rb | 203 - vendor/gems/graphql/lib/graphql/type_kinds.rb | 80 - vendor/gems/graphql/lib/graphql/types.rb | 19 - .../gems/graphql/lib/graphql/types/big_int.rb | 23 - .../gems/graphql/lib/graphql/types/boolean.rb | 18 - .../gems/graphql/lib/graphql/types/float.rb | 19 - vendor/gems/graphql/lib/graphql/types/id.rb | 24 - vendor/gems/graphql/lib/graphql/types/int.rb | 36 - .../lib/graphql/types/iso_8601_date.rb | 45 - .../lib/graphql/types/iso_8601_date_time.rb | 76 - .../lib/graphql/types/iso_8601_duration.rb | 77 - vendor/gems/graphql/lib/graphql/types/json.rb | 25 - .../gems/graphql/lib/graphql/types/relay.rb | 38 - .../graphql/types/relay/base_connection.rb | 49 - .../lib/graphql/types/relay/base_edge.rb | 29 - .../types/relay/connection_behaviors.rb | 216 - .../lib/graphql/types/relay/edge_behaviors.rb | 92 - .../lib/graphql/types/relay/has_node_field.rb | 41 - .../graphql/types/relay/has_nodes_field.rb | 41 - .../graphql/lib/graphql/types/relay/node.rb | 15 - .../lib/graphql/types/relay/node_behaviors.rb | 25 - .../lib/graphql/types/relay/page_info.rb | 11 - .../types/relay/page_info_behaviors.rb | 34 - .../gems/graphql/lib/graphql/types/string.rb | 29 - .../graphql/unauthorized_enum_value_error.rb | 13 - .../graphql/lib/graphql/unauthorized_error.rb | 29 - .../lib/graphql/unauthorized_field_error.rb | 23 - .../lib/graphql/unresolved_type_error.rb | 35 - vendor/gems/graphql/lib/graphql/version.rb | 4 - vendor/gems/graphql/readme.md | 59 - vendor/gems/graphql/spec/dummy/.gitignore | 17 - vendor/gems/graphql/spec/dummy/Rakefile | 7 - .../spec/dummy/app/assets/config/manifest.js | 1 - .../app/assets/javascripts/application.js | 89 - .../app/channels/application_cable/channel.rb | 5 - .../channels/application_cable/connection.rb | 5 - .../dummy/app/channels/graphql_channel.rb | 141 - .../app/controllers/application_controller.rb | 4 - .../dummy/app/controllers/pages_controller.rb | 5 - .../spec/dummy/app/graphql/dummy_schema.rb | 14 - .../dummy/app/graphql/not_installed_schema.rb | 9 - .../dummy/app/helpers/application_helper.rb | 3 - .../spec/dummy/app/jobs/application_job.rb | 3 - .../app/views/layouts/application.html.erb | 12 - .../spec/dummy/app/views/pages/show.html | 143 - vendor/gems/graphql/spec/dummy/bin/bundle | 4 - vendor/gems/graphql/spec/dummy/bin/rails | 5 - vendor/gems/graphql/spec/dummy/bin/rake | 5 - vendor/gems/graphql/spec/dummy/bin/setup | 31 - vendor/gems/graphql/spec/dummy/bin/update | 27 - vendor/gems/graphql/spec/dummy/bin/yarn | 12 - vendor/gems/graphql/spec/dummy/config.ru | 6 - .../graphql/spec/dummy/config/application.rb | 23 - vendor/gems/graphql/spec/dummy/config/boot.rb | 5 - .../gems/graphql/spec/dummy/config/cable.yml | 10 - .../graphql/spec/dummy/config/database.yml | 32 - .../graphql/spec/dummy/config/environment.rb | 6 - .../dummy/config/environments/development.rb | 40 - .../dummy/config/environments/production.rb | 76 - .../spec/dummy/config/environments/test.rb | 37 - .../application_controller_renderer.rb | 9 - .../initializers/backtrace_silencers.rb | 8 - .../config/initializers/cookies_serializer.rb | 6 - .../initializers/filter_parameter_logging.rb | 5 - .../config/initializers/graphql_dashboard.rb | 7 - .../dummy/config/initializers/inflections.rb | 17 - .../dummy/config/initializers/mime_types.rb | 5 - .../config/initializers/wrap_parameters.rb | 10 - .../graphql/spec/dummy/config/locales/en.yml | 33 - vendor/gems/graphql/spec/dummy/config/puma.rb | 57 - .../gems/graphql/spec/dummy/config/routes.rb | 5 - .../graphql/spec/dummy/config/secrets.yml | 32 - vendor/gems/graphql/spec/dummy/package.json | 5 - .../gems/graphql/spec/dummy/public/404.html | 67 - .../gems/graphql/spec/dummy/public/422.html | 67 - .../gems/graphql/spec/dummy/public/500.html | 66 - .../public/apple-touch-icon-precomposed.png | 0 .../spec/dummy/public/apple-touch-icon.png | 0 .../graphql/spec/dummy/public/favicon.ico | 0 .../gems/graphql/spec/dummy/public/robots.txt | 1 - .../test/application_system_test_case.rb | 6 - .../test/channels/graphql_channel_test.rb | 159 - .../dashboard/application_controller_test.rb | 8 - .../dashboard/landings_controller_test.rb | 17 - .../dashboard/statics_controller_test.rb | 22 - .../dashboard/traces_controller_test.rb | 68 - .../system/action_cable_subscription_test.rb | 175 - .../graphql/spec/dummy/test/test_helper.rb | 3 - .../graphql/spec/fixtures/cop/field_type.rb | 18 - .../spec/fixtures/cop/field_type_array.rb | 6 - .../cop/field_type_array_corrected.rb | 7 - .../spec/fixtures/cop/field_type_corrected.rb | 22 - .../spec/fixtures/cop/field_type_interface.rb | 5 - .../cop/field_type_interface_corrected.rb | 7 - .../graphql/spec/fixtures/cop/null_true.rb | 13 - .../spec/fixtures/cop/null_true_corrected.rb | 12 - .../spec/fixtures/cop/required_true.rb | 20 - .../fixtures/cop/required_true_corrected.rb | 19 - .../graphql/spec/fixtures/cop/root_types.rb | 5 - .../spec/fixtures/cop/root_types_corrected.rb | 5 - .../spec/fixtures/cop/small_field_type.rb | 3 - .../spec/fixtures/eager_module/eager_class.rb | 6 - .../eager_module/nested_eager_module.rb | 7 - .../nested_eager_module/nested_eager_class.rb | 7 - .../eager_module/other_eager_class.rb | 6 - .../spec/fixtures/lazy_module/lazy_class.rb | 6 - .../fixtures/unicode_escapes/query1.graphql | 6 - .../fixtures/unicode_escapes/query2.graphql | 6 - .../spec/graphql/analysis/field_usage_spec.rb | 295 - .../analysis/max_query_complexity_spec.rb | 223 - .../graphql/analysis/max_query_depth_spec.rb | 179 - .../graphql/analysis/query_complexity_spec.rb | 732 - .../spec/graphql/analysis/query_depth_spec.rb | 108 - .../graphql/spec/graphql/analysis_spec.rb | 612 - .../spec/graphql/authorization_spec.rb | 1009 -- .../graphql/spec/graphql/autoload_spec.rb | 63 - .../graphql/spec/graphql/backtrace_spec.rb | 326 - .../graphql/cop/default_null_true_spec.rb | 28 - .../graphql/cop/default_required_true_spec.rb | 33 - .../graphql/cop/field_type_in_block_spec.rb | 57 - .../graphql/cop/root_types_in_block_spec.rb | 28 - .../gems/graphql/spec/graphql/current_spec.rb | 62 - .../active_record_association_source_spec.rb | 109 - .../dataloader/active_record_source_spec.rb | 109 - .../dataloader/async_dataloader_spec.rb | 358 - .../dataloader/nonblocking_dataloader_spec.rb | 261 - .../graphql/dataloader/snapshots/example.json | 2709 --- .../spec/graphql/dataloader/source_spec.rb | 104 - .../graphql/spec/graphql/dataloader_spec.rb | 1710 -- .../graphql/spec/graphql/directive_spec.rb | 278 - .../spec/graphql/execution/errors_spec.rb | 336 - .../graphql/execution/instrumentation_spec.rb | 208 - .../execution/interpreter/arguments_spec.rb | 54 - .../graphql/execution/interpreter_spec.rb | 952 - .../execution/lazy/lazy_method_map_spec.rb | 57 - .../spec/graphql/execution/lazy_spec.rb | 244 - .../spec/graphql/execution/lookahead_spec.rb | 866 - .../spec/graphql/execution/multiplex_spec.rb | 286 - .../spec/graphql/execution_error_spec.rb | 497 - .../introspection/directive_type_spec.rb | 164 - .../introspection/entry_points_spec.rb | 60 - .../introspection/input_value_type_spec.rb | 144 - .../introspection/introspection_query_spec.rb | 62 - .../graphql/introspection/schema_type_spec.rb | 203 - .../graphql/introspection/type_type_spec.rb | 235 - .../spec/graphql/invalid_null_error_spec.rb | 8 - .../graphql/language/block_string_spec.rb | 87 - .../spec/graphql/language/clexer_spec.rb | 66 - .../graphql/language/definition_slice_spec.rb | 226 - .../document_from_schema_definition_spec.rb | 921 - .../spec/graphql/language/equality_spec.rb | 84 - .../spec/graphql/language/generation_spec.rb | 38 - .../spec/graphql/language/lexer_examples.rb | 258 - .../spec/graphql/language/lexer_spec.rb | 14 - .../spec/graphql/language/nodes_spec.rb | 227 - .../spec/graphql/language/parser_spec.rb | 655 - .../spec/graphql/language/printer_spec.rb | 458 - .../language/sanitized_printer_spec.rb | 380 - .../spec/graphql/language/visitor_spec.rb | 412 - .../gems/graphql/spec/graphql/logger_spec.rb | 124 - .../spec/graphql/non_null_type_spec.rb | 56 - .../active_record_relation_connection_spec.rb | 257 - .../pagination/array_connection_spec.rb | 22 - .../graphql/pagination/connection_spec.rb | 21 - .../graphql/pagination/connections_spec.rb | 180 - .../mongoid_relation_connection_spec.rb | 33 - .../sequel_dataset_connection_spec.rb | 31 - .../query/context/scoped_context_spec.rb | 94 - .../spec/graphql/query/context_spec.rb | 482 - .../spec/graphql/query/executor_spec.rb | 341 - .../spec/graphql/query/fingerprint_spec.rb | 78 - .../spec/graphql/query/null_context_spec.rb | 37 - .../graphql/spec/graphql/query/result_spec.rb | 35 - .../query/variable_validation_error_spec.rb | 51 - .../spec/graphql/query/variables_spec.rb | 139 - .../gems/graphql/spec/graphql/query_spec.rb | 1158 -- .../graphql/spec/graphql/rake_task_spec.rb | 98 - .../spec/graphql/schema/addition_spec.rb | 17 - .../graphql/schema/always_visible_spec.rb | 33 - .../spec/graphql/schema/argument_spec.rb | 801 - .../graphql/schema/base_64_encoder_spec.rb | 20 - .../schema/build_from_definition_spec.rb | 1760 -- .../graphql/schema/directive/feature_spec.rb | 120 - .../graphql/schema/directive/flagged_spec.rb | 102 - .../graphql/schema/directive/one_of_spec.rb | 160 - .../directive/query_level_directive_spec.rb | 83 - .../schema/directive/transform_spec.rb | 36 - .../spec/graphql/schema/directive_spec.rb | 403 - .../graphql/schema/dynamic_members_spec.rb | 1249 -- .../graphql/spec/graphql/schema/enum_spec.rb | 394 - .../spec/graphql/schema/enum_value_spec.rb | 38 - .../schema/field/connection_extension_spec.rb | 49 - .../graphql/schema/field_extension_spec.rb | 414 - .../graphql/spec/graphql/schema/field_spec.rb | 896 - .../spec/graphql/schema/finder_spec.rb | 135 - .../schema/has_single_input_argument_spec.rb | 230 - .../spec/graphql/schema/input_object_spec.rb | 1536 -- .../graphql/schema/instrumentation_spec.rb | 30 - .../spec/graphql/schema/interface_spec.rb | 702 - .../schema/introspection_system_spec.rb | 322 - .../graphql/spec/graphql/schema/list_spec.rb | 190 - .../spec/graphql/schema/loader_spec.rb | 560 - .../graphql/schema/member/build_type_spec.rb | 80 - .../schema/member/has_arguments_spec.rb | 29 - .../schema/member/has_dataloader_spec.rb | 48 - .../member/has_unresolved_type_error_spec.rb | 56 - .../schema/member/relay_shortcuts_spec.rb | 47 - .../spec/graphql/schema/member/scoped_spec.rb | 384 - .../schema/member/type_system_helpers_spec.rb | 63 - .../spec/graphql/schema/mutation_spec.rb | 356 - .../spec/graphql/schema/non_null_spec.rb | 89 - .../spec/graphql/schema/object_spec.rb | 563 - .../spec/graphql/schema/printer_spec.rb | 1009 -- .../schema/relay_classic_mutation_spec.rb | 635 - .../spec/graphql/schema/resolver_spec.rb | 1221 -- .../spec/graphql/schema/scalar_spec.rb | 246 - .../spec/graphql/schema/subscription_spec.rb | 770 - .../spec/graphql/schema/timeout_spec.rb | 257 - .../graphql/schema/type_expression_spec.rb | 38 - .../graphql/spec/graphql/schema/union_spec.rb | 463 - .../graphql/schema/unique_within_type_spec.rb | 51 - .../schema/validator/all_validator_spec.rb | 67 - .../validator/allow_blank_validator_spec.rb | 39 - .../validator/allow_null_validator_spec.rb | 39 - .../validator/exclusion_validator_spec.rb | 20 - .../schema/validator/format_validator_spec.rb | 39 - .../validator/inclusion_validator_spec.rb | 28 - .../schema/validator/length_validator_spec.rb | 146 - .../validator/numericality_validator_spec.rb | 69 - .../validator/required_validator_spec.rb | 88 - .../schema/validator/validator_helpers.rb | 118 - .../spec/graphql/schema/validator_spec.rb | 210 - .../graphql/schema/visibility/profile_spec.rb | 115 - .../spec/graphql/schema/visibility_spec.rb | 274 - .../spec/graphql/schema/warden_spec.rb | 1117 -- .../gems/graphql/spec/graphql/schema_spec.rb | 607 - .../argument_literals_are_compatible_spec.rb | 410 - .../rules/argument_names_are_unique_spec.rb | 78 - .../rules/arguments_are_defined_spec.rb | 81 - .../rules/directives_are_defined_spec.rb | 49 - .../directives_are_in_valid_locations_spec.rb | 58 - .../rules/fields_are_defined_on_type_spec.rb | 173 - ...fields_have_appropriate_selections_spec.rb | 90 - .../rules/fields_will_merge_spec.rb | 1001 - .../rules/fragment_names_are_unique_spec.rb | 78 - .../fragment_spreads_are_possible_spec.rb | 52 - .../rules/fragment_types_exist_spec.rb | 53 - .../rules/fragments_are_finite_spec.rb | 149 - .../rules/fragments_are_named_spec.rb | 24 - .../fragments_are_on_composite_types_spec.rb | 56 - .../rules/fragments_are_used_spec.rb | 98 - .../input_object_names_are_unique_spec.rb | 64 - .../rules/mutation_root_exists_spec.rb | 38 - .../rules/no_definitions_are_present_spec.rb | 62 - .../one_of_input_objects_are_valid_spec.rb | 141 - .../rules/operation_names_are_valid_spec.rb | 130 - .../required_arguments_are_present_spec.rb | 99 - ...nput_object_attributes_are_present_spec.rb | 115 - .../rules/subscription_root_exists_spec.rb | 33 - .../unique_directives_per_location_spec.rb | 233 - ...default_values_are_correctly_typed_spec.rb | 187 - .../rules/variable_names_are_unique_spec.rb | 52 - .../rules/variable_usages_are_allowed_spec.rb | 379 - .../rules/variables_are_input_types_spec.rb | 87 - .../variables_are_used_and_defined_spec.rb | 143 - .../static_validation/validator_spec.rb | 211 - .../action_cable_subscriptions_spec.rb | 370 - .../subscriptions/broadcast_analyzer_spec.rb | 201 - .../spec/graphql/subscriptions/event_spec.rb | 81 - .../graphql/subscriptions/serialize_spec.rb | 135 - .../spec/graphql/subscriptions_spec.rb | 1206 -- .../spec/graphql/testing/helpers_spec.rb | 232 - .../graphql/tracing/appoptics_trace_spec.rb | 147 - .../graphql/tracing/appoptics_tracing_spec.rb | 147 - .../graphql/tracing/appsignal_trace_spec.rb | 192 - .../graphql/tracing/data_dog_trace_spec.rb | 99 - .../graphql/tracing/data_dog_tracing_spec.rb | 94 - .../detailed_trace/backend_assertions.rb | 96 - .../detailed_trace/memory_backend_spec.rb | 10 - .../detailed_trace/redis_backend_spec.rb | 12 - .../graphql/tracing/detailed_trace_spec.rb | 71 - .../spec/graphql/tracing/legacy_trace_spec.rb | 81 - .../graphql/tracing/new_relic_trace_spec.rb | 244 - .../graphql/tracing/new_relic_tracing_spec.rb | 99 - .../tracing/notifications_trace_spec.rb | 75 - .../tracing/notifications_tracing_spec.rb | 48 - .../graphql/tracing/perfetto_trace_spec.rb | 157 - .../graphql/tracing/platform_trace_spec.rb | 260 - .../graphql/tracing/platform_tracing_spec.rb | 239 - .../graphql/tracing/prometheus_trace_spec.rb | 38 - .../tracing/prometheus_tracing_spec.rb | 39 - .../spec/graphql/tracing/scout_trace_spec.rb | 52 - .../graphql/tracing/scout_tracing_spec.rb | 52 - .../spec/graphql/tracing/sentry_trace_spec.rb | 160 - .../tracing/snapshots/example-rails-7-0.json | 14709 --------------- .../tracing/snapshots/example-rails-7-1.json | 14705 --------------- .../tracing/snapshots/example-rails-8-1.json | 15047 ---------------- .../spec/graphql/tracing/statsd_trace_spec.rb | 63 - .../graphql/tracing/statsd_tracing_spec.rb | 63 - .../spec/graphql/tracing/trace_modes_spec.rb | 338 - .../spec/graphql/tracing/trace_spec.rb | 11 - .../gems/graphql/spec/graphql/tracing_spec.rb | 52 - .../spec/graphql/type_kinds/type_kind_spec.rb | 15 - .../spec/graphql/types/big_int_spec.rb | 33 - .../spec/graphql/types/boolean_spec.rb | 21 - .../graphql/spec/graphql/types/float_spec.rb | 19 - .../graphql/spec/graphql/types/id_spec.rb | 43 - .../graphql/spec/graphql/types/int_spec.rb | 47 - .../spec/graphql/types/iso_8601_date_spec.rb | 316 - .../graphql/types/iso_8601_date_time_spec.rb | 282 - .../types/relay/base_connection_spec.rb | 120 - .../graphql/types/relay/base_edge_spec.rb | 60 - .../types/relay/has_node_field_spec.rb | 20 - .../types/relay/node_behaviors_spec.rb | 31 - .../graphql/spec/graphql/types/string_spec.rb | 148 - vendor/gems/graphql/spec/graphql_spec.rb | 11 - .../relay/mongo_relation_connection_spec.rb | 491 - .../integration/mongoid/star_trek/data.rb | 105 - .../integration/mongoid/star_trek/schema.rb | 373 - .../graphql/spec/integration/rails/data.rb | 84 - .../rails/generators/base_generator_test.rb | 5 - .../generators/graphql/enum_generator_spec.rb | 62 - .../graphql/input_generator_spec.rb | 123 - .../graphql/install_generator_spec.rb | 439 - .../graphql/interface_generator_spec.rb | 65 - .../graphql/loader_generator_spec.rb | 63 - .../graphql/mutation_create_generator_spec.rb | 86 - .../graphql/mutation_delete_generator_spec.rb | 89 - .../graphql/mutation_generator_spec.rb | 98 - .../graphql/mutation_update_generator_spec.rb | 88 - .../graphql/object_generator_spec.rb | 130 - .../graphql/relay_generator_spec.rb | 60 - .../graphql/scalar_generator_spec.rb | 53 - .../graphql/union_generator_spec.rb | 75 - .../dataloader/async_dataloader_spec.rb | 117 - .../rails/graphql/dataloader_spec.rb | 118 - .../rails/graphql/input_object_spec.rb | 19 - .../rails/graphql/query/variables_spec.rb | 334 - .../graphql/relay/array_connection_spec.rb | 290 - .../graphql/relay/connection_resolve_spec.rb | 54 - .../graphql/relay/connection_type_spec.rb | 106 - .../rails/graphql/relay/page_info_spec.rb | 120 - .../rails/graphql/relay/range_add_spec.rb | 126 - .../graphql/relay/relation_connection_spec.rb | 648 - .../integration/rails/graphql/schema_spec.rb | 387 - ...tive_support_notifications_tracing_spec.rb | 63 - .../graphql/types/iso_8601_duration_spec.rb | 113 - .../spec/integration/rails/query_logs_spec.rb | 119 - .../spec/integration/rails/spec_helper.rb | 37 - vendor/gems/graphql/spec/spec_helper.rb | 212 - .../spec/support/active_record_setup.rb | 213 - vendor/gems/graphql/spec/support/appoptics.rb | 49 - .../spec/support/connection_assertions.rb | 482 - vendor/gems/graphql/spec/support/datadog.rb | 41 - .../gems/graphql/spec/support/dummy/data.rb | 45 - .../gems/graphql/spec/support/dummy/schema.rb | 579 - .../graphql/spec/support/dummy_scheduler.rb | 228 - vendor/gems/graphql/spec/support/global_id.rb | 32 - vendor/gems/graphql/spec/support/jazz.rb | 952 - .../gems/graphql/spec/support/lazy_helpers.rb | 199 - .../spec/support/magic_cards/schema.graphql | 33 - .../spec/support/minimum_input_object.rb | 21 - .../graphql/spec/support/mongoid_setup.rb | 19 - vendor/gems/graphql/spec/support/new_relic.rb | 50 - .../support/parser/filename_example.graphql | 5 - .../parser/filename_example_error_1.graphql | 4 - .../parser/filename_example_error_2.graphql | 5 - .../filename_example_invalid_utf8.graphql | 1 - .../graphql/spec/support/perfetto_snapshot.rb | 134 - .../spec/support/rubocop_test_helpers.rb | 44 - vendor/gems/graphql/spec/support/scout_apm.rb | 31 - vendor/gems/graphql/spec/support/sentry.rb | 57 - .../graphql/spec/support/star_wars/schema.rb | 434 - .../spec/support/static_validation_helpers.rb | 34 - yarn.lock | 38 +- 1323 files changed, 951 insertions(+), 187217 deletions(-) delete mode 100644 app/assets/javascripts/organizations/shared/graphql/fragments/base_group.fragment.graphql create mode 100644 app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer.vue create mode 100644 app/assets/javascripts/vue_shared/components/groups_list/group_list_item_inactive_badge.vue create mode 100644 spec/frontend/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer_spec.js create mode 100644 spec/frontend/vue_shared/components/groups_list/group_list_item_inactive_badge_spec.js create mode 100644 spec/frontend/work_items/graphql/mock_query.query.graphql delete mode 100644 vendor/gems/graphql/.gitlab-ci.yml delete mode 100644 vendor/gems/graphql/.yardopts delete mode 100644 vendor/gems/graphql/CHANGELOG-enterprise.md delete mode 100644 vendor/gems/graphql/CHANGELOG-pro.md delete mode 100644 vendor/gems/graphql/CHANGELOG-relay.md delete mode 100644 vendor/gems/graphql/CHANGELOG.md delete mode 100644 vendor/gems/graphql/CNAME delete mode 100644 vendor/gems/graphql/Gemfile delete mode 100644 vendor/gems/graphql/Gemfile.lock delete mode 100644 vendor/gems/graphql/MIT-LICENSE delete mode 100644 vendor/gems/graphql/Rakefile delete mode 100644 vendor/gems/graphql/benchmark/abstract_fragments.graphql delete mode 100644 vendor/gems/graphql/benchmark/abstract_fragments_2.graphql delete mode 100644 vendor/gems/graphql/benchmark/batch_loading.rb delete mode 100644 vendor/gems/graphql/benchmark/big_query.graphql delete mode 100644 vendor/gems/graphql/benchmark/big_schema.graphql delete mode 100644 vendor/gems/graphql/benchmark/run.rb delete mode 100644 vendor/gems/graphql/benchmark/schema.graphql delete mode 100644 vendor/gems/graphql/cop/development/context_is_passed_cop.rb delete mode 100644 vendor/gems/graphql/cop/development/no_eval_cop.rb delete mode 100644 vendor/gems/graphql/cop/development/no_focus_cop.rb delete mode 100644 vendor/gems/graphql/cop/development/none_without_block_cop.rb delete mode 100644 vendor/gems/graphql/cop/development/trace_methods_cop.rb delete mode 100644 vendor/gems/graphql/gemfiles/mongoid_8.gemfile delete mode 100644 vendor/gems/graphql/gemfiles/mongoid_9.gemfile delete mode 100644 vendor/gems/graphql/gemfiles/rails_7.0.gemfile delete mode 100644 vendor/gems/graphql/gemfiles/rails_7.1.gemfile delete mode 100644 vendor/gems/graphql/gemfiles/rails_7.1_postgresql.gemfile delete mode 100644 vendor/gems/graphql/gemfiles/rails_master.gemfile delete mode 100644 vendor/gems/graphql/graphql-c_parser/CHANGELOG.md delete mode 100644 vendor/gems/graphql/graphql-c_parser/Rakefile delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/extconf.rb delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.c delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.h delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.c delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.h delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.c delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.h delete mode 100644 vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.y delete mode 100644 vendor/gems/graphql/graphql-c_parser/graphql-c_parser.gemspec delete mode 100644 vendor/gems/graphql/graphql-c_parser/lib/graphql-c_parser.rb delete mode 100644 vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser.rb delete mode 100644 vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser/version.rb delete mode 100644 vendor/gems/graphql/graphql-ruby.png delete mode 100644 vendor/gems/graphql/graphql-ruby.svg delete mode 100644 vendor/gems/graphql/graphql.gemspec delete mode 100644 vendor/gems/graphql/guides/CNAME delete mode 100644 vendor/gems/graphql/guides/_config.yml delete mode 100644 vendor/gems/graphql/guides/_layouts/default.html delete mode 100644 vendor/gems/graphql/guides/_layouts/doc_stub.html delete mode 100644 vendor/gems/graphql/guides/_layouts/guide.html delete mode 100644 vendor/gems/graphql/guides/_plugins/api_doc.rb delete mode 100644 vendor/gems/graphql/guides/_sass/reset.scss delete mode 100644 vendor/gems/graphql/guides/_tasks/site.rb delete mode 100644 vendor/gems/graphql/guides/authorization/authorization.md delete mode 100644 vendor/gems/graphql/guides/authorization/can_can_integration.md delete mode 100644 vendor/gems/graphql/guides/authorization/overview.md delete mode 100644 vendor/gems/graphql/guides/authorization/pundit_integration.md delete mode 100644 vendor/gems/graphql/guides/authorization/scoping.md delete mode 100644 vendor/gems/graphql/guides/authorization/visibility.md delete mode 100644 vendor/gems/graphql/guides/changesets/definition.md delete mode 100644 vendor/gems/graphql/guides/changesets/installation.md delete mode 100644 vendor/gems/graphql/guides/changesets/overview.md delete mode 100644 vendor/gems/graphql/guides/changesets/releases.md delete mode 100644 vendor/gems/graphql/guides/css/main.scss delete mode 100644 vendor/gems/graphql/guides/dataloader/adopting.md delete mode 100644 vendor/gems/graphql/guides/dataloader/async_dataloader.md delete mode 100644 vendor/gems/graphql/guides/dataloader/dataloader.md delete mode 100644 vendor/gems/graphql/guides/dataloader/overview.md delete mode 100644 vendor/gems/graphql/guides/dataloader/parallelism.md delete mode 100644 vendor/gems/graphql/guides/dataloader/sources.md delete mode 100644 vendor/gems/graphql/guides/dataloader/testing.md delete mode 100644 vendor/gems/graphql/guides/defer/defer-graphiql-gif.gif delete mode 100644 vendor/gems/graphql/guides/defer/graphiql.md delete mode 100644 vendor/gems/graphql/guides/defer/overview.md delete mode 100644 vendor/gems/graphql/guides/defer/setup.md delete mode 100644 vendor/gems/graphql/guides/defer/stream.md delete mode 100644 vendor/gems/graphql/guides/defer/usage.md delete mode 100644 vendor/gems/graphql/guides/development.md delete mode 100644 vendor/gems/graphql/guides/errors/error_handling.md delete mode 100644 vendor/gems/graphql/guides/errors/execution_errors.md delete mode 100644 vendor/gems/graphql/guides/errors/overview.md delete mode 100644 vendor/gems/graphql/guides/errors/type_errors.md delete mode 100644 vendor/gems/graphql/guides/faq.md delete mode 100644 vendor/gems/graphql/guides/fields/arguments.md delete mode 100644 vendor/gems/graphql/guides/fields/introduction.md delete mode 100644 vendor/gems/graphql/guides/fields/limits.md delete mode 100644 vendor/gems/graphql/guides/fields/resolvers.md delete mode 100644 vendor/gems/graphql/guides/fields/validation.md delete mode 100644 vendor/gems/graphql/guides/getting_started.md delete mode 100644 vendor/gems/graphql/guides/graphql-ruby-dark.png delete mode 100644 vendor/gems/graphql/guides/graphql-ruby-icon.png delete mode 100644 vendor/gems/graphql/guides/graphql-ruby.png delete mode 100644 vendor/gems/graphql/guides/guides.html delete mode 100644 vendor/gems/graphql/guides/index.html delete mode 100644 vendor/gems/graphql/guides/javascript_client/ably_key.png delete mode 100644 vendor/gems/graphql/guides/javascript_client/apollo_subscriptions.md delete mode 100644 vendor/gems/graphql/guides/javascript_client/graphiql_subscriptions.md delete mode 100644 vendor/gems/graphql/guides/javascript_client/overview.md delete mode 100644 vendor/gems/graphql/guides/javascript_client/relay_subscriptions.md delete mode 100644 vendor/gems/graphql/guides/javascript_client/sync.md delete mode 100644 vendor/gems/graphql/guides/javascript_client/urql_subscriptions.md delete mode 100644 vendor/gems/graphql/guides/js/search.js delete mode 100644 vendor/gems/graphql/guides/language_tools/c_parser.md delete mode 100644 vendor/gems/graphql/guides/language_tools/visitor.md delete mode 100644 vendor/gems/graphql/guides/limiters/active_operation_limiter_dashboard.png delete mode 100644 vendor/gems/graphql/guides/limiters/active_operations.md delete mode 100644 vendor/gems/graphql/guides/limiters/deployment.md delete mode 100644 vendor/gems/graphql/guides/limiters/overview.md delete mode 100644 vendor/gems/graphql/guides/limiters/redis.md delete mode 100644 vendor/gems/graphql/guides/limiters/runtime.md delete mode 100644 vendor/gems/graphql/guides/limiters/runtime_limiter_dashboard.png delete mode 100644 vendor/gems/graphql/guides/limiters/soft_button.png delete mode 100644 vendor/gems/graphql/guides/mutations/mutation_authorization.md delete mode 100644 vendor/gems/graphql/guides/mutations/mutation_classes.md delete mode 100644 vendor/gems/graphql/guides/mutations/mutation_errors.md delete mode 100644 vendor/gems/graphql/guides/mutations/mutation_root.md delete mode 100644 vendor/gems/graphql/guides/object_cache/caching.md delete mode 100644 vendor/gems/graphql/guides/object_cache/memcached.md delete mode 100644 vendor/gems/graphql/guides/object_cache/overview.md delete mode 100644 vendor/gems/graphql/guides/object_cache/query-with-cache.png delete mode 100644 vendor/gems/graphql/guides/object_cache/query-without-cache.png delete mode 100644 vendor/gems/graphql/guides/object_cache/redis.md delete mode 100644 vendor/gems/graphql/guides/object_cache/runtime_considerations.md delete mode 100644 vendor/gems/graphql/guides/object_cache/schema_setup.md delete mode 100644 vendor/gems/graphql/guides/operation_store/access_control.md delete mode 100644 vendor/gems/graphql/guides/operation_store/active_record_backend.md delete mode 100644 vendor/gems/graphql/guides/operation_store/add_a_client.png delete mode 100644 vendor/gems/graphql/guides/operation_store/client_workflow.md delete mode 100644 vendor/gems/graphql/guides/operation_store/getting_started.md delete mode 100644 vendor/gems/graphql/guides/operation_store/graphql_ui.png delete mode 100644 vendor/gems/graphql/guides/operation_store/operation_index.png delete mode 100644 vendor/gems/graphql/guides/operation_store/overview.md delete mode 100644 vendor/gems/graphql/guides/operation_store/redis_backend.md delete mode 100644 vendor/gems/graphql/guides/operation_store/request_after.png delete mode 100644 vendor/gems/graphql/guides/operation_store/request_before.png delete mode 100644 vendor/gems/graphql/guides/operation_store/server_management.md delete mode 100644 vendor/gems/graphql/guides/operation_store/sync_example.png delete mode 100644 vendor/gems/graphql/guides/pagination/connection_concepts.md delete mode 100644 vendor/gems/graphql/guides/pagination/cursors.md delete mode 100644 vendor/gems/graphql/guides/pagination/custom_connections.md delete mode 100644 vendor/gems/graphql/guides/pagination/overview.md delete mode 100644 vendor/gems/graphql/guides/pagination/stable_relation_connections.md delete mode 100644 vendor/gems/graphql/guides/pagination/using_connections.md delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-1.26.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.10.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.11.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.12.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.13.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.14.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.2.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.11.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.10.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.11.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.12.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.13.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.14.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.15.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.10.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.11.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.12.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.13.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.14.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.15.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.3.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.10.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.11.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.12.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.13.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.0.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.1.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.10.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.11.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.12.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.13.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.2.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.5.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.6.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.8.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.9.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/pro-1.26.4.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/pro-1.27.3.txt delete mode 100644 vendor/gems/graphql/guides/pro/checksums/pro-1.27.7.txt delete mode 100644 vendor/gems/graphql/guides/pro/dashboard.md delete mode 100644 vendor/gems/graphql/guides/pro/encoders.md delete mode 100644 vendor/gems/graphql/guides/pro/home.md delete mode 100644 vendor/gems/graphql/guides/pro/installation.md delete mode 100644 vendor/gems/graphql/guides/pro/privacy.md delete mode 100644 vendor/gems/graphql/guides/queries/ast_analysis.md delete mode 100644 vendor/gems/graphql/guides/queries/backtrace_annotations.md delete mode 100644 vendor/gems/graphql/guides/queries/complexity_and_depth.md delete mode 100644 vendor/gems/graphql/guides/queries/executing_queries.md delete mode 100644 vendor/gems/graphql/guides/queries/logging.md delete mode 100644 vendor/gems/graphql/guides/queries/lookahead.md delete mode 100644 vendor/gems/graphql/guides/queries/multiplex.md delete mode 100644 vendor/gems/graphql/guides/queries/perfetto_example.png delete mode 100644 vendor/gems/graphql/guides/queries/phases_of_execution.md delete mode 100644 vendor/gems/graphql/guides/queries/response_extensions.md delete mode 100644 vendor/gems/graphql/guides/queries/timeout.md delete mode 100644 vendor/gems/graphql/guides/queries/tracing.md delete mode 100644 vendor/gems/graphql/guides/related_projects.md delete mode 100644 vendor/gems/graphql/guides/relay/range_add.md delete mode 100644 vendor/gems/graphql/guides/schema/definition.md delete mode 100644 vendor/gems/graphql/guides/schema/dynamic_types.md delete mode 100644 vendor/gems/graphql/guides/schema/generators.md delete mode 100644 vendor/gems/graphql/guides/schema/introspection.md delete mode 100644 vendor/gems/graphql/guides/schema/lazy_execution.md delete mode 100644 vendor/gems/graphql/guides/schema/object_identification.md delete mode 100644 vendor/gems/graphql/guides/schema/root_types.md delete mode 100644 vendor/gems/graphql/guides/schema/sdl.md delete mode 100644 vendor/gems/graphql/guides/search.html delete mode 100644 vendor/gems/graphql/guides/subscriptions/ably_implementation.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/action_cable_implementation.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/broadcast.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/implementation.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/multi_tenant.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/overview.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/pusher_implementation.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/pusher_webhook_configuration.png delete mode 100644 vendor/gems/graphql/guides/subscriptions/redis_dashboard_1.png delete mode 100644 vendor/gems/graphql/guides/subscriptions/redis_dashboard_2.png delete mode 100644 vendor/gems/graphql/guides/subscriptions/subscription_classes.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/subscription_type.md delete mode 100644 vendor/gems/graphql/guides/subscriptions/triggers.md delete mode 100644 vendor/gems/graphql/guides/testing/helpers.md delete mode 100644 vendor/gems/graphql/guides/testing/integration_tests.md delete mode 100644 vendor/gems/graphql/guides/testing/overview.md delete mode 100644 vendor/gems/graphql/guides/testing/profiling.md delete mode 100644 vendor/gems/graphql/guides/testing/schema_structure.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/directives.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/enums.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/extensions.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/field_extensions.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/input_objects.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/interfaces.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/lists.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/non_nulls.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/objects.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/scalars.md delete mode 100644 vendor/gems/graphql/guides/type_definitions/unions.md delete mode 100644 vendor/gems/graphql/javascript_client/CHANGELOG.md delete mode 100644 vendor/gems/graphql/javascript_client/LICENSE.md delete mode 100644 vendor/gems/graphql/javascript_client/jest.config.js delete mode 100644 vendor/gems/graphql/javascript_client/package.json delete mode 100644 vendor/gems/graphql/javascript_client/readme.md delete mode 100644 vendor/gems/graphql/javascript_client/src/__generated__/Card_card.graphql.js delete mode 100644 vendor/gems/graphql/javascript_client/src/__generated__/GetStuff.graphql.js delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/__snapshots__/syncTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/apollo.config.js delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/fragment.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/gen/output.json delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/mutation.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/query.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/schema.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/cliTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/documents/doc1.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/example-apollo-android-operation-output.json delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/example-relay-persisted-queries.json delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/indexTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/frag_1.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/frag_2.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/frag_3.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/frag_4.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/op_1.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/op_2.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/op_3.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_1.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_2.graphql delete mode 100644 vendor/gems/graphql/javascript_client/src/__tests__/syncTest.ts delete mode 100755 vendor/gems/graphql/javascript_client/src/cli.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/index.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/AblyLink.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableLink.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableSubscriber.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/PusherLink.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/PusherSubscriber.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/SubscriptionExchange.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/AblyLinkTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/PusherLinkTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/SubscriptionExchangeTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/addGraphQLSubscriptionsTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyFetcherTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyHandlerTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableFetcherTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableHandlerTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createPusherFetcherTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createRelaySubscriptionHandlerTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/registryTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/addGraphQLSubscriptions.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createAblyFetcher.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createAblyHandler.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableFetcher.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableHandler.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createPusherFetcher.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createPusherHandler.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/createRelaySubscriptionHandler.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/subscriptions/registry.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/dumpPayloadTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateClientTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateJsonClientTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareIsolatedFilesTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/preparePersistedQueryListTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareProjectTest.ts.snap delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/addTypenameToSelectionSetTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/dumpPayloadTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/generate-persisted-query-manifest.json delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/generateClientTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsClientTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsonClientTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareIsolatedFilesTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/preparePersistedQueryListTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareProjectTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/removeClientFieldsTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/__tests__/sendPayloadTest.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/addTypenameToSelectionSet.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/dumpPayload.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/generateClient.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/index.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/logger.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/md5.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/js.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/json.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/prepareIsolatedFiles.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/preparePersistedQueryList.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/prepareProject.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/prepareRelay.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/removeClientFields.ts delete mode 100644 vendor/gems/graphql/javascript_client/src/sync/sendPayload.ts delete mode 100644 vendor/gems/graphql/javascript_client/tsconfig.json delete mode 100644 vendor/gems/graphql/lib/generators/graphql/core.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/enum_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/field_extractor.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/input_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/install/mutation_root_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/install/templates/base_mutation.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/install/templates/mutation_type.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/install_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/interface_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/loader_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/mutation_create_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/mutation_delete_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/mutation_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/mutation_update_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/object_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/orm_mutations_base.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/relay.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/relay_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/scalar_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_argument.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_connection.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_edge.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_enum.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_field.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_input_object.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_interface.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_object.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_resolver.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_scalar.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/base_union.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/enum.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/graphql_controller.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/input.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/interface.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/loader.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/mutation.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/mutation_create.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/mutation_delete.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/mutation_update.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/node_type.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/object.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/query_type.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/scalar.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/schema.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/templates/union.erb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/type_generator.rb delete mode 100644 vendor/gems/graphql/lib/generators/graphql/union_generator.rb delete mode 100644 vendor/gems/graphql/lib/graphql.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/analyzer.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/field_usage.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/max_query_complexity.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/max_query_depth.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/query_complexity.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/query_depth.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis/visitor.rb delete mode 100644 vendor/gems/graphql/lib/graphql/analysis_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/autoload.rb delete mode 100644 vendor/gems/graphql/lib/graphql/backtrace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/backtrace/table.rb delete mode 100644 vendor/gems/graphql/lib/graphql/backtrace/traced_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/coercion_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/current.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.css delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.js delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/statics/header-icon.png delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/statics/icon.png delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb delete mode 100644 vendor/gems/graphql/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/active_record_association_source.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/active_record_source.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/async_dataloader.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/null_dataloader.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/request.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/request_all.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dataloader/source.rb delete mode 100644 vendor/gems/graphql/lib/graphql/date_encoding_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/dig.rb delete mode 100644 vendor/gems/graphql/lib/graphql/duration_encoding_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/directive_checks.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/errors.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/argument_value.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/arguments.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/arguments_cache.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/execution_errors.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/handles_raw_value.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/resolve.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/runtime.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/interpreter/runtime/graphql_result.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/lazy.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/lazy/lazy_method_map.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/lookahead.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution/multiplex.rb delete mode 100644 vendor/gems/graphql/lib/graphql/execution_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/integer_decoding_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/integer_encoding_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/base_object.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/directive_location_enum.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/directive_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/dynamic_fields.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/entry_points.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/enum_value_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/field_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/input_value_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/introspection_query.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/schema_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/type_kind_enum.rb delete mode 100644 vendor/gems/graphql/lib/graphql/introspection/type_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/invalid_name_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/invalid_null_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/block_string.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/cache.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/comment.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/definition_slice.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/document_from_schema_definition.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/generation.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/lexer.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/nodes.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/parser.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/printer.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/sanitized_printer.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/static_visitor.rb delete mode 100644 vendor/gems/graphql/lib/graphql/language/visitor.rb delete mode 100644 vendor/gems/graphql/lib/graphql/load_application_object_failed_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/name_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/active_record_relation_connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/array_connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/connections.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/mongoid_relation_connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/relation_connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/pagination/sequel_dataset_connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/parse_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/context.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/context/scoped_context.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/fingerprint.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/input_validation_result.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/null_context.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/result.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/validation_pipeline.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/variable_validation_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/query/variables.rb delete mode 100644 vendor/gems/graphql/lib/graphql/railtie.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rake_task.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rake_task/validate.rb delete mode 100644 vendor/gems/graphql/lib/graphql/relay.rb delete mode 100644 vendor/gems/graphql/lib/graphql/relay/range_add.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rubocop.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rubocop/graphql/base_cop.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rubocop/graphql/default_null_true.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rubocop/graphql/default_required_true.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rubocop/graphql/field_type_in_block.rb delete mode 100644 vendor/gems/graphql/lib/graphql/rubocop/graphql/root_types_in_block.rb delete mode 100644 vendor/gems/graphql/lib/graphql/runtime_type_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/addition.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/always_visible.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/argument.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/base_64_encoder.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/build_from_definition.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/built_in_types.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/deprecated.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/feature.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/flagged.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/include.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/one_of.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/skip.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/specified_by.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/directive/transform.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/enum.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/enum_value.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/field.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/field/connection_extension.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/field/scope_extension.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/field_extension.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/find_inherited_value.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/finder.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/has_single_input_argument.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/input_object.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/interface.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/introspection_system.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/late_bound_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/list.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/loader.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/base_dsl_methods.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/build_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/graphql_type_names.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_arguments.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_ast_node.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_dataloader.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_deprecation_reason.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_directives.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_fields.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_interfaces.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_path.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_unresolved_type_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/has_validators.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/relay_shortcuts.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/scoped.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/type_system_helpers.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/member/validates_input.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/mutation.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/non_null.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/object.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/printer.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/relay_classic_mutation.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/resolver.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/resolver/has_payload_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/scalar.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/subscription.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/timeout.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/type_expression.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/type_membership.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/union.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/unique_within_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/all_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/allow_blank_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/allow_null_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/exclusion_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/format_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/inclusion_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/length_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/numericality_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/validator/required_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/visibility.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/visibility/migration.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/visibility/profile.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/visibility/visit.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/warden.rb delete mode 100644 vendor/gems/graphql/lib/graphql/schema/wrapper.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/all_rules.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/base_visitor.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/definition_dependencies.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/interpreter_visitor.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/literal_validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/validation_context.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/validation_timeout_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/static_validation/validator.rb delete mode 100644 vendor/gems/graphql/lib/graphql/string_encoding_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/subscriptions.rb delete mode 100644 vendor/gems/graphql/lib/graphql/subscriptions/action_cable_subscriptions.rb delete mode 100644 vendor/gems/graphql/lib/graphql/subscriptions/broadcast_analyzer.rb delete mode 100644 vendor/gems/graphql/lib/graphql/subscriptions/default_subscription_resolve_extension.rb delete mode 100644 vendor/gems/graphql/lib/graphql/subscriptions/event.rb delete mode 100644 vendor/gems/graphql/lib/graphql/subscriptions/serialize.rb delete mode 100644 vendor/gems/graphql/lib/graphql/testing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/testing/helpers.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/appoptics_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/appoptics_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/appsignal_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/appsignal_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/call_legacy_tracers.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/data_dog_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/data_dog_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/detailed_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/detailed_trace/memory_backend.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/detailed_trace/redis_backend.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/legacy_hooks_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/legacy_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/new_relic_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/new_relic_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/notifications_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/notifications_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/null_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/perfetto_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace.proto delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace_pb.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/platform_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/platform_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/prometheus_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/prometheus_trace/graphql_collector.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/prometheus_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/scout_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/scout_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/sentry_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/statsd_trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/statsd_tracing.rb delete mode 100644 vendor/gems/graphql/lib/graphql/tracing/trace.rb delete mode 100644 vendor/gems/graphql/lib/graphql/type_kinds.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/big_int.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/boolean.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/float.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/id.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/int.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/iso_8601_date.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/iso_8601_date_time.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/iso_8601_duration.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/json.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/base_connection.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/base_edge.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/connection_behaviors.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/edge_behaviors.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/has_node_field.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/has_nodes_field.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/node.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/node_behaviors.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/page_info.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/relay/page_info_behaviors.rb delete mode 100644 vendor/gems/graphql/lib/graphql/types/string.rb delete mode 100644 vendor/gems/graphql/lib/graphql/unauthorized_enum_value_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/unauthorized_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/unauthorized_field_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/unresolved_type_error.rb delete mode 100644 vendor/gems/graphql/lib/graphql/version.rb delete mode 100644 vendor/gems/graphql/readme.md delete mode 100644 vendor/gems/graphql/spec/dummy/.gitignore delete mode 100644 vendor/gems/graphql/spec/dummy/Rakefile delete mode 100644 vendor/gems/graphql/spec/dummy/app/assets/config/manifest.js delete mode 100644 vendor/gems/graphql/spec/dummy/app/assets/javascripts/application.js delete mode 100644 vendor/gems/graphql/spec/dummy/app/channels/application_cable/channel.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/channels/application_cable/connection.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/channels/graphql_channel.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/controllers/application_controller.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/controllers/pages_controller.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/graphql/dummy_schema.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/graphql/not_installed_schema.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/helpers/application_helper.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/jobs/application_job.rb delete mode 100644 vendor/gems/graphql/spec/dummy/app/views/layouts/application.html.erb delete mode 100644 vendor/gems/graphql/spec/dummy/app/views/pages/show.html delete mode 100755 vendor/gems/graphql/spec/dummy/bin/bundle delete mode 100755 vendor/gems/graphql/spec/dummy/bin/rails delete mode 100755 vendor/gems/graphql/spec/dummy/bin/rake delete mode 100755 vendor/gems/graphql/spec/dummy/bin/setup delete mode 100755 vendor/gems/graphql/spec/dummy/bin/update delete mode 100755 vendor/gems/graphql/spec/dummy/bin/yarn delete mode 100644 vendor/gems/graphql/spec/dummy/config.ru delete mode 100644 vendor/gems/graphql/spec/dummy/config/application.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/boot.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/cable.yml delete mode 100644 vendor/gems/graphql/spec/dummy/config/database.yml delete mode 100644 vendor/gems/graphql/spec/dummy/config/environment.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/environments/development.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/environments/production.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/environments/test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/application_controller_renderer.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/backtrace_silencers.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/cookies_serializer.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/filter_parameter_logging.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/graphql_dashboard.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/inflections.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/mime_types.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/initializers/wrap_parameters.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/locales/en.yml delete mode 100644 vendor/gems/graphql/spec/dummy/config/puma.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/routes.rb delete mode 100644 vendor/gems/graphql/spec/dummy/config/secrets.yml delete mode 100644 vendor/gems/graphql/spec/dummy/package.json delete mode 100644 vendor/gems/graphql/spec/dummy/public/404.html delete mode 100644 vendor/gems/graphql/spec/dummy/public/422.html delete mode 100644 vendor/gems/graphql/spec/dummy/public/500.html delete mode 100644 vendor/gems/graphql/spec/dummy/public/apple-touch-icon-precomposed.png delete mode 100644 vendor/gems/graphql/spec/dummy/public/apple-touch-icon.png delete mode 100644 vendor/gems/graphql/spec/dummy/public/favicon.ico delete mode 100644 vendor/gems/graphql/spec/dummy/public/robots.txt delete mode 100644 vendor/gems/graphql/spec/dummy/test/application_system_test_case.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/channels/graphql_channel_test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/controllers/dashboard/application_controller_test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/controllers/dashboard/landings_controller_test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/controllers/dashboard/statics_controller_test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/controllers/dashboard/traces_controller_test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/system/action_cable_subscription_test.rb delete mode 100644 vendor/gems/graphql/spec/dummy/test/test_helper.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/field_type.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/field_type_array.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/field_type_array_corrected.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/field_type_corrected.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/field_type_interface.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/field_type_interface_corrected.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/null_true.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/null_true_corrected.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/required_true.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/required_true_corrected.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/root_types.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/root_types_corrected.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/cop/small_field_type.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/eager_module/eager_class.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module/nested_eager_class.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/eager_module/other_eager_class.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/lazy_module/lazy_class.rb delete mode 100644 vendor/gems/graphql/spec/fixtures/unicode_escapes/query1.graphql delete mode 100644 vendor/gems/graphql/spec/fixtures/unicode_escapes/query2.graphql delete mode 100644 vendor/gems/graphql/spec/graphql/analysis/field_usage_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/analysis/max_query_complexity_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/analysis/max_query_depth_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/analysis/query_complexity_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/analysis/query_depth_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/analysis_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/authorization_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/autoload_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/backtrace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/cop/default_null_true_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/cop/default_required_true_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/cop/field_type_in_block_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/cop/root_types_in_block_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/current_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader/active_record_association_source_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader/active_record_source_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader/async_dataloader_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader/nonblocking_dataloader_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader/snapshots/example.json delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader/source_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/dataloader_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/directive_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/errors_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/instrumentation_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/interpreter/arguments_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/interpreter_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/lazy/lazy_method_map_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/lazy_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/lookahead_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution/multiplex_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/execution_error_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/introspection/directive_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/introspection/entry_points_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/introspection/input_value_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/introspection/introspection_query_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/introspection/schema_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/introspection/type_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/invalid_null_error_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/block_string_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/clexer_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/definition_slice_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/document_from_schema_definition_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/equality_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/generation_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/lexer_examples.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/lexer_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/nodes_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/parser_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/printer_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/sanitized_printer_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/language/visitor_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/logger_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/non_null_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/pagination/active_record_relation_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/pagination/array_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/pagination/connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/pagination/connections_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/pagination/mongoid_relation_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/pagination/sequel_dataset_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/context/scoped_context_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/context_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/executor_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/fingerprint_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/null_context_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/result_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/variable_validation_error_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query/variables_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/query_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/rake_task_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/addition_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/always_visible_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/argument_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/base_64_encoder_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/build_from_definition_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/directive/feature_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/directive/flagged_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/directive/one_of_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/directive/query_level_directive_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/directive/transform_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/directive_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/dynamic_members_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/enum_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/enum_value_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/field/connection_extension_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/field_extension_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/field_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/finder_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/has_single_input_argument_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/input_object_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/instrumentation_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/interface_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/introspection_system_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/list_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/loader_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/build_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/has_arguments_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/has_dataloader_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/has_unresolved_type_error_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/relay_shortcuts_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/scoped_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/member/type_system_helpers_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/mutation_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/non_null_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/object_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/printer_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/relay_classic_mutation_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/resolver_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/scalar_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/subscription_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/timeout_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/type_expression_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/union_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/unique_within_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/all_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/allow_blank_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/allow_null_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/exclusion_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/format_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/inclusion_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/length_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/numericality_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/required_validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator/validator_helpers.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/visibility/profile_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/visibility_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema/warden_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/schema_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/directives_are_defined_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fields_will_merge_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragments_are_named_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/fragments_are_used_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/input_object_names_are_unique_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/one_of_input_objects_are_valid_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/variable_names_are_unique_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/static_validation/validator_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/subscriptions/action_cable_subscriptions_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/subscriptions/broadcast_analyzer_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/subscriptions/event_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/subscriptions/serialize_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/subscriptions_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/testing/helpers_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/appoptics_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/appoptics_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/appsignal_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/data_dog_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/data_dog_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/detailed_trace/backend_assertions.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/detailed_trace/memory_backend_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/detailed_trace/redis_backend_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/detailed_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/legacy_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/new_relic_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/new_relic_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/notifications_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/notifications_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/perfetto_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/platform_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/platform_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/prometheus_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/prometheus_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/scout_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/scout_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/sentry_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/snapshots/example-rails-7-0.json delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/snapshots/example-rails-7-1.json delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/snapshots/example-rails-8-1.json delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/statsd_trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/statsd_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/trace_modes_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing/trace_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/type_kinds/type_kind_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/big_int_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/boolean_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/float_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/id_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/int_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/iso_8601_date_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/iso_8601_date_time_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/relay/base_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/relay/base_edge_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/relay/has_node_field_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/relay/node_behaviors_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql/types/string_spec.rb delete mode 100644 vendor/gems/graphql/spec/graphql_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/mongoid/graphql/relay/mongo_relation_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/mongoid/star_trek/data.rb delete mode 100644 vendor/gems/graphql/spec/integration/mongoid/star_trek/schema.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/data.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/base_generator_test.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/enum_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/input_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/install_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/interface_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/loader_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/mutation_create_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/mutation_delete_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/mutation_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/mutation_update_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/object_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/relay_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/scalar_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/generators/graphql/union_generator_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/dataloader/async_dataloader_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/dataloader_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/input_object_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/query/variables_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/relay/array_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/relay/connection_resolve_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/relay/connection_type_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/relay/page_info_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/relay/range_add_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/relay/relation_connection_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/schema_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/tracing/active_support_notifications_tracing_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/graphql/types/iso_8601_duration_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/query_logs_spec.rb delete mode 100644 vendor/gems/graphql/spec/integration/rails/spec_helper.rb delete mode 100644 vendor/gems/graphql/spec/spec_helper.rb delete mode 100644 vendor/gems/graphql/spec/support/active_record_setup.rb delete mode 100644 vendor/gems/graphql/spec/support/appoptics.rb delete mode 100644 vendor/gems/graphql/spec/support/connection_assertions.rb delete mode 100644 vendor/gems/graphql/spec/support/datadog.rb delete mode 100644 vendor/gems/graphql/spec/support/dummy/data.rb delete mode 100644 vendor/gems/graphql/spec/support/dummy/schema.rb delete mode 100644 vendor/gems/graphql/spec/support/dummy_scheduler.rb delete mode 100644 vendor/gems/graphql/spec/support/global_id.rb delete mode 100644 vendor/gems/graphql/spec/support/jazz.rb delete mode 100644 vendor/gems/graphql/spec/support/lazy_helpers.rb delete mode 100644 vendor/gems/graphql/spec/support/magic_cards/schema.graphql delete mode 100644 vendor/gems/graphql/spec/support/minimum_input_object.rb delete mode 100644 vendor/gems/graphql/spec/support/mongoid_setup.rb delete mode 100644 vendor/gems/graphql/spec/support/new_relic.rb delete mode 100644 vendor/gems/graphql/spec/support/parser/filename_example.graphql delete mode 100644 vendor/gems/graphql/spec/support/parser/filename_example_error_1.graphql delete mode 100644 vendor/gems/graphql/spec/support/parser/filename_example_error_2.graphql delete mode 100644 vendor/gems/graphql/spec/support/parser/filename_example_invalid_utf8.graphql delete mode 100644 vendor/gems/graphql/spec/support/perfetto_snapshot.rb delete mode 100644 vendor/gems/graphql/spec/support/rubocop_test_helpers.rb delete mode 100644 vendor/gems/graphql/spec/support/scout_apm.rb delete mode 100644 vendor/gems/graphql/spec/support/sentry.rb delete mode 100644 vendor/gems/graphql/spec/support/star_wars/schema.rb delete mode 100644 vendor/gems/graphql/spec/support/static_validation_helpers.rb diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml index bf965b1723e..901db22402a 100644 --- a/.gitlab/ci/reports.gitlab-ci.yml +++ b/.gitlab/ci/reports.gitlab-ci.yml @@ -109,26 +109,6 @@ package_hunter-bundler: variables: PACKAGE_MANAGER: bundler -xray_scan: - extends: - - .default-retry - - .reports:rules:x-ray - stage: lint - needs: [] - image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/code-creation/repository-x-ray:rc - variables: - OUTPUT_DIR: reports - allow_failure: true - script: - - x-ray-scan -p "$CI_PROJECT_DIR" -o "$OUTPUT_DIR" - artifacts: - # this line uses xray_scan job output as source for GitLab Rails code gen feature - reports: - repository_xray: "$OUTPUT_DIR/*/*.json" - # this line saves xray_scan job output in raw form for inspection for testing purposes - paths: - - "$OUTPUT_DIR/*/*.json" - pajamas_adoption: extends: - .default-retry diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 2005fff7775..fb64125fbc1 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -2797,18 +2797,6 @@ - <<: *if-merge-request changes: ["Gemfile.lock", "Gemfile.next.lock"] -.reports:rules:x-ray: - rules: - - <<: *if-default-branch-refs - changes: *dependency-patterns - - <<: *if-merge-request - changes: *dependency-patterns - when: never - - <<: *if-merge-request - changes: [".gitlab/ci/reports.gitlab-ci.yml"] - when: manual - - when: never - .reports:rules:pajamas_adoption: rules: - <<: *if-not-ee diff --git a/Gemfile b/Gemfile index 18007dcef72..eb70a3f6307 100644 --- a/Gemfile +++ b/Gemfile @@ -168,7 +168,7 @@ gem 'grape-path-helpers', '~> 2.0.1', feature_category: :api gem 'rack-cors', '~> 2.0.1', require: 'rack/cors', feature_category: :shared # GraphQL API -gem 'graphql', '2.4.11', path: 'vendor/gems/graphql', feature_category: :api +gem 'graphql', '2.4.13', feature_category: :api gem 'graphql-docs', '~> 5.0.0', group: [:development, :test], feature_category: :api gem 'apollo_upload_server', '~> 2.1.6', feature_category: :api diff --git a/Gemfile.checksum b/Gemfile.checksum index b7891d40a0d..7982de6b91b 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -85,7 +85,7 @@ {"name":"colored2","version":"3.1.2","platform":"ruby","checksum":"b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a"}, {"name":"commonmarker","version":"0.23.11","platform":"ruby","checksum":"9d1d35d358740151bce29235aebfecc63314fb57dd89a83e72d4061b4fe3d2bf"}, {"name":"concurrent-ruby","version":"1.2.3","platform":"ruby","checksum":"82fdd3f8a0816e28d513e637bb2b90a45d7b982bdf4f3a0511722d2e495801e2"}, -{"name":"connection_pool","version":"2.5.1","platform":"ruby","checksum":"ae802a90a4b5a081101b39d618e69921a9a50bea9ac3420a5b8c71f1befa3e9c"}, +{"name":"connection_pool","version":"2.5.2","platform":"ruby","checksum":"c121d090f8217911f960c7b628bf2bf1b1444f284fd854edc188821c4f602108"}, {"name":"console","version":"1.29.2","platform":"ruby","checksum":"afd9b75a1b047059dda22df0e3c0a386e96f50f6752c87c4b00b1a9fcbe77cd6"}, {"name":"cork","version":"0.3.0","platform":"ruby","checksum":"a0a0ac50e262f8514d1abe0a14e95e71c98b24e3378690e5d044daf0013ad4bc"}, {"name":"cose","version":"1.3.0","platform":"ruby","checksum":"63247c66a5bc76e53926756574fe3724cc0a88707e358c90532ae2a320e98601"}, @@ -292,6 +292,7 @@ {"name":"grape-swagger-entity","version":"0.5.5","platform":"ruby","checksum":"a2a0eb28964b1a56775a3571358a9f0a300b703dbaee1ee535adb2a7bed7ece6"}, {"name":"grape_logging","version":"1.8.4","platform":"ruby","checksum":"efcc3e322dbd5d620a68f078733b7db043cf12680144cd03c982f14115c792d1"}, {"name":"graphlyte","version":"1.0.0","platform":"ruby","checksum":"b5af4ab67dde6e961f00ea1c18f159f73b52ed11395bb4ece297fe628fa1804d"}, +{"name":"graphql","version":"2.4.13","platform":"ruby","checksum":"fb1db6e9e24c93c995f8083d66ec65ea70991aa2b68da1b15a360b418af5aa9d"}, {"name":"graphql-docs","version":"5.0.0","platform":"ruby","checksum":"76baca6e5a803a4b6a9fbbbfdbf16742b7c4c546c8592b6e1a7aa4e79e562d04"}, {"name":"grpc","version":"1.63.0","platform":"aarch64-linux","checksum":"dc75c5fd570b819470781d9512105dddfdd11d984f38b8e60bb946f92d1f79ee"}, {"name":"grpc","version":"1.63.0","platform":"arm64-darwin","checksum":"91b93a354508a9d1772f095554f2e4c04358c2b32d7a670e3705b7fc4695c996"}, diff --git a/Gemfile.lock b/Gemfile.lock index 0ec791e8bf4..229316b6cf9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -170,14 +170,6 @@ PATH google-protobuf (~> 3) grpc -PATH - remote: vendor/gems/graphql - specs: - graphql (2.4.11) - base64 - fiber-storage - logger - PATH remote: vendor/gems/mail-smtp_pool specs: @@ -453,7 +445,7 @@ GEM colored2 (3.1.2) commonmarker (0.23.11) concurrent-ruby (1.2.3) - connection_pool (2.5.1) + connection_pool (2.5.2) console (1.29.2) fiber-annotation fiber-local (~> 1.1) @@ -948,6 +940,10 @@ GEM grape rack graphlyte (1.0.0) + graphql (2.4.13) + base64 + fiber-storage + logger graphql-docs (5.0.0) commonmarker (~> 0.23, >= 0.23.6) escape_utils (~> 1.2) @@ -2180,7 +2176,7 @@ DEPENDENCIES grape-swagger-entity (~> 0.5.5) grape_logging (~> 1.8, >= 1.8.4) graphlyte (~> 1.0.0) - graphql (= 2.4.11)! + graphql (= 2.4.13) graphql-docs (~> 5.0.0) grpc (= 1.63.0) gssapi (~> 1.3.1) diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum index ff7b4f040c1..a8df8e246b7 100644 --- a/Gemfile.next.checksum +++ b/Gemfile.next.checksum @@ -85,7 +85,7 @@ {"name":"colored2","version":"3.1.2","platform":"ruby","checksum":"b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a"}, {"name":"commonmarker","version":"0.23.11","platform":"ruby","checksum":"9d1d35d358740151bce29235aebfecc63314fb57dd89a83e72d4061b4fe3d2bf"}, {"name":"concurrent-ruby","version":"1.2.3","platform":"ruby","checksum":"82fdd3f8a0816e28d513e637bb2b90a45d7b982bdf4f3a0511722d2e495801e2"}, -{"name":"connection_pool","version":"2.5.1","platform":"ruby","checksum":"ae802a90a4b5a081101b39d618e69921a9a50bea9ac3420a5b8c71f1befa3e9c"}, +{"name":"connection_pool","version":"2.5.2","platform":"ruby","checksum":"c121d090f8217911f960c7b628bf2bf1b1444f284fd854edc188821c4f602108"}, {"name":"console","version":"1.29.2","platform":"ruby","checksum":"afd9b75a1b047059dda22df0e3c0a386e96f50f6752c87c4b00b1a9fcbe77cd6"}, {"name":"cork","version":"0.3.0","platform":"ruby","checksum":"a0a0ac50e262f8514d1abe0a14e95e71c98b24e3378690e5d044daf0013ad4bc"}, {"name":"cose","version":"1.3.0","platform":"ruby","checksum":"63247c66a5bc76e53926756574fe3724cc0a88707e358c90532ae2a320e98601"}, @@ -292,6 +292,7 @@ {"name":"grape-swagger-entity","version":"0.5.5","platform":"ruby","checksum":"a2a0eb28964b1a56775a3571358a9f0a300b703dbaee1ee535adb2a7bed7ece6"}, {"name":"grape_logging","version":"1.8.4","platform":"ruby","checksum":"efcc3e322dbd5d620a68f078733b7db043cf12680144cd03c982f14115c792d1"}, {"name":"graphlyte","version":"1.0.0","platform":"ruby","checksum":"b5af4ab67dde6e961f00ea1c18f159f73b52ed11395bb4ece297fe628fa1804d"}, +{"name":"graphql","version":"2.4.13","platform":"ruby","checksum":"fb1db6e9e24c93c995f8083d66ec65ea70991aa2b68da1b15a360b418af5aa9d"}, {"name":"graphql-docs","version":"5.0.0","platform":"ruby","checksum":"76baca6e5a803a4b6a9fbbbfdbf16742b7c4c546c8592b6e1a7aa4e79e562d04"}, {"name":"grpc","version":"1.63.0","platform":"aarch64-linux","checksum":"dc75c5fd570b819470781d9512105dddfdd11d984f38b8e60bb946f92d1f79ee"}, {"name":"grpc","version":"1.63.0","platform":"arm64-darwin","checksum":"91b93a354508a9d1772f095554f2e4c04358c2b32d7a670e3705b7fc4695c996"}, diff --git a/Gemfile.next.lock b/Gemfile.next.lock index 921a73a3295..df53e13f900 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -170,14 +170,6 @@ PATH google-protobuf (~> 3) grpc -PATH - remote: vendor/gems/graphql - specs: - graphql (2.4.11) - base64 - fiber-storage - logger - PATH remote: vendor/gems/mail-smtp_pool specs: @@ -465,7 +457,7 @@ GEM colored2 (3.1.2) commonmarker (0.23.11) concurrent-ruby (1.2.3) - connection_pool (2.5.1) + connection_pool (2.5.2) console (1.29.2) fiber-annotation fiber-local (~> 1.1) @@ -960,6 +952,10 @@ GEM grape rack graphlyte (1.0.0) + graphql (2.4.13) + base64 + fiber-storage + logger graphql-docs (5.0.0) commonmarker (~> 0.23, >= 0.23.6) escape_utils (~> 1.2) @@ -2214,7 +2210,7 @@ DEPENDENCIES grape-swagger-entity (~> 0.5.5) grape_logging (~> 1.8, >= 1.8.4) graphlyte (~> 1.0.0) - graphql (= 2.4.11)! + graphql (= 2.4.13) graphql-docs (~> 5.0.0) grpc (= 1.63.0) gssapi (~> 1.3.1) diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 417179a7559..c6b7d0d11c5 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -469,6 +469,7 @@ export default { 'goToFile', 'reviewFile', 'setFileCollapsedByUser', + 'toggleTreeOpen', ]), ...mapActions(useFileBrowser, ['setFileBrowserVisibility']), ...mapVuexActions('findingsDrawer', ['setDrawer']), @@ -805,6 +806,7 @@ export default { class="gl-px-5" :total-files-count="numTotalFiles" @clickFile="goToFile({ path: $event.path })" + @toggleFolder="toggleTreeOpen" />
diff --git a/app/assets/javascripts/diffs/components/diffs_file_tree.vue b/app/assets/javascripts/diffs/components/diffs_file_tree.vue index f2b40c091b5..9fef8d68460 100644 --- a/app/assets/javascripts/diffs/components/diffs_file_tree.vue +++ b/app/assets/javascripts/diffs/components/diffs_file_tree.vue @@ -35,6 +35,11 @@ export default { default: undefined, required: false, }, + groupBlobsListItems: { + type: Boolean, + required: false, + default: true, + }, }, data() { return { @@ -149,7 +154,9 @@ export default { :loaded-files="loadedFiles" :total-files-count="totalFilesCount" :row-height="rowHeight" + :group-blobs-list-items="groupBlobsListItems" @clickFile="onFileClick" + @toggleFolder="$emit('toggleFolder', $event)" />
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index 51e850e5673..4e942ce3da0 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -48,6 +48,11 @@ export default { type: Number, required: true, }, + groupBlobsListItems: { + type: Boolean, + required: false, + default: true, + }, }, data() { return { @@ -62,7 +67,26 @@ export default { 'fileTree', 'allBlobs', 'linkedFile', + 'flatBlobsList', ]), + flatUngroupedList() { + return this.flatBlobsList.reduce((acc, blob, index) => { + const loading = this.isLoading(blob.fileHash); + const lastIndex = acc.length; + const previous = acc[lastIndex - 1]; + const adjacentNonHeader = previous?.isHeader ? acc[lastIndex - 2] : previous; + const isSibling = adjacentNonHeader?.parentPath === blob.parentPath; + if (isSibling) return [...acc, { ...blob, loading, level: 1 }]; + const header = { + key: `header-${index}`, + path: blob.parentPath, + isHeader: true, + tree: [], + level: 0, + }; + return [...acc, header, { ...blob, loading, level: 1 }]; + }, []); + }, filteredTreeList() { let search = this.search.toLowerCase().trim(); @@ -101,8 +125,7 @@ export default { const result = []; const createFlatten = (level, hidden) => (item) => { const isTree = item.type === 'tree'; - const loading = - !isTree && !item.isHeader && this.loadedFiles && !this.loadedFiles[item.fileHash]; + const loading = !isTree && !item.isHeader && this.isLoading(item.fileHash); result.push({ ...item, hidden, @@ -152,6 +175,8 @@ export default { ]; }, treeList() { + if (!this.renderTreeList && !this.groupBlobsListItems && !this.search) + return this.flatUngroupedList; const list = this.linkedFile ? this.flatListWithLinkedFile : this.flatFilteredTreeList; if (this.search) return list; return list.filter((item) => !item.hidden); @@ -166,7 +191,7 @@ export default { }, }, methods: { - ...mapActions(useLegacyDiffs, ['toggleTreeOpen', 'setRenderTreeList', 'setTreeOpen']), + ...mapActions(useLegacyDiffs, ['setRenderTreeList', 'setTreeOpen']), preventClippingSelectedFile(hash) { // let the layout stabilize, we need to wait for: // scroll to file, sticky elements update, file browser height update @@ -194,6 +219,9 @@ export default { .forEach((path) => this.setTreeOpen({ path, opened: true })); } }, + isLoading(fileHash) { + return this.loadedFiles && !this.loadedFiles[fileHash]; + }, }, searchPlaceholder: sprintf(s__('MergeRequest|Search (e.g. *.vue) (%{MODIFIER_KEY}P)'), { MODIFIER_KEY, @@ -260,7 +288,7 @@ export default { :tabindex="item.loading ? -1 : 0" class="gl-relative !gl-m-1" :data-file-row="item.fileHash" - @toggleTreeOpen="toggleTreeOpen" + @toggleTreeOpen="$emit('toggleFolder', $event)" @clickFile="!item.loading && $emit('clickFile', $event)" /> diff --git a/app/assets/javascripts/groups/your_work/graphql/queries/member_groups.query.graphql b/app/assets/javascripts/groups/your_work/graphql/queries/member_groups.query.graphql index a8061f7ba4e..d8dece90dce 100644 --- a/app/assets/javascripts/groups/your_work/graphql/queries/member_groups.query.graphql +++ b/app/assets/javascripts/groups/your_work/graphql/queries/member_groups.query.graphql @@ -1,4 +1,4 @@ -#import "ee_else_ce/organizations/shared/graphql/fragments/group.fragment.graphql" +#import "~/organizations/shared/graphql/fragments/group.fragment.graphql" query getMemberYourWorkGroups($search: String, $sort: String, $parentId: Int, $page: Int) { groups(search: $search, sort: $sort, parentId: $parentId, page: $page) @client { diff --git a/app/assets/javascripts/groups/your_work/graphql/resolvers.js b/app/assets/javascripts/groups/your_work/graphql/resolvers.js index 462cb3d9169..df2492d21e0 100644 --- a/app/assets/javascripts/groups/your_work/graphql/resolvers.js +++ b/app/assets/javascripts/groups/your_work/graphql/resolvers.js @@ -1,6 +1,6 @@ import axios from '~/lib/utils/axios_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; -import { formatGroup } from 'ee_else_ce/groups/your_work/graphql/utils'; +import { formatGroup } from '~/groups/your_work/graphql/utils'; export const resolvers = (endpoint) => ({ Query: { diff --git a/app/assets/javascripts/groups/your_work/graphql/utils.js b/app/assets/javascripts/groups/your_work/graphql/utils.js index 83bd9931909..9cb8a359327 100644 --- a/app/assets/javascripts/groups/your_work/graphql/utils.js +++ b/app/assets/javascripts/groups/your_work/graphql/utils.js @@ -34,4 +34,7 @@ export const formatGroup = (group) => ({ organizationEditPath: '', groupMembersCount: 0, isLinkedToSubscription: false, + markedForDeletionOn: null, + isAdjournedDeletionEnabled: false, + permanentDeletionDate: null, }); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 133199f5d2b..ae51dae60db 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -142,6 +142,14 @@ const sortTreesByTypeAndName = (a, b) => { return 0; }; +export const linkTreeNodes = (tree) => { + return tree.map((entity) => + Object.assign(entity, { + tree: entity.tree.length ? linkTreeNodes(entity.tree) : [], + }), + ); +}; + export const sortTree = (sortedTree) => sortedTree .map((entity) => diff --git a/app/assets/javascripts/organizations/shared/graphql/fragments/base_group.fragment.graphql b/app/assets/javascripts/organizations/shared/graphql/fragments/base_group.fragment.graphql deleted file mode 100644 index 575b8de4316..00000000000 --- a/app/assets/javascripts/organizations/shared/graphql/fragments/base_group.fragment.graphql +++ /dev/null @@ -1,26 +0,0 @@ -fragment BaseGroup on Group { - id - fullPath - fullName - parent { - id - } - webUrl - organizationEditPath - descriptionHtml - avatarUrl - descendantGroupsCount - projectsCount - groupMembersCount - visibility - createdAt - updatedAt - userPermissions { - removeGroup - viewEditPage - } - maxAccessLevel { - integerValue - } - isLinkedToSubscription -} diff --git a/app/assets/javascripts/organizations/shared/graphql/fragments/group.fragment.graphql b/app/assets/javascripts/organizations/shared/graphql/fragments/group.fragment.graphql index 474d47a0d38..50707faaf99 100644 --- a/app/assets/javascripts/organizations/shared/graphql/fragments/group.fragment.graphql +++ b/app/assets/javascripts/organizations/shared/graphql/fragments/group.fragment.graphql @@ -1,5 +1,29 @@ -#import "./base_group.fragment.graphql" - fragment Group on Group { - ...BaseGroup + id + fullPath + fullName + parent { + id + } + webUrl + organizationEditPath + descriptionHtml + avatarUrl + descendantGroupsCount + projectsCount + groupMembersCount + visibility + createdAt + updatedAt + userPermissions { + removeGroup + viewEditPage + } + maxAccessLevel { + integerValue + } + isLinkedToSubscription + markedForDeletionOn + isAdjournedDeletionEnabled + permanentDeletionDate } diff --git a/app/assets/javascripts/organizations/shared/graphql/queries/groups.query.graphql b/app/assets/javascripts/organizations/shared/graphql/queries/groups.query.graphql index 13c79942bd1..373ed7361aa 100644 --- a/app/assets/javascripts/organizations/shared/graphql/queries/groups.query.graphql +++ b/app/assets/javascripts/organizations/shared/graphql/queries/groups.query.graphql @@ -1,5 +1,5 @@ #import "~/graphql_shared/fragments/page_info.fragment.graphql" -#import "ee_else_ce/organizations/shared/graphql/fragments/group.fragment.graphql" +#import "~/organizations/shared/graphql/fragments/group.fragment.graphql" query getOrganizationGroups( $id: OrganizationsOrganizationID! diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js index bf716bf2b1a..278e7bce147 100644 --- a/app/assets/javascripts/pages/users/activity_calendar.js +++ b/app/assets/javascripts/pages/users/activity_calendar.js @@ -288,7 +288,7 @@ export default class ActivityCalendar { $(this.activitiesContainer) .empty() - .append(loadingIconForLegacyJS({ size: 'lg' })); + .append(loadingIconForLegacyJS({ size: 'md', classes: 'gl-my-8' })); $(this.recentActivitiesContainer).hide(); diff --git a/app/assets/javascripts/rapid_diffs/app/file_browser.vue b/app/assets/javascripts/rapid_diffs/app/file_browser.vue index e1470bf1ddc..b2de22b84b1 100644 --- a/app/assets/javascripts/rapid_diffs/app/file_browser.vue +++ b/app/assets/javascripts/rapid_diffs/app/file_browser.vue @@ -4,12 +4,20 @@ import DiffsFileTree from '~/diffs/components/diffs_file_tree.vue'; import { useDiffsList } from '~/rapid_diffs/stores/diffs_list'; import { useFileBrowser } from '~/diffs/stores/file_browser'; import { useDiffsView } from '~/rapid_diffs/stores/diffs_view'; +import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs'; export default { name: 'FileBrowser', components: { DiffsFileTree, }, + props: { + groupBlobsListItems: { + type: Boolean, + required: false, + default: true, + }, + }, computed: { ...mapState(useDiffsView, ['totalFilesCount']), ...mapState(useDiffsList, ['loadedFiles']), @@ -19,6 +27,9 @@ export default { clickFile(file) { this.$emit('clickFile', file); }, + toggleFolder(path) { + useLegacyDiffs().toggleTreeOpen(path); + }, }, }; @@ -29,6 +40,8 @@ export default { floating-resize :loaded-files="loadedFiles" :total-files-count="totalFilesCount" + :group-blobs-list-items="groupBlobsListItems" @clickFile="clickFile" + @toggleFolder="toggleFolder" /> diff --git a/app/assets/javascripts/rapid_diffs/app/index.js b/app/assets/javascripts/rapid_diffs/app/index.js index 5e7d1d12389..0f33b0e8470 100644 --- a/app/assets/javascripts/rapid_diffs/app/index.js +++ b/app/assets/javascripts/rapid_diffs/app/index.js @@ -11,6 +11,7 @@ import { createAlert } from '~/alert'; import { __ } from '~/locale'; import { fixWebComponentsStreamingOnSafari } from '~/rapid_diffs/app/safari_fix'; import { DIFF_FILE_MOUNTED } from '~/rapid_diffs/dom_events'; +import { parseBoolean } from '~/lib/utils/common_utils'; // This facade interface joins together all the bits and pieces of Rapid Diffs: DiffFile, Settings, File browser, etc. // It's a unified entrypoint for Rapid Diffs and all external communications should happen through this interface. @@ -25,7 +26,7 @@ class RapidDiffsFacade { document.querySelector('[data-diffs-list]'), this.DiffFileImplementation, ); - const { reloadStreamUrl, diffsStatsEndpoint, diffFilesEndpoint } = + const { reloadStreamUrl, diffsStatsEndpoint, diffFilesEndpoint, shouldSortMetadataFiles } = document.querySelector('[data-rapid-diffs]').dataset; useDiffsView(pinia).diffsStatsEndpoint = diffsStatsEndpoint; useDiffsView(pinia) @@ -36,7 +37,7 @@ class RapidDiffsFacade { error, }); }); - initFileBrowser(diffFilesEndpoint).catch((error) => { + initFileBrowser(diffFilesEndpoint, parseBoolean(shouldSortMetadataFiles)).catch((error) => { createAlert({ message: __('Failed to load file browser. Try reloading the page.'), error, diff --git a/app/assets/javascripts/rapid_diffs/app/init_file_browser.js b/app/assets/javascripts/rapid_diffs/app/init_file_browser.js index b4028446af2..3181f80e2c2 100644 --- a/app/assets/javascripts/rapid_diffs/app/init_file_browser.js +++ b/app/assets/javascripts/rapid_diffs/app/init_file_browser.js @@ -1,20 +1,20 @@ import Vue from 'vue'; import axios from 'axios'; -import store from '~/mr_notes/stores'; import { pinia } from '~/pinia/instance'; import { DiffFile } from '~/rapid_diffs/diff_file'; import FileBrowserToggle from '~/diffs/components/file_browser_toggle.vue'; import { generateTreeList } from '~/diffs/utils/tree_worker_utils'; import { SET_TREE_DATA } from '~/diffs/store/mutation_types'; -import { sortTree } from '~/ide/stores/utils'; +import { linkTreeNodes, sortTree } from '~/ide/stores/utils'; +import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs'; import FileBrowser from './file_browser.vue'; -const loadFileBrowserData = async (diffFilesEndpoint) => { +const loadFileBrowserData = async (diffFilesEndpoint, shouldSort) => { const { data } = await axios.get(diffFilesEndpoint); const { treeEntries, tree } = generateTreeList(data.diff_files); - store.commit(`diffs/${SET_TREE_DATA}`, { + useLegacyDiffs()[SET_TREE_DATA]({ treeEntries, - tree: sortTree(tree), + tree: shouldSort ? sortTree(tree) : linkTreeNodes(tree), }); }; @@ -31,15 +31,17 @@ const initToggle = () => { }); }; -const initBrowserComponent = async () => { +const initBrowserComponent = async (shouldSort) => { const el = document.querySelector('[data-file-browser]'); // eslint-disable-next-line no-new new Vue({ el, - store, pinia, render(h) { return h(FileBrowser, { + props: { + groupBlobsListItems: shouldSort, + }, on: { clickFile(file) { DiffFile.findByFileHash(file.fileHash).selectFile(); @@ -50,8 +52,8 @@ const initBrowserComponent = async () => { }); }; -export async function initFileBrowser(diffFilesEndpoint) { +export async function initFileBrowser(diffFilesEndpoint, shouldSort) { initToggle(); - await loadFileBrowserData(diffFilesEndpoint); - initBrowserComponent(); + await loadFileBrowserData(diffFilesEndpoint, shouldSort); + initBrowserComponent(shouldSort); } diff --git a/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer.vue b/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer.vue new file mode 100644 index 00000000000..3700ecf4696 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer.vue @@ -0,0 +1,48 @@ + + + diff --git a/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delete_modal.vue b/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delete_modal.vue index b8e17be0e7e..26b5797cdc3 100644 --- a/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delete_modal.vue +++ b/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_delete_modal.vue @@ -1,10 +1,21 @@ diff --git a/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_inactive_badge.vue b/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_inactive_badge.vue new file mode 100644 index 00000000000..e9600d6dece --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/groups_list/group_list_item_inactive_badge.vue @@ -0,0 +1,41 @@ + + + diff --git a/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue b/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue index f8de9497879..4fd34b245ae 100644 --- a/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue +++ b/app/assets/javascripts/vue_shared/components/groups_list/groups_list_item.vue @@ -3,7 +3,7 @@ import { GlIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui'; import uniqueId from 'lodash/uniqueId'; import { createAlert } from '~/alert'; -import GroupListItemDeleteModal from 'ee_else_ce/vue_shared/components/groups_list/group_list_item_delete_modal.vue'; +import GroupListItemDeleteModal from '~/vue_shared/components/groups_list/group_list_item_delete_modal.vue'; import axios from '~/lib/utils/axios_utils'; import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/visibility_level/constants'; import { ACCESS_LEVEL_LABELS, ACCESS_LEVEL_NO_ACCESS_INTEGER } from '~/access_level/constants'; @@ -16,11 +16,9 @@ import { } from '~/vue_shared/components/resource_lists/constants'; import ListItem from '~/vue_shared/components/resource_lists/list_item.vue'; import ListItemStat from '~/vue_shared/components/resource_lists/list_item_stat.vue'; -import { - renderDeleteSuccessToast, - deleteParams, -} from 'ee_else_ce/vue_shared/components/groups_list/utils'; +import { renderDeleteSuccessToast, deleteParams } from '~/vue_shared/components/groups_list/utils'; import GroupListItemPreventDeleteModal from './group_list_item_prevent_delete_modal.vue'; +import GroupListItemInactiveBadge from './group_list_item_inactive_badge.vue'; export default { i18n: { @@ -38,8 +36,7 @@ export default { GlBadge, GroupListItemPreventDeleteModal, GroupListItemDeleteModal, - GroupListItemInactiveBadge: () => - import('ee_component/vue_shared/components/groups_list/group_list_item_inactive_badge.vue'), + GroupListItemInactiveBadge, }, directives: { GlTooltip: GlTooltipDirective, diff --git a/app/assets/javascripts/vue_shared/components/groups_list/utils.js b/app/assets/javascripts/vue_shared/components/groups_list/utils.js index 499a5df0f1b..fadc0111dd9 100644 --- a/app/assets/javascripts/vue_shared/components/groups_list/utils.js +++ b/app/assets/javascripts/vue_shared/components/groups_list/utils.js @@ -1,15 +1,31 @@ import toast from '~/vue_shared/plugins/global_toast'; import { sprintf, __ } from '~/locale'; -export const renderDeleteSuccessToast = (group) => { +export const renderDeleteSuccessToast = (item) => { + // If delayed deletion is disabled or the project/group is already marked for deletion + if (!item.isAdjournedDeletionEnabled || item.markedForDeletionOn) { + toast( + sprintf(__("Group '%{group_name}' is being deleted."), { + group_name: item.fullName, + }), + ); + + return; + } + toast( - sprintf(__("Group '%{group_name}' is being deleted."), { - group_name: group.fullName, + sprintf(__("Group '%{group_name}' will be deleted on %{date}."), { + group_name: item.fullName, + date: item.permanentDeletionDate, }), ); }; -export const deleteParams = () => { - // Overridden in EE - return {}; +export const deleteParams = (item) => { + // If delayed deletion is disabled or the project/group is not yet marked for deletion + if (!item.isAdjournedDeletionEnabled || !item.markedForDeletionOn) { + return {}; + } + + return { permanently_remove: true }; }; diff --git a/app/assets/javascripts/work_items/pages/work_items_list_app.vue b/app/assets/javascripts/work_items/pages/work_items_list_app.vue index 5ea5369621e..9c335efde70 100644 --- a/app/assets/javascripts/work_items/pages/work_items_list_app.vue +++ b/app/assets/javascripts/work_items/pages/work_items_list_app.vue @@ -158,6 +158,11 @@ export default { required: false, default: 0, }, + eeEpicListQuery: { + type: Object, + required: false, + default: null, + }, withTabs: { type: Boolean, required: false, @@ -199,7 +204,9 @@ export default { }, apollo: { workItems: { - query: getWorkItemsQuery, + query() { + return this.isEpicsList && this.eeEpicListQuery ? this.eeEpicListQuery : getWorkItemsQuery; + }, variables() { return this.queryVariables; }, diff --git a/app/assets/stylesheets/page_bundles/profile.scss b/app/assets/stylesheets/page_bundles/profile.scss index fee43d0a8ed..4fc10430475 100644 --- a/app/assets/stylesheets/page_bundles/profile.scss +++ b/app/assets/stylesheets/page_bundles/profile.scss @@ -181,7 +181,7 @@ $profile-grid-flex: 1fr; @apply gl-bg-subtle; } -.user-activity-content { +.user-activity-content, .user-calendar-activities { position: relative; .system-note-image { diff --git a/app/components/rapid_diffs/app_component.html.haml b/app/components/rapid_diffs/app_component.html.haml index 4a6b1fcd752..1e063de7f4a 100644 --- a/app/components/rapid_diffs/app_component.html.haml +++ b/app/components/rapid_diffs/app_component.html.haml @@ -11,7 +11,7 @@ streamRequest: fetch('#{Gitlab::UrlSanitizer.sanitize(@stream_url)}', { signal: controller.signal }) } -.rd-app{ data: { rapid_diffs: true, reload_stream_url: @reload_stream_url, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint } } +.rd-app{ data: { rapid_diffs: true, reload_stream_url: @reload_stream_url, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, should_sort_metadata_files: @should_sort_metadata_files.to_json } } .rd-app-header .rd-app-file-browser-toggle %div{ data: { file_browser_toggle: true } } diff --git a/app/components/rapid_diffs/app_component.rb b/app/components/rapid_diffs/app_component.rb index 4250f4e8afb..22136a9b67f 100644 --- a/app/components/rapid_diffs/app_component.rb +++ b/app/components/rapid_diffs/app_component.rb @@ -13,6 +13,7 @@ module RapidDiffs update_user_endpoint:, diffs_stats_endpoint:, diff_files_endpoint:, + should_sort_metadata_files: false, lazy: false ) @diffs_slice = diffs_slice @@ -23,6 +24,7 @@ module RapidDiffs @update_user_endpoint = update_user_endpoint @diffs_stats_endpoint = diffs_stats_endpoint @diff_files_endpoint = diff_files_endpoint + @should_sort_metadata_files = should_sort_metadata_files @lazy = lazy end diff --git a/app/controllers/concerns/rapid_diffs/resource.rb b/app/controllers/concerns/rapid_diffs/resource.rb index d8f267c9a70..533d801158c 100644 --- a/app/controllers/concerns/rapid_diffs/resource.rb +++ b/app/controllers/concerns/rapid_diffs/resource.rb @@ -15,7 +15,7 @@ module RapidDiffs return render_404 unless diffs_resource.present? render json: { - diff_files: DiffFileMetadataEntity.represent(diffs_resource.raw_diff_files(sorted: true)) + diff_files: DiffFileMetadataEntity.represent(diffs_resource.raw_diff_files) } end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 512742070ac..570e2e66692 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -201,7 +201,9 @@ class UsersController < ApplicationController rescue StandardError Date.today end + @events = contributions_calendar.events_by_date(@calendar_date).map(&:present) + Events::RenderService.new(current_user).execute(@events) render 'calendar_activities', layout: false end diff --git a/app/events/groups/group_deleted_event.rb b/app/events/groups/group_deleted_event.rb index d89cce17be9..2450ee143a6 100644 --- a/app/events/groups/group_deleted_event.rb +++ b/app/events/groups/group_deleted_event.rb @@ -7,7 +7,8 @@ module Groups 'type' => 'object', 'properties' => { 'group_id' => { 'type' => 'integer' }, - 'root_namespace_id' => { 'type' => 'integer' } + 'root_namespace_id' => { 'type' => 'integer' }, + 'parent_namespace_id' => { 'type' => 'integer' } }, 'required' => %w[group_id root_namespace_id] } diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index e2628ab9fb2..3d500c7ff86 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -476,7 +476,7 @@ module Types end def permanent_deletion_date - return unless group.adjourned_deletion_configured? + return unless group.adjourned_deletion? permanent_deletion_date_formatted(Date.current) end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 69adceea0c5..0e789d50537 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -312,14 +312,14 @@ module EventsHelper def icon_for_profile_event(event) base_class = 'system-note-image' - classes = current_path?('users#activity') ? "#{event.action_name.parameterize}-icon gl-rounded-full gl-bg-strong gl-leading-0" : "user-avatar" - content = current_path?('users#activity') ? icon_for_event(event.action_name, size: 14) : author_avatar(event, size: 32, css_class: 'gl-inline-block', project: event.project) + classes = current_controller?('users') ? "#{event.action_name.parameterize}-icon gl-rounded-full gl-bg-strong gl-leading-0" : "user-avatar" + content = current_controller?('users') ? icon_for_event(event.action_name, size: 14) : author_avatar(event, size: 32, css_class: 'gl-inline-block', project: event.project) tag.div(class: "#{base_class} #{classes}") { content } end def inline_event_icon(event) - unless current_path?('users#activity') + unless current_controller?('users') content_tag :span, class: "system-note-image-inline gl-flex gl-mr-2 gl-mt-1 #{event.action_name.parameterize}-icon" do next design_event_icon(event.action, size: 14) if event.design? @@ -329,7 +329,7 @@ module EventsHelper end def event_user_info(event) - return if current_path?('users#activity') + return if current_controller?('users') tag.div(class: 'event-user-info') do concat tag.span(link_to_author(event), class: 'author-name') @@ -339,7 +339,7 @@ module EventsHelper end def user_profile_activity_classes - current_path?('users#activity') ? ' gl-font-semibold gl-text-default' : '' + current_controller?('users') ? ' gl-font-semibold gl-text-default' : '' end private diff --git a/app/services/draft_notes/publish_service.rb b/app/services/draft_notes/publish_service.rb index f97c89a4573..f469af6b5df 100644 --- a/app/services/draft_notes/publish_service.rb +++ b/app/services/draft_notes/publish_service.rb @@ -14,6 +14,8 @@ module DraftNotes merge_request_activity_counter.track_publish_review_action(user: current_user) end + todo_service.new_review(merge_request, current_user) + success rescue ActiveRecord::RecordInvalid => e message = "Unable to save #{e.record.class.name}: #{e.record.errors.full_messages.join(', ')} " @@ -51,7 +53,6 @@ module DraftNotes keep_around_commits(created_notes) draft_notes.delete_all notification_service.async.new_review(review) - todo_service.new_review(review, current_user) MergeRequests::ResolvedDiscussionNotificationService.new(project: project, current_user: current_user).execute(merge_request) GraphqlTriggers.merge_request_merge_status_updated(merge_request) after_publish diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb index bc2ef4e5802..8edef9a5348 100644 --- a/app/services/groups/destroy_service.rb +++ b/app/services/groups/destroy_service.rb @@ -115,14 +115,13 @@ module Groups # rubocop:enable CodeReuse/ActiveRecord def publish_event - event = Groups::GroupDeletedEvent.new( - data: { - group_id: group.id, - root_namespace_id: group.root_ancestor&.id.to_i # remove safe navigation and `.to_i` with https://gitlab.com/gitlab-org/gitlab/-/issues/508611 - } - ) + event_data = { + group_id: group.id, + root_namespace_id: group.root_ancestor&.id.to_i # remove safe navigation and `.to_i` with https://gitlab.com/gitlab-org/gitlab/-/issues/508611 + } + event_data[:parent_namespace_id] = group.parent_id if group.parent_id.present? - Gitlab::EventStore.publish(event) + Gitlab::EventStore.publish(Groups::GroupDeletedEvent.new(data: event_data)) end end end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 2784a414df0..7b46abd4383 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -193,8 +193,8 @@ class TodoService # # * Mark all outstanding todos on this MR for the current user as done # - def new_review(review, current_user) - resolve_todos_for_target(review.merge_request, current_user) + def new_review(merge_request, current_user) + resolve_todos_for_target(merge_request, current_user) end # When user marks a target as todo diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index d78d66e4d15..d0fd677f204 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -1,24 +1,31 @@ - event = event.present +- event_visible_to_user = event.visible_to_user?(current_user) +- timezone = local_assigns[:timezone] || nil -- if event.visible_to_user?(current_user) - .event-item.gl-border-b-0.gl-pb-3{ class: current_path?('users#activity') ? 'user-profile-activity !gl-pl-7' : 'project-activity-item' } +- if event_visible_to_user || @user&.include_private_contributions? + .event-item.gl-border-b-0.gl-pb-3{ class: current_controller?('users') ? 'user-profile-activity !gl-pl-7' : 'project-activity-item' } .event-item-timestamp.gl-text-sm - #{time_ago_with_tooltip(event.created_at)} - - if event.imported? + - if timezone + %span.js-localtime{ data: { datetime: event.created_at.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), toggle: 'tooltip', placement: 'top' } } + = event.created_at.to_time.in_time_zone(timezone).strftime('%-I:%M%P') + - else + = time_ago_with_tooltip(event.created_at) + - if event_visible_to_user && event.imported? %span · = render "import/shared/imported_badge", text_only: true, importable: _('event') - - if event.wiki_page? - = render "events/event/wiki", event: event - - elsif event.design? - = render 'events/event/design', event: event - - elsif event.created_project_action? - = render "events/event/created_project", event: event - - elsif event.push_action? - = render "events/event/push", event: event - - elsif event.commented_action? - = render "events/event/note", event: event - - else - = render "events/event/common", event: event -- elsif @user&.include_private_contributions? - = render "events/event/private", event: event + - if event_visible_to_user + - if event.wiki_page? + = render "events/event/wiki", event: event + - elsif event.design? + = render 'events/event/design', event: event + - elsif event.created_project_action? + = render "events/event/created_project", event: event + - elsif event.push_action? + = render "events/event/push", event: event + - elsif event.commented_action? + = render "events/event/note", event: event + - else + = render "events/event/common", event: event + - elsif @user&.include_private_contributions? + = render "events/event/private", event: event diff --git a/app/views/events/event/_private.html.haml b/app/views/events/event/_private.html.haml index c6d7c93de25..f5a2b95bd9a 100644 --- a/app/views/events/event/_private.html.haml +++ b/app/views/events/event/_private.html.haml @@ -1,11 +1,7 @@ -.event-item{ class: current_path?('users#activity') ? 'user-profile-activity gl-border-b-0 !gl-pl-7 gl-pb-3' : '' } - .event-item-timestamp.gl-text-sm - = time_ago_with_tooltip(event.created_at) +.system-note-image.gl-rounded-full.gl-bg-strong.gl-leading-0= sprite_icon('eye-slash', size: 14, css_class: 'icon') - .system-note-image.gl-rounded-full.gl-bg-strong.gl-leading-0= sprite_icon('eye-slash', size: 14, css_class: 'icon') += event_user_info(event) - = event_user_info(event) - - .event-title.gl-flex - = inline_event_icon(event) - = s_('Profiles|Made a private contribution') +.event-title.gl-flex.gl-font-semibold.gl-text-default + = inline_event_icon(event) + = s_('Profiles|Made a private contribution') diff --git a/app/views/projects/merge_requests/rapid_diffs.html.haml b/app/views/projects/merge_requests/rapid_diffs.html.haml index 46a53c7d7bd..792252e7a4e 100644 --- a/app/views/projects/merge_requests/rapid_diffs.html.haml +++ b/app/views/projects/merge_requests/rapid_diffs.html.haml @@ -3,7 +3,7 @@ - @content_class = 'rd-page-container diffs-container-limited' = render 'page' -- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint } +- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, should_sort_metadata_files: true } = render ::RapidDiffs::AppComponent.new(**args) do |c| - c.with_diffs_list do = render RapidDiffs::MergeRequestDiffFileComponent.with_collection(@diffs_slice, merge_request: @merge_request, parallel_view: @diff_view == :parallel) diff --git a/app/views/user_settings/personal_access_tokens/_dpop.html.haml b/app/views/user_settings/personal_access_tokens/_dpop.html.haml index 4d788b76df9..850ac76e2ab 100644 --- a/app/views/user_settings/personal_access_tokens/_dpop.html.haml +++ b/app/views/user_settings/personal_access_tokens/_dpop.html.haml @@ -3,10 +3,10 @@ .settings-sticky-header .settings-sticky-header-inner %h3.gl-heading-4.gl-mb-3 - = s_('AccessTokens|Require Demonstrating Proof of Possession (DPoP) headers') + = s_('AccessTokens|Use Demonstrating Proof of Possession (DPoP)') %p.gl-text-secondary - = s_('AccessTokens|Require DPoP headers to access the REST or GraphQL API with a personal access token.') - = link_to s_('AccessTokens|How do I use DPoP headers?'), help_page_path('user/profile/personal_access_tokens.md', anchor: 'require-dpop-headers-with-personal-access-tokens'), target: '_blank', rel: 'noopener noreferrer' + = s_('AccessTokens|Use DPoP to protect your REST or GraphQL API access when using a personal access token.') + = link_to s_('AccessTokens|How do I use DPoP?'), help_page_path('user/profile/personal_access_tokens.md', anchor: 'use-dpop-with-personal-access-tokens'), target: '_blank', rel: 'noopener noreferrer' .form-group = f.gitlab_ui_checkbox_component :dpop_enabled, s_('AccessTokens|Enable DPoP'), checkbox_options: { checked: current_user.dpop_enabled } = f.submit _('Save changes'), pajamas_button: true diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml index e8567719acd..38785ba8605 100644 --- a/app/views/users/_overview.html.haml +++ b/app/views/users/_overview.html.haml @@ -31,7 +31,7 @@ = render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'js-retry-load' }) do = s_('UserProfile|Retry') - .user-calendar-activities + .user-calendar-activities.gl-mb-5 .overview-content-list.user-activity-content.gl-mb-5{ data: { href: user_activity_path, testid: 'user-activity-content' } } = gl_loading_icon(size: 'md', css_class: 'loading') diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index 43c940942da..39ff2e9ecf4 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -2,36 +2,7 @@ = html_escape(_("Contributions for %{calendar_date}")) % { calendar_date: tag.strong(@calendar_date.to_fs(:medium)) } - if @events.any? - %ul.bordered-list - - @events.sort_by(&:created_at).each do |event| - %li - %span.light.js-localtime{ :data => { :datetime => event.created_at.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), :toggle => 'tooltip', :placement => 'top' } } - = sprite_icon('clock', css_class: 'gl-align-text-bottom') - = event.created_at.to_time.in_time_zone(local_timezone_instance(@user.timezone)).strftime('%-I:%M%P') - - if event.visible_to_user?(current_user) - - if event.push_action? - #{event.action_name} #{event.ref_type} - %strong - - commits_path = project_commits_path(event.project, event.ref_name) - = link_to_if event.project.repository.branch_exists?(event.ref_name), event.ref_name, commits_path - - else - = event_action_name(event) - %strong - - if event.note? - = link_to event.note_target.to_reference, event_note_target_url(event), class: 'has-tooltip', title: event.target_title - - elsif event.target - = link_to event.target.to_reference, Gitlab::UrlBuilder.build(event.target, only_path: true), class: 'has-tooltip', title: event.target_title - - = s_('UserProfile|at') - %strong - - if event.project - = link_to_project(event.project) - - elsif event.group - = link_to_group(event.group) - - else - = event.resource_parent_name - - else - = s_('UserProfile|made a private contribution') + = render partial: 'events/event', collection: @events.sort_by(&:created_at), locals: { timezone: local_timezone_instance(@user.timezone) } - else %p = _('No contributions were found') diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index adbadb04262..e1041061937 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -43162,6 +43162,7 @@ The status of the workflow. | `PLAN_APPROVAL_REQUIRED` | The workflow is plan_approval_required. | | `RUNNING` | The workflow is running. | | `STOPPED` | The workflow is stopped. | +| `TOOL_CALL_APPROVAL_REQUIRED` | The workflow is tool_call_approval_required. | ### `EntryType` diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 39d28796988..3806b7fa692 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -3029,16 +3029,8 @@ In the following example, the value of `uid` attribute in the SAML response is s ## Assertion encryption (optional) -GitLab requires the use of TLS encryption with SAML 2.0. Sometimes, GitLab needs -additional assertion encryption. For example, if you: - -- Terminate TLS encryption early at a load balancer. -- Include sensitive details in assertions that you do not want appearing in logs. - -Most organizations should not need additional encryption at this layer. - -Your IdP encrypts the assertion with the public certificate of GitLab. -GitLab decrypts the `EncryptedAssertion` with its private key. +Encrypting the SAML assertion is optional but recommended. This adds an additional layer of protection +to prevent unencrypted data being logged or intercepted by malicious actors. {{< alert type="note" >}} @@ -3047,9 +3039,9 @@ assertion encryption and request signing. {{< /alert >}} -The SAML integration supports `EncryptedAssertion`. To encrypt your assertions, -define the private key and the public certificate of your GitLab instance in the -SAML settings. +To encrypt your SAML assertions, define the private key and the public certificate in the GitLab +SAML settings. Your IdP encrypts the assertion with the public certificate and +GitLab decrypts the assertion with the private key. When you define the key and certificate, replace all line feeds in the key file with `\n`. This makes the key file one long string with no line feeds. diff --git a/doc/user/application_security/policies/_index.md b/doc/user/application_security/policies/_index.md index 366cd1e598e..224aacaaaf4 100644 --- a/doc/user/application_security/policies/_index.md +++ b/doc/user/application_security/policies/_index.md @@ -446,6 +446,29 @@ occurs: In [GitLab 17.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/463064), security policy projects are excluded from push rules that enforce branch name validation. +### Security policy projects + +To prevent the exposure of sensitive information that was intended to remain private in your security policy project, when you link security policy projects to other projects: + +- Don't include sensitive content in your security policy projects. +- Before linking a private security policy project, review the member list of the target project to ensure all members should have access to your policy content. +- Evaluate the visibility settings of target projects. +- Use [security policy management](../../compliance/audit_event_types.md#security-policy-management) audit logs to monitor project linking. + +These recommendations prevent sensitive information exposure for the following reasons: + +- Shared visibility: When a private security project is linked to another project, users with access to the **Security Policies** page of the linked project can view the contents of the `.gitlab/security-policies/policy.yml` file. This includes linking a private security policy project to a public project, which can expose the policy contents to anyone who can access the public project. +- Access control: All members of the project to which a private security project is linked can view the policy file on the **Policy** page, even if they don't have access to the original private repository. + +### Security and compliance controls + +Project maintainers can create policies for projects that interfere with the execution of policies for groups. To limit who can modify policies for groups and ensure that compliance requirements are being met, when you implement critical security or compliance controls: + +- Use custom roles to restrict who can create or modify pipeline execution policies at the project level. +- Configure protected branches for the default branch in your security policy projects to prevent direct pushes. +- Set up merge request approval rules in your security policy projects that require review from designated approvers. +- Monitor and review all policy changes in policies for both groups and projects. + ## Policy management The Policies page displays deployed policies for all available environments. You can check a diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 177eb012966..352515e11b1 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -384,7 +384,7 @@ Prerequisites: You can now create personal access tokens for a service account user with no expiry date. -## Require DPoP headers with personal access tokens +## Use DPoP with personal access tokens {{< details >}} @@ -414,14 +414,17 @@ signed DPoP header requires your corresponding private SSH key. {{< alert type="note" >}} -If you enable this feature, all REST and GraphQL API requests without a valid DPoP header fail with a `DpopValidationError`. +If you enable this feature, all API requests without a valid DPoP header return a `DpopValidationError` error. + +DPoP header is not required for Git operations over HTTPS that include an access token. {{< /alert >}} Prerequisites: -- You must have [added at least one public SSH key](../ssh.md#add-an-ssh-key-to-your-gitlab-account) - to your account, with the **Usage type** of **Signing**, or **Authentication & Signing**. +- You must [add at least one public SSH key](../ssh.md#add-an-ssh-key-to-your-gitlab-account) + to your account, with a **Usage type** of **Signing** or **Authentication & Signing**. + - Your SSH key type must be RSA. - You must have installed and configured the [GitLab CLI](../../editor_extensions/gitlab_cli/_index.md) for your GitLab account. @@ -430,14 +433,14 @@ To require DPoP on all calls to the REST and GraphQL APIs: 1. On the left sidebar, select your avatar. 1. Select **Edit profile**. 1. On the left sidebar, select **Access Tokens**. -1. Go to the **Use Demonstrating Proof of Possession** section, and select **Enable DPoP**. +1. Go to the **Use Demonstrating Proof of Possession (DPoP)** section, and select **Enable DPoP**. 1. Select **Save changes**. 1. To generate a DPoP header with the [GitLab CLI](../../editor_extensions/gitlab_cli/_index.md), run this command in your terminal. Replace `` with your access token, and `~/.ssh/id_rsa` with the location of your private key: ```shell - bin/glab auth dpop-gen --pat "" --private-key ~/.ssh/id_rsa + glab auth dpop-gen --pat "" --private-key ~/.ssh/id_rsa ``` The DPoP header you generated in the CLI can be used: @@ -463,7 +466,7 @@ The DPoP header you generated in the CLI can be used: "https://gitlab.example.com/api/graphql" ``` -To learn more about DPoP headers, see the blueprint +To learn more about DPoP, see the blueprint [Sender Constraining Personal Access Tokens](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/security-feature-blueprints/-/tree/main/sender_constraining_access_tokens). ## Create a personal access token programmatically diff --git a/doc/user/project/codeowners/reference.md b/doc/user/project/codeowners/reference.md index 8666928f23b..e7af2b0221c 100644 --- a/doc/user/project/codeowners/reference.md +++ b/doc/user/project/codeowners/reference.md @@ -383,7 +383,9 @@ Use `**` to match zero or more directories recursively: {{< history >}} -- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/180162) in GitLab 17.10. +- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/180162) in GitLab 17.10 [with a flag](../../../administration/feature_flags.md) named `codeowners_file_exclusions`. +- [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/517075) in GitLab 17.10. +- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/517309) in GitLab 17.11. Feature flag `codeowners_file_exclusions` removed. {{< /history >}} diff --git a/doc/user/project/repository/code_suggestions/_index.md b/doc/user/project/repository/code_suggestions/_index.md index b632727bf9f..603975f151c 100644 --- a/doc/user/project/repository/code_suggestions/_index.md +++ b/doc/user/project/repository/code_suggestions/_index.md @@ -344,6 +344,8 @@ Streaming of Code Generation responses is supported in JetBrains and Visual Stud perceived faster response times. Other supported IDEs will return the generated code in a single block. +Streaming is not enabled for code completion. + ### Direct and indirect connections {{< history >}} diff --git a/gems/gitlab-active-context/lib/active_context/databases/postgresql/executor.rb b/gems/gitlab-active-context/lib/active_context/databases/postgresql/executor.rb index 2b757c12005..f0ec932a25e 100644 --- a/gems/gitlab-active-context/lib/active_context/databases/postgresql/executor.rb +++ b/gems/gitlab-active-context/lib/active_context/databases/postgresql/executor.rb @@ -69,15 +69,15 @@ module ActiveContext fields.each do |field| case field - when Field::Vector - # Vector fields have fixed size based on dimensions - fixed_columns << [field, field.options[:dimensions] * 4] when Field::Bigint # Bigint is 8 bytes fixed_columns << [field, 8] when Field::Keyword, Field::Text # Text fields are variable width variable_columns << field + when Field::Vector + # Vector fields have fixed size based on dimensions + fixed_columns << [field, field.options[:dimensions] * 4] else raise ArgumentError, "Unknown field type: #{field.class}" end @@ -89,12 +89,12 @@ module ActiveContext def add_column_from_field(table, field) case field - when Field::Vector - table.column(field.name, "vector(#{field.options[:dimensions]})") when Field::Bigint table.bigint(field.name, **field.options.except(:index)) when Field::Keyword, Field::Text table.text(field.name, **field.options.except(:index)) + when Field::Vector + table.column(field.name, "vector(#{field.options[:dimensions]})") else raise ArgumentError, "Unknown field type: #{field.class}" end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8a351e67dd8..901ecad7d6c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3112,7 +3112,7 @@ msgstr "" msgid "AccessTokens|Grants read-write access to repositories on private projects using Git-over-HTTP (not using the API)." msgstr "" -msgid "AccessTokens|How do I use DPoP headers?" +msgid "AccessTokens|How do I use DPoP?" msgstr "" msgid "AccessTokens|IP: %{ips}" @@ -3165,12 +3165,6 @@ msgstr "" msgid "AccessTokens|Personal access tokens" msgstr "" -msgid "AccessTokens|Require DPoP headers to access the REST or GraphQL API with a personal access token." -msgstr "" - -msgid "AccessTokens|Require Demonstrating Proof of Possession (DPoP) headers" -msgstr "" - msgid "AccessTokens|Revoke" msgstr "" @@ -3252,6 +3246,12 @@ msgstr "" msgid "AccessTokens|Usage" msgstr "" +msgid "AccessTokens|Use DPoP to protect your REST or GraphQL API access when using a personal access token." +msgstr "" + +msgid "AccessTokens|Use Demonstrating Proof of Possession (DPoP)" +msgstr "" + msgid "AccessTokens|View token usage information" msgstr "" @@ -53667,9 +53667,6 @@ msgstr "" msgid "Secrets manager" msgstr "" -msgid "Secrets|Actions" -msgstr "" - msgid "Secrets|Add a description for the secret" msgstr "" @@ -53697,9 +53694,6 @@ msgstr "" msgid "Secrets|Created" msgstr "" -msgid "Secrets|Delete" -msgstr "" - msgid "Secrets|Delete Secret" msgstr "" @@ -53712,9 +53706,6 @@ msgstr "" msgid "Secrets|Edit %{id}" msgstr "" -msgid "Secrets|Edit secret" -msgstr "" - msgid "Secrets|Enable the Secrets Manager to securely store and manage sensitive information for this project." msgstr "" @@ -53748,9 +53739,6 @@ msgstr "" msgid "Secrets|Provisioning in progress" msgstr "" -msgid "Secrets|Revoke" -msgstr "" - msgid "Secrets|Rotation period" msgstr "" @@ -65880,12 +65868,6 @@ msgstr "" msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice." msgstr "" -msgid "UserProfile|at" -msgstr "" - -msgid "UserProfile|made a private contribution" -msgstr "" - msgid "UserProfile|updated %{updated}" msgstr "" diff --git a/scripts/frontend/quarantined_vue3_specs.txt b/scripts/frontend/quarantined_vue3_specs.txt index c9ec01e5c94..e42b7200ca6 100644 --- a/scripts/frontend/quarantined_vue3_specs.txt +++ b/scripts/frontend/quarantined_vue3_specs.txt @@ -60,7 +60,6 @@ ee/spec/frontend/status_checks/mount_spec.js ee/spec/frontend/usage_quotas/transfer/components/usage_by_month_spec.js ee/spec/frontend/users/identity_verification/components/international_phone_input_spec.js ee/spec/frontend/users/identity_verification/components/verify_phone_verification_code_spec.js -ee/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js ee/spec/frontend/vue_shared/components/projects_list/projects_list_item_spec.js spec/frontend/__helpers__/vue_test_utils_helper_spec.js spec/frontend/access_tokens/index_spec.js diff --git a/spec/components/rapid_diffs/app_component_spec.rb b/spec/components/rapid_diffs/app_component_spec.rb index 866f0df5e73..c7fbf3505ce 100644 --- a/spec/components/rapid_diffs/app_component_spec.rb +++ b/spec/components/rapid_diffs/app_component_spec.rb @@ -11,6 +11,7 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co let(:update_user_endpoint) { '/update_user' } let(:diffs_stats_endpoint) { '/diffs_stats' } let(:diff_files_endpoint) { '/diff_files_metadata' } + let(:should_sort_metadata_files) { false } it "renders diffs slice" do render_component @@ -26,6 +27,16 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co expect(app['data-diff-files-endpoint']).to eq(diff_files_endpoint) end + context "with should_sort_metadata_files set to true" do + let(:should_sort_metadata_files) { true } + + it "renders should_sort_metadata_files" do + render_component + app = page.find('[data-rapid-diffs]') + expect(app['data-should-sort-metadata-files']).to eq('true') + end + end + it "renders view settings" do render_component settings = page.find('[data-view-settings]') @@ -116,6 +127,7 @@ RSpec.describe RapidDiffs::AppComponent, type: :component, feature_category: :co update_user_endpoint:, diffs_stats_endpoint:, diff_files_endpoint:, + should_sort_metadata_files:, lazy: ) end diff --git a/spec/features/merge_request/rapid_diffs/user_views_diffs_spec.rb b/spec/features/merge_request/rapid_diffs/user_views_diffs_spec.rb index dd42b276503..6be7f28798c 100644 --- a/spec/features/merge_request/rapid_diffs/user_views_diffs_spec.rb +++ b/spec/features/merge_request/rapid_diffs/user_views_diffs_spec.rb @@ -21,4 +21,17 @@ RSpec.describe 'User views diffs', :js, feature_category: :code_review_workflow it 'shows the last diff file' do expect(page).to have_selector('[data-testid="rd-diff-file"]', text: last_commit_text) end + + it 'has matching diff file order' do + skip 'MR streaming has wrong order for the diffs, remove skip once the order is correct' + browser_item_selector = '[data-testid="file-row-name-container"]:not(:has([data-testid="folder-open-icon"]))' + browser_item_titles = page.find_all(browser_item_selector).map { |element| element.text.delete("\n").strip } + # TODO: fix this selector, do not rely on classes + diff_titles = page.find_all('.rd-diff-file-title strong:first-of-type').map do |element| + element.text.delete("\n").strip + end + expect(browser_item_titles.each_with_index.all? do |browser_item_title, index| + diff_titles[index].end_with?(browser_item_title) + end).to be(true) + end end diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index ba422f7bfd6..3e86dcf8d07 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -628,6 +628,14 @@ describe('diffs/components/app', () => { wrapper.findComponent(DiffsFileTree).vm.$emit('clickFile', file); expect(store.goToFile).toHaveBeenCalledWith({ path: file.path }); }); + + it('should handle toggleFolder events', () => { + const file = { path: '111.js' }; + store.treeEntries = { 111: { type: 'blob', fileHash: '111', path: '111.js' } }; + createComponent(); + wrapper.findComponent(DiffsFileTree).vm.$emit('toggleFolder', file); + expect(store.toggleTreeOpen).toHaveBeenCalledWith(file); + }); }); }); diff --git a/spec/frontend/diffs/components/diffs_file_tree_spec.js b/spec/frontend/diffs/components/diffs_file_tree_spec.js index f4f3f2989c0..948bd1ebadd 100644 --- a/spec/frontend/diffs/components/diffs_file_tree_spec.js +++ b/spec/frontend/diffs/components/diffs_file_tree_spec.js @@ -55,6 +55,13 @@ describe('DiffsFileTree', () => { expect(wrapper.emitted('clickFile')).toStrictEqual([[obj]]); }); + it('re-emits toggleFolder event', () => { + const obj = {}; + createComponent(); + wrapper.findComponent(TreeList).vm.$emit('toggleFolder', obj); + expect(wrapper.emitted('toggleFolder')).toStrictEqual([[obj]]); + }); + it('sets current file on click', () => { const file = { fileHash: 'foo' }; createComponent(); @@ -200,6 +207,7 @@ describe('DiffsFileTree', () => { }); it('passes down props to tree list', async () => { + const groupBlobsListItems = false; const loadedFiles = { foo: true }; const totalFilesCount = '20'; const rowHeight = 30; @@ -208,10 +216,11 @@ describe('DiffsFileTree', () => { return `${rowHeight}px`; }, }); - createComponent({ loadedFiles, totalFilesCount }); + createComponent({ loadedFiles, totalFilesCount, groupBlobsListItems }); await nextTick(); expect(wrapper.findComponent(TreeList).props('loadedFiles')).toBe(loadedFiles); expect(wrapper.findComponent(TreeList).props('totalFilesCount')).toBe(totalFilesCount); expect(wrapper.findComponent(TreeList).props('rowHeight')).toBe(rowHeight); + expect(wrapper.findComponent(TreeList).props('groupBlobsListItems')).toBe(groupBlobsListItems); }); }); diff --git a/spec/frontend/diffs/components/tree_list_spec.js b/spec/frontend/diffs/components/tree_list_spec.js index 39a5ca976bd..07b26457acd 100644 --- a/spec/frontend/diffs/components/tree_list_spec.js +++ b/spec/frontend/diffs/components/tree_list_spec.js @@ -107,6 +107,22 @@ describe('Diffs tree list component', () => { file_path: 'app/index.js', file_hash: 'app-index', }, + 'unordered.rb': { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'unordered', + key: 'unordered.rb', + name: 'unordered.rb', + path: 'unordered.rb', + removedLines: 0, + tempFile: true, + type: 'blob', + parentPath: '/', + tree: [], + file_path: 'unordered.rb', + file_hash: 'unordered', + }, 'test.rb': { addedLines: 0, changed: true, @@ -143,11 +159,12 @@ describe('Diffs tree list component', () => { useLegacyDiffs().treeEntries = treeEntries; useLegacyDiffs().tree = [ - treeEntries.LICENSE, { ...treeEntries.app, tree: [treeEntries.javascript, treeEntries['index.js'], treeEntries['test.rb']], }, + treeEntries['unordered.rb'], + treeEntries.LICENSE, ]; return treeEntries; @@ -192,7 +209,7 @@ describe('Diffs tree list component', () => { ${'*.js'} | ${2} ${'index.js'} | ${2} ${'app/*.js'} | ${2} - ${'*.js, *.rb'} | ${3} + ${'*.js, *.rb'} | ${5} `('returns $itemSize item for $extension', async ({ extension, itemSize }) => { const input = findDiffTreeSearch(); @@ -205,7 +222,19 @@ describe('Diffs tree list component', () => { }); it('renders tree', () => { - expect(getScroller().props('items')).toHaveLength(6); + expect( + getScroller() + .props('items') + .map((item) => item.path), + ).toStrictEqual([ + 'app', + 'app/javascript', + 'app/javascript/file.rb', + 'app/index.js', + 'app/test.rb', + 'unordered.rb', + 'LICENSE', + ]); }); it('re-emits clickFile event', () => { @@ -219,17 +248,43 @@ describe('Diffs tree list component', () => { expect(getFileRow().props('hideFileStats')).toBe(true); }); - it('calls toggleTreeOpen when clicking folder', () => { + it('re-emits toggleTreeOpen event as toggleFolder', () => { getFileRow().vm.$emit('toggleTreeOpen', 'app'); - - expect(useLegacyDiffs().toggleTreeOpen).toHaveBeenCalledWith('app'); + expect(wrapper.emitted('toggleFolder')).toStrictEqual([['app']]); }); - it('renders when renderTreeList is false', async () => { - useLegacyDiffs().renderTreeList = false; + describe('when renderTreeList is false', () => { + beforeEach(() => { + useLegacyDiffs().renderTreeList = false; + }); - await nextTick(); - expect(getScroller().props('items')).toHaveLength(5); + it('renders list items', async () => { + await nextTick(); + expect( + getScroller() + .props('items') + .map((item) => item.path), + ).toStrictEqual(['app', 'app/index.js', 'app/test.rb', '/', 'unordered.rb', 'LICENSE']); + }); + + it('renders ungrouped list items', async () => { + createComponent({ groupBlobsListItems: false }); + await nextTick(); + expect( + getScroller() + .props('items') + .map((item) => item.path), + ).toStrictEqual([ + 'app', + 'app/index.js', + '/', + 'unordered.rb', + 'app', + 'app/test.rb', + '/', + 'LICENSE', + ]); + }); }); it('dispatches setTreeOpen with all paths for the current diff file', async () => { @@ -396,36 +451,43 @@ describe('Diffs tree list component', () => { }); describe('loading state', () => { - const getLoadedFiles = (offset = 1) => - useLegacyDiffs() - .tree.slice(offset) - .reduce((acc, el) => { - acc[el.fileHash] = true; - return acc; - }, {}); + const getLoadingFile = () => useLegacyDiffs().tree[2]; + const getRootItems = () => + getScroller() + .props('items') + .filter((item) => item.type !== 'tree'); + const findLoadingItem = (loadedFile) => + getRootItems().find((item) => item.type !== 'tree' && item.fileHash !== loadedFile.fileHash); + const findLoadedItem = (loadedFile) => + getRootItems().find((item) => item.type !== 'tree' && item.fileHash === loadedFile.fileHash); beforeEach(() => { setupFilesInState(); }); it('sets loading state for loading files', () => { - const loadedFiles = getLoadedFiles(); - createComponent({ loadedFiles }); - const [firstItem, secondItem] = getScroller().props('items'); - expect(firstItem.loading).toBe(true); - expect(secondItem.loading).toBe(false); + const loadedFile = getLoadingFile(); + createComponent({ loadedFiles: { [loadedFile.fileHash]: true } }); + const loadedItem = findLoadedItem(loadedFile); + const loadingItem = findLoadingItem(loadedFile); + expect(loadingItem.loading).toBe(true); + expect(loadedItem.loading).toBe(false); }); it('is not focusable', () => { - const loadedFiles = getLoadedFiles(); - createComponent({ loadedFiles }); - expect(wrapper.findAllComponents(DiffFileRow).at(0).attributes('tabindex')).toBe('-1'); + const loadedFile = getLoadingFile(); + createComponent({ loadedFiles: { [loadedFile.fileHash]: true } }); + const loadingItemIndex = getScroller().props('items').indexOf(findLoadingItem(loadedFile)); + expect( + wrapper.findAllComponents(DiffFileRow).at(loadingItemIndex).attributes('tabindex'), + ).toBe('-1'); }); it('ignores clicks on loading files', () => { - const loadedFiles = getLoadedFiles(); - createComponent({ loadedFiles }); - wrapper.findAllComponents(DiffFileRow).at(0).vm.$emit('clickFile', {}); + const loadedFile = getLoadingFile(); + createComponent({ loadedFiles: { [loadedFile.fileHash]: true } }); + const loadingItemIndex = getScroller().props('items').indexOf(findLoadingItem(loadedFile)); + wrapper.findAllComponents(DiffFileRow).at(loadingItemIndex).vm.$emit('clickFile', {}); expect(wrapper.emitted('clickFile')).toBe(undefined); }); }); diff --git a/spec/frontend/organizations/shared/components/groups_view_spec.js b/spec/frontend/organizations/shared/components/groups_view_spec.js index e2532bf1402..70665eb49f7 100644 --- a/spec/frontend/organizations/shared/components/groups_view_spec.js +++ b/spec/frontend/organizations/shared/components/groups_view_spec.js @@ -38,8 +38,8 @@ const MOCK_DELETE_PARAMS = { testParam: true, }; -jest.mock('ee_else_ce/vue_shared/components/groups_list/utils', () => ({ - ...jest.requireActual('ee_else_ce/vue_shared/components/groups_list/utils'), +jest.mock('~/vue_shared/components/groups_list/utils', () => ({ + ...jest.requireActual('~/vue_shared/components/groups_list/utils'), renderDeleteSuccessToast: jest.fn(), deleteParams: jest.fn(() => MOCK_DELETE_PARAMS), })); diff --git a/spec/frontend/rapid_diffs/app/app_spec.js b/spec/frontend/rapid_diffs/app/app_spec.js index aa4e61236e3..e91aa3ecf0c 100644 --- a/spec/frontend/rapid_diffs/app/app_spec.js +++ b/spec/frontend/rapid_diffs/app/app_spec.js @@ -41,7 +41,13 @@ describe('Rapid Diffs App', () => { initFileBrowser.mockResolvedValue(); setHTMLFixture( ` -
+
`, @@ -59,7 +65,7 @@ describe('Rapid Diffs App', () => { expect(window.customElements.define).toHaveBeenCalledWith('streaming-error', StreamingError); expect(initHiddenFilesWarning).toHaveBeenCalled(); expect(fixWebComponentsStreamingOnSafari).toHaveBeenCalled(); - expect(initFileBrowser).toHaveBeenCalledWith('/diff-files-metadata'); + expect(initFileBrowser).toHaveBeenCalledWith('/diff-files-metadata', true); }); it('streams remaining diffs', () => { @@ -82,4 +88,23 @@ describe('Rapid Diffs App', () => { document.dispatchEvent(new CustomEvent(DIFF_FILE_MOUNTED)); expect(useDiffsList(pinia).addLoadedFile).toHaveBeenCalled(); }); + + it('skips sorting', () => { + setHTMLFixture( + ` +
+
+
+ `, + ); + createApp(); + app.init(); + expect(initFileBrowser).toHaveBeenCalledWith('/diff-files-metadata', false); + }); }); diff --git a/spec/frontend/rapid_diffs/app/file_browser_spec.js b/spec/frontend/rapid_diffs/app/file_browser_spec.js index 9e734b6ac52..d8545b4a80e 100644 --- a/spec/frontend/rapid_diffs/app/file_browser_spec.js +++ b/spec/frontend/rapid_diffs/app/file_browser_spec.js @@ -8,6 +8,7 @@ import store from '~/mr_notes/stores'; import { useDiffsList } from '~/rapid_diffs/stores/diffs_list'; import { useFileBrowser } from '~/diffs/stores/file_browser'; import { useDiffsView } from '~/rapid_diffs/stores/diffs_view'; +import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs'; Vue.use(PiniaVuePlugin); @@ -27,6 +28,7 @@ describe('FileBrowser', () => { useDiffsList(); useDiffsView(); useFileBrowser(); + useLegacyDiffs(); }); it('passes down props', () => { @@ -63,4 +65,11 @@ describe('FileBrowser', () => { await wrapper.findComponent(DiffsFileTree).vm.$emit('clickFile', file); expect(wrapper.emitted('clickFile')).toStrictEqual([[file]]); }); + + it('handles toggleFolder', async () => { + const path = 'foo'; + createComponent(); + await wrapper.findComponent(DiffsFileTree).vm.$emit('toggleFolder', path); + expect(useLegacyDiffs().toggleTreeOpen).toHaveBeenCalledWith(path); + }); }); diff --git a/spec/frontend/rapid_diffs/app/init_file_browser_spec.js b/spec/frontend/rapid_diffs/app/init_file_browser_spec.js index b0b3acbf36d..1945cc4a5d3 100644 --- a/spec/frontend/rapid_diffs/app/init_file_browser_spec.js +++ b/spec/frontend/rapid_diffs/app/init_file_browser_spec.js @@ -15,6 +15,7 @@ jest.mock('~/rapid_diffs/app/file_browser.vue', () => ({ return h('div', { attrs: { 'data-file-browser-component': true, + 'data-group-blobs-list-items': JSON.stringify(this.groupBlobsListItems), }, on: { click: () => { @@ -120,4 +121,10 @@ describe('Init file browser', () => { await waitForPromises(); expect(document.querySelector('[data-file-browser-toggle-component]')).not.toBe(null); }); + + it('disables sorting', async () => { + initFileBrowser(diffFilesEndpoint, false); + await waitForPromises(); + expect(document.querySelector('[data-group-blobs-list-items="false"]')).not.toBe(null); + }); }); diff --git a/spec/frontend/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer_spec.js b/spec/frontend/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer_spec.js new file mode 100644 index 00000000000..c11b3b2a72a --- /dev/null +++ b/spec/frontend/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer_spec.js @@ -0,0 +1,62 @@ +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import GroupListItemDelayedDeletionModalFooter from '~/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer.vue'; +import { groups } from 'jest/vue_shared/components/groups_list/mock_data'; + +describe('GroupListItemDelayedDeletionModalFooter', () => { + let wrapper; + + const [group] = groups; + const MOCK_PERM_DELETION_DATE = '2024-03-31'; + const HELP_PATH = helpPagePath('user/group/_index', { + anchor: 'restore-a-group', + }); + + const defaultProps = { + group, + }; + + const createComponent = ({ props = {} } = {}) => { + wrapper = shallowMountExtended(GroupListItemDelayedDeletionModalFooter, { + propsData: { ...defaultProps, ...props }, + stubs: { + GlSprintf, + }, + }); + }; + + const findDelayedDeletionModalFooter = () => wrapper.findByTestId('delayed-delete-modal-footer'); + const findGlLink = () => wrapper.findComponent(GlLink); + + describe.each` + isAdjournedDeletionEnabled | markedForDeletionOn | footer | link + ${false} | ${null} | ${false} | ${false} + ${false} | ${'2024-03-24'} | ${false} | ${false} + ${true} | ${null} | ${`This group can be restored until ${MOCK_PERM_DELETION_DATE}. Learn more.`} | ${HELP_PATH} + ${true} | ${'2024-03-24'} | ${false} | ${false} + `( + 'when group.isAdjournedDeletionEnabled is $isAdjournedDeletionEnabled and group.markedForDeletionOn is $markedForDeletionOn', + ({ isAdjournedDeletionEnabled, markedForDeletionOn, footer, link }) => { + beforeEach(() => { + createComponent({ + props: { + group: { + ...group, + isAdjournedDeletionEnabled, + markedForDeletionOn, + permanentDeletionDate: MOCK_PERM_DELETION_DATE, + }, + }, + }); + }); + + it(`does ${footer ? 'render' : 'not render'} the delayed deletion modal footer`, () => { + expect( + findDelayedDeletionModalFooter().exists() && findDelayedDeletionModalFooter().text(), + ).toBe(footer); + expect(findGlLink().exists() && findGlLink().attributes('href')).toBe(link); + }); + }, + ); +}); diff --git a/spec/frontend/vue_shared/components/groups_list/group_list_item_delete_modal_spec.js b/spec/frontend/vue_shared/components/groups_list/group_list_item_delete_modal_spec.js index 5001b17af1e..0da4dd75d4f 100644 --- a/spec/frontend/vue_shared/components/groups_list/group_list_item_delete_modal_spec.js +++ b/spec/frontend/vue_shared/components/groups_list/group_list_item_delete_modal_spec.js @@ -1,61 +1,132 @@ +import { GlSprintf } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { stubComponent } from 'helpers/stub_component'; import GroupListItemDeleteModal from '~/vue_shared/components/groups_list/group_list_item_delete_modal.vue'; +import GroupListItemDelayedDeletionModalFooter from '~/vue_shared/components/groups_list/group_list_item_delayed_deletion_modal_footer.vue'; import DangerConfirmModal from '~/vue_shared/components/confirm_danger/confirm_danger_modal.vue'; +import { groups } from 'jest/vue_shared/components/groups_list/mock_data'; -describe('GroupListItemDeleteModalCE', () => { +describe('GroupListItemDeleteModal', () => { let wrapper; + const [group] = groups; + + const MOCK_PERM_DELETION_DATE = '2024-03-31'; + + const DELETE_MODAL_BODY_OVERRIDE = `This group is scheduled to be deleted on ${MOCK_PERM_DELETION_DATE}. You are about to delete this group, including its subgroups and projects, immediately. This action cannot be undone.`; + const DELETE_MODAL_TITLE_OVERRIDE = 'Delete group immediately?'; + const DEFAULT_DELETE_MODAL_TITLE = 'Are you absolutely sure?'; + const defaultProps = { modalId: '123', phrase: 'mock phrase', + group, }; const createComponent = ({ props = {} } = {}) => { wrapper = shallowMountExtended(GroupListItemDeleteModal, { propsData: { ...defaultProps, ...props }, + stubs: { + GlSprintf, + DangerConfirmModal: stubComponent(DangerConfirmModal, { + template: '
', + }), + }, }); }; const findDangerConfirmModal = () => wrapper.findComponent(DangerConfirmModal); + const findDelayedDeletionModalFooter = () => + wrapper.findComponent(GroupListItemDelayedDeletionModalFooter); + + it('renders modal footer', () => { + createComponent({ props: { visible: true } }); + + expect(findDelayedDeletionModalFooter().props('group')).toEqual(group); + }); describe('when visible is false', () => { beforeEach(() => { createComponent({ props: { visible: false } }); }); - it('does not render modal', () => { - expect(findDangerConfirmModal().exists()).toBe(false); + it('does not show modal', () => { + expect(findDangerConfirmModal().props('visible')).toBe(false); }); }); - describe('when visible is true', () => { - beforeEach(() => { - createComponent({ props: { visible: true } }); - }); + describe('delete modal overrides', () => { + describe.each` + isAdjournedDeletionEnabled | markedForDeletionOn | modalTitle | modalBody + ${false} | ${false} | ${DELETE_MODAL_TITLE_OVERRIDE} | ${DELETE_MODAL_BODY_OVERRIDE} + ${true} | ${false} | ${DEFAULT_DELETE_MODAL_TITLE} | ${''} + ${false} | ${'2024-03-24'} | ${DELETE_MODAL_TITLE_OVERRIDE} | ${DELETE_MODAL_BODY_OVERRIDE} + ${true} | ${'2024-03-24'} | ${DELETE_MODAL_TITLE_OVERRIDE} | ${DELETE_MODAL_BODY_OVERRIDE} + `( + 'when group isAdjournedDeletionEnabled is $isAdjournedDeletionEnabled and markedForDeletionOn is $markedForDeletionOn', + ({ isAdjournedDeletionEnabled, markedForDeletionOn, modalTitle, modalBody }) => { + beforeEach(() => { + createComponent({ + props: { + visible: true, + group: { + ...group, + parent: { id: 1 }, + permanentDeletionDate: MOCK_PERM_DELETION_DATE, + isAdjournedDeletionEnabled, + markedForDeletionOn, + }, + }, + }); + }); - it('does render modal', () => { - expect(findDangerConfirmModal().exists()).toBe(true); - }); + it(`${ + modalTitle === DELETE_MODAL_TITLE_OVERRIDE ? 'does' : 'does not' + } override deletion modal title`, () => { + expect(findDangerConfirmModal().props('modalTitle')).toBe(modalTitle); + }); - describe('when confirm is emitted', () => { + it(`${modalBody ? 'does' : 'does not'} override deletion modal body`, () => { + expect(findDangerConfirmModal().text()).toBe(modalBody); + }); + }, + ); + }); + + describe('events', () => { + describe('deletion modal events', () => { beforeEach(() => { - findDangerConfirmModal().vm.$emit('confirm', { - preventDefault: jest.fn(), + createComponent({ + props: { + visible: true, + group: { + ...group, + parent: { id: 1 }, + }, + }, }); }); - it('emits `confirm` event to parent', () => { - expect(wrapper.emitted('confirm')).toHaveLength(1); - }); - }); + describe('when confirm is emitted', () => { + beforeEach(() => { + findDangerConfirmModal().vm.$emit('confirm', { + preventDefault: jest.fn(), + }); + }); - describe('when change is emitted', () => { - beforeEach(() => { - findDangerConfirmModal().vm.$emit('change', false); + it('emits `confirm` event to parent', () => { + expect(wrapper.emitted('confirm')).toHaveLength(1); + }); }); - it('emits `change` event to parent', () => { - expect(wrapper.emitted('change')).toMatchObject([[false]]); + describe('when change is emitted', () => { + beforeEach(() => { + findDangerConfirmModal().vm.$emit('change', false); + }); + + it('emits `change` event to parent', () => { + expect(wrapper.emitted('change')).toMatchObject([[false]]); + }); }); }); }); diff --git a/spec/frontend/vue_shared/components/groups_list/group_list_item_inactive_badge_spec.js b/spec/frontend/vue_shared/components/groups_list/group_list_item_inactive_badge_spec.js new file mode 100644 index 00000000000..1d2db13f583 --- /dev/null +++ b/spec/frontend/vue_shared/components/groups_list/group_list_item_inactive_badge_spec.js @@ -0,0 +1,45 @@ +import { GlBadge } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import GroupListItemInactiveBadge from '~/vue_shared/components/groups_list/group_list_item_inactive_badge.vue'; +import { groups } from 'jest/vue_shared/components/groups_list/mock_data'; + +describe('GroupListItemInactiveBadge', () => { + let wrapper; + + const [group] = groups; + + const defaultProps = { group }; + + const createComponent = ({ props = {} } = {}) => { + wrapper = shallowMountExtended(GroupListItemInactiveBadge, { + propsData: { ...defaultProps, ...props }, + }); + }; + + const findGlBadge = () => wrapper.findComponent(GlBadge); + + describe.each` + markedForDeletionOn | variant | text + ${null} | ${false} | ${false} + ${'2024-01-01'} | ${'warning'} | ${'Pending deletion'} + `( + 'when group.markedForDeletionOn is $markedForDeletionOn', + ({ markedForDeletionOn, variant, text }) => { + beforeEach(() => { + createComponent({ + props: { + group: { + ...group, + markedForDeletionOn, + }, + }, + }); + }); + + it('renders the badge correctly', () => { + expect(findGlBadge().exists() && findGlBadge().props('variant')).toBe(variant); + expect(findGlBadge().exists() && findGlBadge().text()).toBe(text); + }); + }, + ); +}); diff --git a/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js b/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js index 148cce2f063..0cbc1e6cb3d 100644 --- a/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js +++ b/spec/frontend/vue_shared/components/groups_list/groups_list_item_spec.js @@ -6,7 +6,8 @@ import { mountExtended } from 'helpers/vue_test_utils_helper'; import GroupsListItem from '~/vue_shared/components/groups_list/groups_list_item.vue'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import waitForPromises from 'helpers/wait_for_promises'; -import GroupListItemDeleteModal from 'ee_else_ce/vue_shared/components/groups_list/group_list_item_delete_modal.vue'; +import GroupListItemDeleteModal from '~/vue_shared/components/groups_list/group_list_item_delete_modal.vue'; +import GroupListItemInactiveBadge from '~/vue_shared/components/groups_list/group_list_item_inactive_badge.vue'; import GroupListItemPreventDeleteModal from '~/vue_shared/components/groups_list/group_list_item_prevent_delete_modal.vue'; import { VISIBILITY_TYPE_ICON, @@ -21,7 +22,7 @@ import { TIMESTAMP_TYPE_CREATED_AT, TIMESTAMP_TYPE_UPDATED_AT, } from '~/vue_shared/components/resource_lists/constants'; -import { renderDeleteSuccessToast } from 'ee_else_ce/vue_shared/components/groups_list/utils'; +import { renderDeleteSuccessToast } from '~/vue_shared/components/groups_list/utils'; import { createAlert } from '~/alert'; import { groups } from './mock_data'; @@ -29,8 +30,8 @@ const MOCK_DELETE_PARAMS = { testParam: true, }; -jest.mock('ee_else_ce/vue_shared/components/groups_list/utils', () => ({ - ...jest.requireActual('ee_else_ce/vue_shared/components/groups_list/utils'), +jest.mock('~/vue_shared/components/groups_list/utils', () => ({ + ...jest.requireActual('~/vue_shared/components/groups_list/utils'), renderDeleteSuccessToast: jest.fn(), deleteParams: jest.fn(() => MOCK_DELETE_PARAMS), })); @@ -66,6 +67,7 @@ describe('GroupsListItem', () => { const findAccessLevelBadge = () => wrapper.findByTestId('user-access-role'); const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip); const fireDeleteAction = () => findListActions().props('actions')[ACTION_DELETE].action(); + const findInactiveBadge = () => wrapper.findComponent(GroupListItemInactiveBadge); const deleteModalFireConfirmEvent = async () => { findConfirmationModal().vm.$emit('confirm', { preventDefault: jest.fn(), @@ -462,4 +464,10 @@ describe('GroupsListItem', () => { expect(wrapper.findByTestId('children').exists()).toBe(true); }); + + it('renders inactive badge', () => { + createComponent(); + + expect(findInactiveBadge().exists()).toBe(true); + }); }); diff --git a/spec/frontend/vue_shared/components/groups_list/utils_spec.js b/spec/frontend/vue_shared/components/groups_list/utils_spec.js index 6888588c787..c0b2a5fc6ef 100644 --- a/spec/frontend/vue_shared/components/groups_list/utils_spec.js +++ b/spec/frontend/vue_shared/components/groups_list/utils_spec.js @@ -1,30 +1,76 @@ -import organizationGroupsGraphQlResponse from 'test_fixtures/graphql/organizations/groups.query.graphql.json'; import { deleteParams, renderDeleteSuccessToast } from '~/vue_shared/components/groups_list/utils'; -import { formatGroups } from '~/organizations/shared/utils'; import toast from '~/vue_shared/plugins/global_toast'; jest.mock('~/vue_shared/plugins/global_toast'); -const { - data: { - organization: { - groups: { nodes: groups }, - }, - }, -} = organizationGroupsGraphQlResponse; +const MOCK_GROUP_NO_DELAY_DELETION = { + fullName: 'No Delay Group', + fullPath: 'path/to/group/1', + isAdjournedDeletionEnabled: false, + markedForDeletionOn: null, + permanentDeletionDate: null, +}; + +const MOCK_GROUP_WITH_DELAY_DELETION = { + fullName: 'With Delay Group', + fullPath: 'path/to/group/2', + isAdjournedDeletionEnabled: true, + markedForDeletionOn: null, + permanentDeletionDate: '2024-03-31', +}; + +const MOCK_GROUP_PENDING_DELETION = { + fullName: 'Pending Deletion Group', + fullPath: 'path/to/group/3', + isAdjournedDeletionEnabled: true, + markedForDeletionOn: '2024-03-24', + permanentDeletionDate: '2024-03-31', +}; describe('renderDeleteSuccessToast', () => { - const [MOCK_GROUP] = formatGroups(groups); + it('when delayed deletion is disabled, renders the delete immediately message', () => { + renderDeleteSuccessToast(MOCK_GROUP_NO_DELAY_DELETION); - it('calls toast correctly', () => { - renderDeleteSuccessToast(MOCK_GROUP); + expect(toast).toHaveBeenCalledWith( + `Group '${MOCK_GROUP_NO_DELAY_DELETION.fullName}' is being deleted.`, + ); + }); - expect(toast).toHaveBeenCalledWith(`Group '${MOCK_GROUP.fullName}' is being deleted.`); + it('when delayed deletion is enabled and group is not pending deletion, calls toast with pending deletion info', () => { + renderDeleteSuccessToast(MOCK_GROUP_WITH_DELAY_DELETION); + + expect(toast).toHaveBeenCalledWith( + `Group '${MOCK_GROUP_WITH_DELAY_DELETION.fullName}' will be deleted on ${MOCK_GROUP_WITH_DELAY_DELETION.permanentDeletionDate}.`, + ); + }); + + it('when delayed deletion is enabled and group is already pending deletion, renders the delete immediately message', () => { + renderDeleteSuccessToast(MOCK_GROUP_PENDING_DELETION); + + expect(toast).toHaveBeenCalledWith( + `Group '${MOCK_GROUP_PENDING_DELETION.fullName}' is being deleted.`, + ); }); }); describe('deleteParams', () => { - it('returns {} always', () => { - expect(deleteParams()).toStrictEqual({}); + it('when delayed deletion is disabled, returns an empty object', () => { + const res = deleteParams(MOCK_GROUP_NO_DELAY_DELETION); + + expect(res).toStrictEqual({}); + }); + + it('when delayed deletion is enabled and group is not pending deletion, returns an empty object', () => { + const res = deleteParams(MOCK_GROUP_WITH_DELAY_DELETION); + + expect(res).toStrictEqual({}); + }); + + it('when delayed deletion is enabled and group is already pending deletion, returns permanent deletion params', () => { + const res = deleteParams(MOCK_GROUP_PENDING_DELETION); + + expect(res).toStrictEqual({ + permanently_remove: true, + }); }); }); diff --git a/spec/frontend/work_items/graphql/mock_query.query.graphql b/spec/frontend/work_items/graphql/mock_query.query.graphql new file mode 100644 index 00000000000..7fb8415e8b1 --- /dev/null +++ b/spec/frontend/work_items/graphql/mock_query.query.graphql @@ -0,0 +1,5 @@ +query mockQuery { + group(fullPath: "example") { + id + } +} diff --git a/spec/frontend/work_items/list/components/work_items_list_app_spec.js b/spec/frontend/work_items/list/components/work_items_list_app_spec.js index 12df5eadf46..bc1bee1b7ef 100644 --- a/spec/frontend/work_items/list/components/work_items_list_app_spec.js +++ b/spec/frontend/work_items/list/components/work_items_list_app_spec.js @@ -69,6 +69,7 @@ import { groupWorkItemStateCountsQueryResponse, workItemParentQueryResponse, } from '../../mock_data'; +import mockQuery from '../../graphql/mock_query.query.graphql'; jest.mock('~/lib/utils/scroll_utils', () => ({ scrollUp: jest.fn() })); jest.mock('~/sentry/sentry_browser_wrapper'); @@ -113,6 +114,7 @@ describeSkipVue3(skipReason, () => { workItemsViewPreference = false, workItemsToggleEnabled = true, props = {}, + additionalHandlers = [], } = {}) => { window.gon = { ...window.gon, @@ -130,6 +132,7 @@ describeSkipVue3(skipReason, () => { [workItemParentQuery, workItemParentQueryHandler], [setSortPreferenceMutation, sortPreferenceMutationResponse], [workItemBulkUpdateMutation, workItemBulkUpdateHandler], + ...additionalHandlers, ]), provide: { glFeatures: { @@ -304,6 +307,24 @@ describeSkipVue3(skipReason, () => { }), ); }); + + it('uses the eeEpicListQuery prop rather than the regular query', async () => { + const handler = jest.fn(); + const mockEEQueryHandler = [mockQuery, handler]; + mountComponent({ + provide: { + workItemType: WORK_ITEM_TYPE_NAME_EPIC, + }, + additionalHandlers: [mockEEQueryHandler], + props: { + eeEpicListQuery: mockQuery, + }, + }); + + await waitForPromises(); + + expect(handler).toHaveBeenCalled(); + }); }); describe('when there is an error fetching work items', () => { diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb index e1e26834d6f..8a62fd16c13 100644 --- a/spec/graphql/types/group_type_spec.rb +++ b/spec/graphql/types/group_type_spec.rb @@ -322,7 +322,7 @@ RSpec.describe GitlabSchema.types['Group'], feature_category: :groups_and_projec context 'with adjourned deletion disabled' do before do allow_next_found_instance_of(Group) do |group| - allow(group).to receive_messages(adjourned_deletion?: false, adjourned_deletion_configured?: false) + allow(group).to receive_messages(adjourned_deletion?: false) end end @@ -342,7 +342,7 @@ RSpec.describe GitlabSchema.types['Group'], feature_category: :groups_and_projec context 'with adjourned deletion enabled' do before do allow_next_found_instance_of(Group) do |group| - allow(group).to receive_messages(adjourned_deletion?: true, adjourned_deletion_configured?: true) + allow(group).to receive_messages(adjourned_deletion?: true) end end @@ -361,18 +361,5 @@ RSpec.describe GitlabSchema.types['Group'], feature_category: :groups_and_projec .to eq(::Gitlab::CurrentSettings.deletion_adjourned_period.days.since(Date.current).strftime('%F')) end end - - context 'with adjourned deletion enabled globally' do - before do - allow_next_found_instance_of(Group) do |group| - allow(group).to receive_messages(adjourned_deletion?: false, adjourned_deletion_configured?: true) - end - end - - it 'permanent_deletion_date returns correct date', :freeze_time do - expect(group_data[:permanent_deletion_date]) - .to eq(::Gitlab::CurrentSettings.deletion_adjourned_period.days.since(Date.current).strftime('%F')) - end - end end end diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 25abefdad71..c7c7e47e6e8 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -46,8 +46,8 @@ RSpec.describe EventsHelper, factory_default: :keep, feature_category: :user_pro let(:users_activity_page?) { true } before do - allow(helper).to receive(:current_path?).and_call_original - allow(helper).to receive(:current_path?).with('users#activity').and_return(users_activity_page?) + allow(helper).to receive(:current_controller?).and_call_original + allow(helper).to receive(:current_controller?).with('users').and_return(users_activity_page?) end context 'when on users activity page' do @@ -87,8 +87,8 @@ RSpec.describe EventsHelper, factory_default: :keep, feature_category: :user_pro let(:users_activity_page?) { true } before do - allow(helper).to receive(:current_path?).and_call_original - allow(helper).to receive(:current_path?).with('users#activity').and_return(users_activity_page?) + allow(helper).to receive(:current_controller?).and_call_original + allow(helper).to receive(:current_controller?).with('users').and_return(users_activity_page?) end subject { helper.event_user_info(event) } @@ -564,8 +564,8 @@ RSpec.describe EventsHelper, factory_default: :keep, feature_category: :user_pro let(:users_activity_page?) { true } before do - allow(helper).to receive(:current_path?).and_call_original - allow(helper).to receive(:current_path?).with('users#activity').and_return(users_activity_page?) + allow(helper).to receive(:current_controller?).and_call_original + allow(helper).to receive(:current_controller?).with('users').and_return(users_activity_page?) end context 'when on the user activity page' do diff --git a/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb b/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb index 273df67c255..3a01532018f 100644 --- a/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb +++ b/spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb @@ -59,7 +59,7 @@ RSpec.describe Gitlab::Graphql::QueryAnalyzers::AST::LoggerAnalyzer, feature_cat it 'gracefully handles analysis errors', :aggregate_failures do expect_next_instance_of(described_class::FIELD_USAGE_ANALYZER) do |instance| # pretend it times out on a nested analyzer - expect(instance).to receive(:result).and_raise(Timeout::Error) + expect(instance).to receive(:result).and_raise(GraphQL::Analysis::TimeoutError) end results = GraphQL::Analysis::AST.analyze_query(query, [described_class], multiplex_analyzers: []) diff --git a/spec/services/draft_notes/publish_service_spec.rb b/spec/services/draft_notes/publish_service_spec.rb index 1d400e3a8d8..5ad8e226156 100644 --- a/spec/services/draft_notes/publish_service_spec.rb +++ b/spec/services/draft_notes/publish_service_spec.rb @@ -10,6 +10,8 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl let(:commit) { project.commit(sample_commit.id) } let(:internal) { false } let(:executing_user) { nil } + let(:service) { described_class.new(merge_request, user) } + let(:todo_service) { instance_double(TodoService) } let(:position) do Gitlab::Diff::Position.new( @@ -21,8 +23,13 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl ) end + before do + allow(service).to receive(:todo_service).and_return(todo_service) + allow(todo_service).to receive(:new_review) + end + def publish(draft: nil) - DraftNotes::PublishService.new(merge_request, user).execute(draft: draft, executing_user: executing_user) + service.execute(draft: draft, executing_user: executing_user) end context 'single draft note' do @@ -143,9 +150,7 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl end it 'resolves todos for the MR' do - expect_any_instance_of(TodoService) do |todo_service| - expect(todo_service).to receive(:new_review).with(kind_of(Review), user) - end + expect(todo_service).to receive(:new_review).with(merge_request, user) publish end @@ -255,6 +260,16 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl end end + context 'with no draft notes' do + let(:merge_request) { create(:merge_request) } + + it 'resolves todos for the merge request' do + expect(todo_service).to receive(:new_review).with(merge_request, user) + + publish + end + end + context 'draft notes with suggestions' do let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index 6ad2cf55c8a..8649023432b 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -78,10 +78,16 @@ RSpec.describe Groups::DestroyService, feature_category: :groups_and_projects do it 'publishes a GroupDeletedEvent' do expect { destroy_group(group, user, async) } .to publish_event(Groups::GroupDeletedEvent) - .with( - group_id: group.id, - root_namespace_id: group.root_ancestor.id - ) + .with( + group_id: group.id, + root_namespace_id: group.root_ancestor.id + ) + .and publish_event(Groups::GroupDeletedEvent) + .with( + group_id: nested_group.id, + root_namespace_id: nested_group.root_ancestor.id, + parent_namespace_id: group.id + ) end end end @@ -102,6 +108,7 @@ RSpec.describe Groups::DestroyService, feature_category: :groups_and_projects do before do # Don't run Sidekiq to verify that group and projects are not actually destroyed Sidekiq::Testing.fake! { destroy_group(group, user, true) } + Sidekiq::Testing.fake! { destroy_group(nested_group, user, true) } end it 'verifies original paths and projects still exist' do diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 639573fe403..3217607c35c 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -1226,8 +1226,7 @@ RSpec.describe TodoService, feature_category: :notifications do second_todo = create(:todo, :pending, :review_requested, user: john_doe, project: project, target: mentioned_mr, author: author) third_todo = create(:todo, :pending, :mentioned, user: john_doe, project: project, target: mentioned_mr, author: author) - review = Review.new(merge_request: mentioned_mr) - service.new_review(review, john_doe) + service.new_review(mentioned_mr, john_doe) expect(first_todo.reload).to be_done expect(second_todo.reload).to be_done diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index 90e691b70d0..7f3a5327d6f 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -510,7 +510,8 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do 'BulkImports::RelationExportWorker' => 6, 'Ci::Runners::ExportUsageCsvWorker' => 3, 'AppSec::ContainerScanning::ScanImageWorker' => 3, - 'Ci::DestroyOldPipelinesWorker' => 0 + 'Ci::DestroyOldPipelinesWorker' => 0, + 'AuditEvents::AuditEventStreamingWorker' => 3 }.merge(extra_retry_exceptions) end diff --git a/vendor/gems/graphql/.gitlab-ci.yml b/vendor/gems/graphql/.gitlab-ci.yml deleted file mode 100644 index 0de3a8f7824..00000000000 --- a/vendor/gems/graphql/.gitlab-ci.yml +++ /dev/null @@ -1,24 +0,0 @@ -include: - - local: gems/gem.gitlab-ci.yml - inputs: - gem_name: "graphql" - gem_path_prefix: "vendor/gems/" - -rspec: - extends: .default - before_script: - - apt-get update -qq - - apt-get install -qq -y cmake - - cmake --version - - cd vendor/gems/graphql - - ruby -v # Print out ruby version for debugging - - gem update --system - - bundle_version=$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1 | sed -e 's/[[:space:]]//') - - gem install bundler --version "$bundle_version" --no-document # Bundler is not installed with the image - - bundle config # Show bundler configuration - - bundle install --jobs=$(nproc) --retry=3 - script: - - bundle exec rake test - parallel: - matrix: - - RUBY_VERSION: ["${RUBY_VERSION_DEFAULT}", "${RUBY_VERSION_NEXT}"] diff --git a/vendor/gems/graphql/.yardopts b/vendor/gems/graphql/.yardopts deleted file mode 100644 index 962de13a9af..00000000000 --- a/vendor/gems/graphql/.yardopts +++ /dev/null @@ -1,5 +0,0 @@ ---no-private ---markup=markdown ---readme=readme.md ---title='GraphQL Ruby API Documentation' -'lib/**/*.rb' - '*.md' diff --git a/vendor/gems/graphql/CHANGELOG-enterprise.md b/vendor/gems/graphql/CHANGELOG-enterprise.md deleted file mode 100644 index d873d4d5a9d..00000000000 --- a/vendor/gems/graphql/CHANGELOG-enterprise.md +++ /dev/null @@ -1,198 +0,0 @@ -# graphql-enterprise - -### Breaking Changes - -### Deprecations - -### New Features - -### Bug Fix - -# 1.5.6 (13 Dec 2024) - -- ObjectCache: Add `CacheableRelation` helper for top-level ActiveRecord relations - -# 1.5.5 (10 Dec 2024) - -- Changesets: Add missing `ensure_loaded` call for class-based changesets - -# 1.5.4 (31 Oct 2024) - -- ObjectCache: Add `reauthorize_cached_objects: false` - -# 1.5.3 (1 Oct 2024) - -- Limiters: Add expiration to rate limit data (to reduce Redis footprint) - -# 1.5.2 (6 Sept 2024) - -- Limiters: Add `connection_pool:` support - -# 1.5.1 (30 Aug 2024) - -- ObjectCache: Add `connection_pool:` support - -# 1.5.0 (26 Jul 2024) - -- ObjectCache: Add Dalli backend for Memcached - -# 1.4.2 (11 Jun 2024) - -- ObjectCache: Add `Schema.fingerprint` hook and `context[:refresh_object_cache]` - -# 1.4.1 (30 May 2024) - -- ObjectCache: properly handle when object fingerprints are evicted but the cached result wasn't - -# 1.4.0 (11 Apr 2024) - -- ObjectCache: add support for `redis_cluster: ...` backend - -# 1.3.4 (18 Mar 2024) - -- ObjectCache: use new `trace_with` API for instrumentation - -# 1.3.3 (30 Jan 2024) - -- ObjectCache: fix compatibility with `run_graphql_field` test helper #4816 - -# 1.3.2 (15 Jan 2024) - -### Bug Fix - -- Limiters: Migrate to new `trace_with` instrumentation API, requires GraphQL-Ruby 2.0.18+ - -# 1.3.1 (12 June 2023) - -### Bug Fix - -- Add missing `require "graphql"` #4511 - -# 1.3.0 (29 May 2023) - -### New Features - -- Changesets: Add `added_in: ...` and `removed_in: ...` for inline definition changes - -# 1.2.0 (10 February 2023) - -### New Features - -- Support the `redis-client` gem as `redis:` (requires graphql-pro 1.24.0+) - -# 1.1.14 (3 November 2022) - -### New Features - -- Limiters: Support `dashboard_charts: false` to disable built-in instrumentation -- Limiters: Support `assign_as:` to use a different accessor method for storing limiter instances on schema classes (add a corresponding `class << self; attr_accessor ...; end` to the schema class to use it) -- Limiters: Support `context_key:` to put runtime info in a different key in query context -- Runtime Limiter: Add `window_ms:` to runtime info - -# 1.1.13 (21 October 2022) - -### Bug Fix - -- Limiter: handle missing fields in MutationLimiter - -# 1.1.12 (18 October 2022) - -### New Features - -- Limiters: add MutationLimiter - -### Bug Fix - -- ObjectCache: Update Redis calls to support redis-rb 5.0 - -# 1.1.11 (25 August 2022) - -### Bug Fix - -- ObjectCache: also update `delete` to handle more than 1000 objects in Lua - -# 1.1.10 (19 August 2022) - -### Bug Fix - -- ObjectCache: read and write objects 1000-at-a-time to avoid overloading Lua scripts in Redis - -# 1.1.9 (3 August 2022) - -### New Features - -- ObjectCache: Add a message to context when a type or field causes a query to be treated as "private" - -### Bug Fix - -- ObjectCache: skip the query analyzer when `context[:skip_object_cache]` is present - -# 1.1.8 (1 August 2022) - -### New Features - -- ObjectCache: Add `ObjectType.cache_dependencies_for(object, context)` to customize dependencies for an object - -### Bug Fix - -- ObjectCache: Fix to make `context[:object_cache][:objects]` a Set -# 1.1.7 (28 July 2022) - -### Bug Fix - -- ObjectCache: remove needless `resolve_type` calls - -# 1.1.6 (28 July 2022) - -### Bug Fix - -- ObjectCache: persist the type names of cached objects, pass them to `Schema.resolve_type` when validating cached responses. - -# 1.1.5 (22 July 2022) - -### New Features - -- ObjectCache: add `cache_introspection: { ttl: ... }` for setting an expiration (in seconds) on introspection fields. - -# 1.1.4 (19 March 2022) - -### Bug Fix - -- ObjectCache: don't create a cache fingerprint if the query is found to be uncacheable during analysis. - -# 1.1.3 (3 March 2022) - -### Bug Fix - -- Changesets: Return an empty set when a schema doesn't use changesets #3972 - -# 1.1.2 (1 March 2022) - -### New Features - -- Changesets: Add introspection methods `Schema.changesets` and `Changeset.changes` - -# 1.1.1 (14 February 2021) - -### Bug Fix - -- Changesets: don't require `context.schema` for plain-Ruby calls to introspection methods #3929 - -# 1.1.0 (24 November 2021) - -### New Features - -- Changesets: Add `GraphQL::Enterprise::Changeset` - -# 1.0.1 (9 November 2021) - -### Bug Fix - -- Object Cache: properly handle invalid queries #3703 - -# 1.0.0 (13 October 2021) - -### New Features - -- Rate limiters: first release -- Object cache: first release diff --git a/vendor/gems/graphql/CHANGELOG-pro.md b/vendor/gems/graphql/CHANGELOG-pro.md deleted file mode 100644 index 833af3988a3..00000000000 --- a/vendor/gems/graphql/CHANGELOG-pro.md +++ /dev/null @@ -1,1331 +0,0 @@ -# graphql-pro - -### Breaking Changes - -### Deprecations - -### New Features - -# 1.29.4 (18 Nov 2024) - -- OperationStore: Add forward compatibility for removing old validation code #5164 - -# 1.29.3 (15 Nov 2024) - -- OperationStore: Improve `sync` performance with `GraphQL::Schema::Visibility` - -# 1.29.2 (4 Sept 2024) - -- Subscriptions: show broadcast subscriber count in dashboard (Pusher requires "subscription count" to be turned on and `use ... show_broadcast_subscribers_count: true`) - -# 1.29.1 (29 Aug 2024) - -- OperationStore: Accept a `context:` in `#add` - -# 1.29.0 (28 Aug 2024) - -- Subscriptions: use a single Pusher or Ably channel to deliver broadcast payloads to subscribers -- Dashboard: fix crash when a topic had no active subscriptions - -# 1.28.1 (22 Aug 2024) - -- Subscriptions: Track `last_triggered_at`; add more metadata to the dashboard. - -# 1.28.0 (20 Aug 2024) - -- OperationStore: require the `ActiveRecord` backend inside an `ActiveSupport.on_load(:active_record) { ... }` block to improve Rails compatibility - -# 1.27.7 (13 Aug 2024) - -- Subscriptions: Fix _another_ Lua error in big cleanup operations - -# 1.27.6 (13 Aug 2024) - -- Subscriptions: Fix Lua error when cleaning up huge numbers of inactive subscriptions - -# 1.27.5 (9 May 2024) - -- OperationStore: remove needless call to `.metadata` #4947 - -# 1.27.4 (2 May 2024) - -- Pundit, CanCan, OperationStore: add Rails generators for getting started - -# 1.27.3 (1 May 2024) - -- OperationStore: Fix `.reindex` for many stored operations #4940 - -# 1.27.2 (30 Apr 2024) - -- Dashboard: handle missing index references gracefully #4940 - -# 1.27.1 (18 Apr 2024) - -- OperationStore: Don't call `query.query_string` if there's already a parsed document #4922 - -# 1.27.0 (11 Apr 2024) - -- RelationConnection: support Arel's `NullsFirst` and `NullsLast` nodes #4910 - -# 1.26.5 (1 Mar 2024) - -- OperationStore::AddOperationBatch: remove rescue for StatementInvalid inside transaction - -# 1.26.4 (27 Feb 2024) - -- RelationConnection: Don't quote table names that weren't quoted in original SQL, fixes #4508 (comment) - -# 1.26.3 (19 Feb 2024) - -- OperationStore: fix `sync` endpoint for Rack 3+ #4829 -- Improve error message handling on Rails 7.1 - -# 1.26.2 (30 Jan 2024) - -- `@defer` / `@stream`: Write delimiters at the end of each patch so that clients respond to payloads more quickly. (Previously, delimiters were added at the start of each patch, so clients had to wait for the _next_ patch before they knew the current one was complete.) - -# 1.26.1 (23 Jan 2024) - -- Pundit integration: improve error message when a `Scope` class is missing - -# 1.26.0 (19 Jan 2024) - -### Breaking Changes - -- Pundit integration: when the integration encounters an Array, it tries to find a configured policy class. If it can't, it raises an error. - - Previously, the integration silently permitted all items in the array; this default has been changed. See #4726 for more discussion of this change. - - If you encounter this error: - - - add `scope: false` to any fields that return arrays to get the previous behavior (no authorization applied to the array; each item authorized on its own) - - Or, apply [scoping](https://graphql-ruby.org/authorization/scoping.html) by manually configuring a `pundit_policy_class` in the field's return type, then adding a `class Scope ...` inside that policy class. See the Pundit docs for the scope class API: https://github.com/varvet/pundit#scopes. - - If you want to continue passing _all_ arrays through without scoping (for example, if you know they've already been authorized another way, or if you're OK with them being authorized one-at-a-time later), you can implement this in your base `Scope` class, for example: - - ```ruby - class BasePolicy - class Scope - def initialize(user, items) - @user = user - @items = items - end - - def resolve - if items.is_a?(Array) - items - else - raise "Implement #{self.class}#resolve to filter these items: #{items.inspect}" - end - end - end - - # Pass this scope class along to subclasses: - def self.inherited(child_class) - child_class.const_set(:Scope, Class.new(BasePolicy::Scope)) - super - end - end - ``` - - Alternatively, you could implement `def self.scope_items(items, context)` to skip arrays, for example: - - ```ruby - module SkipScopingOnArrays - def scope_items(items, context) - if items.is_a?(Array) - items # return these as-is - else - super - end - end - end - - # Then, in type definitions which should skip scoping on arrays: - extend SkipScopingOnArrays - ``` - -# 1.25.2 (29 Dec 2023) - -### New Features - -- Subscriptions: send `more: false` when the server calls `unsubscribe` - -# 1.25.1 (21 Dec 2023) - -### Bug Fix - -- Ably subscriptions: update webhook handler for `presence.message` events - -# 1.25.0 (7 Dec 2023) - -### Bug Fix - -- OperationStore: `.dup` the given `context` to avoid leaking state between queries when indexing -- Subscriptions: use the schema or query logger to output debug messages - -# 1.24.15 (17 Nov 2023) - -### Bug Fix - -- OperationStore: don't sort directives when normalizing, properly retain directives on Operation and Fragment definitions #4703 - -# 1.24.14 (16 Nov 2023) - -### Bug Fix - -- OperationStore: also pass `context:` for ActiveRecord backend batches - -# 1.24.13 (13 Nov 2023) - -### New Features - -- OperationStore: accept `context:` for `AddOperationBatch.call` #4697 - -# 1.24.12 (13 Nov 2023) - -### New Features - -- OperationStore: accept `context:` to `Validate.validate` #4697 - -### Bug Fix - -- OperationStore: don't rescue application-raised `KeyError`s #4699 - -# 1.24.11 (8 Nov 2023) - -### Bug Fix - -- OperationStore: fix compatibility with 1.12.x #4696 - -# 1.24.10 (2 Nov 2023) - -### Bug Fix - -- Improve compatibility with GraphQL-Ruby 1.12.x - -# 1.24.9 (4 Oct 2023) - -### Bug Fix - -- OperationStore: Preserve variable default values of `false` when normalizing queries - -# 1.24.8 (29 Aug 2023) - -### Bug Fix - -- OperationStore: search for operation during `Query#initialize` to avoid races with other instrumentation. Add `use ... trace: true` to get the old behavior. - -# 1.24.7 (16 June 2023) - -### Bug Fix - -- Stable relation connections: quote table names and column names in `WHERE` clauses #4508 - -# 1.24.6 (24 May 2023) - -### New Features - -- Defer: Add `incremental: true` for new proposed wire format, add example for working with GraphQL-Batch #4477 - -# 1.24.5 (24 May 2023) - -### Bug Fix - -- Stable relation connection: Quote table names and column names in selects and orders #4485 - -# 1.24.4 (18 April 2023) - -### Bug Fix - -- `@defer`: update `context[:current_path]` usage to fix `path:` on deferred errors - -# 1.24.3 (14 April 2023) - -### Bug Fix - -- `OperationStore`: fix when used with Changesets (or other ways of defining arguments with the same name) #4440 - -# 1.24.2 (20 Mar 2023) - -### Bug Fix - -- Remove debug output, oops - -# 1.24.1 (20 Mar 2023) - -### Bug Fix - -- Fix `OperationStore` with new module-based execution traces (#4389) - -# 1.24.0 (10 Feb 2023) - -### New Features - -- Support the `redis-client` gem as `redis:` - -# 1.23.9 (2 Feb 2023) - -### Bug Fix - -- Dashboard: Support Ruby 3.2.0 - -# 1.23.8 (27 Jan 2023) - -### New Features - -- OperationStore: Support `Changeset-Version` header for syncing with changesets #4304 - -# 1.23.7 (25 Jan 2023) - -### Bug Fix - -- Stable Relation Connections: Fix handling of Postgres JSON accesses - -# 1.23.6 - -### New Features - -- Subscriptions: accept `connection_pool:` instead of `redis:` for use with the `connection_pool` gem - -### Bug Fix - -- Stable connections: rescue `ActiveRecord::StatementInvalid` when loading nodes and return a client-facing error instead - -# 1.23.5 (29 December 2022) - -### New Features - -- Ably subscriptions: Also listen for `presence.leave` webhooks to clean up subscriptions more quickly - -# 1.23.4 (20 December 2022) - -### Bug Fix - -- Dashboard: nicely render subscriptions that are not found or cleaned up by `read_subscription_failed_error` - -# 1.23.3 (19 December 2022) - -### New Features - -- Add `GraphQL::Pro::Subscriptions#read_subscription_failed_error` for handling errors that are raised when reloading queries from storage - -# 1.23.2 (18 October 2022) - -### New Features - -- Add dashboard component for Enterprise mutation limiter - -# 1.23.1 (25 August 2022) - -### Bug Fix - -- Redis: update redis usage to be forward-compatible with redis 5.x #4167 - -# 1.23.0 (2 August 2022) - -### New Features - -- Stable connections: support SQL queries that sort by `IS NOT NULL` #4153 - -# 1.22.3 (26 July 2022) - -### Bug Fix - -- Stable connections: handle `edges {...}` when an invalid cursor is given #4148 - -# 1.22.2 (20 April 2022) - -### Bug Fix - -- Use `deprecated_accepts_definitions` to stop warnings when loading this gem on 1.13.x - -# 1.22.1 (22 March 2022) - -### Bug Fix - -- Pusher subscriptions: don't try to send empty trigger batches to Pusher - -# 1.22.0 (19 March 2022) - -### New Features - -- Pusher subscriptions: it now sends updates in groups of 10 by default, pass `use ..., batch_size: 1` to revert to the previous behavior. -- OperationStore: when using ActiveRecord for storage, it now batches updates to `last_used_at` every 5 seconds. Pass `use ..., update_last_used_at_every: 0` to update that column synchronously, instead, as before. - -# 1.21.6 (16 March 2022) - -### Bug Fix - -- OperationStore: Fix no method error in Redis pipeline usage - -# 1.21.5 (7 March 2022) - -### Bug Fix - -- Postgres stable connection: support more complex aliased selects #3976 - -# 1.21.4 (15 February 2022) - -### Bug Fix - -- Encoders: don't extend `DeprecatedDefine` if it's not present (graphql-ruby < 1.12) - -# 1.21.3 (9 February 2022) - -### New Features - -- Future-proof for GraphQL-Ruby 2.0 - -# 1.21.2 (27 January 2022) - -### New Features - -- Dashboard, Routes: support lazy-loading the schema with `Routes::Lazy` #3868 -- OperationStore: Update deprecated usage of `@redis.pipelined` to address warning - -# 1.21.1 (20 January 2022) - -### Bug Fix - -- Stream, Defer: Include `hasNext: true|false` in patches - -# 1.21.0 (20 January 2022) - -### New Features - -- Stream: Add `@stream` directive for evaluating list items one-at-a-time - -# 1.20.4 (4 December 2021) - -### Bug Fix - -- Stable connections: Fix using startCursor / endCursor without nodes #3752 - -# 1.20.3 (27 November 2021) - -### Bug Fix - -- Stable Connections: Properly handle cursors containing invalid JSON #3735 - -# 1.20.2 (15 November 2021) - -### New Features - -- Operation Store sync: ActiveRecord backend performance improvements: when syncing operations, only validate newly-added operations, reduce allocations when normalizing incoming query strings - -# 1.20.1 (8 November 2021) - -### Bug Fix - -- Operation Store sync: fix when operations are re-synced with new aliases - -# 1.20.0 (5 November 2021) - -### New Features - -- Operation Store: Use Rails `insert_all` for better performance when adding new operations - -# 1.19.2 (26 October 2021) - -### New Features - -- Pundit and CanCan integrations: Add `ResolverIntegration` modules for plain resolvers #3392 - -### Bug Fix - -- OperationStore Redis backend: pipeline updates to last_used_at values #3672 - -# 1.19.1 (15 October 2021) - -### Bug Fix - -- OperationStore: fix a stack overflow error on GraphQL 1.9 #3653 - -# 1.19.0 (13 October 2021) - -### New Features - -- Dashboard: add a component for GraphQL-Enterprise rate limiters -# 1.18.3 (1 Sept 2021) - -### Breaking Changes - -- Stable cursors: raise an error on unrecognized orderings instead of ignoring them #3605 - -### Bug Fix - -- Stable cursors: Handle `Arel::Attributes::Attribute` and `Arel::SqlLiteral` #3605 - -# 1.18.2 (16 August 2021) - -### Bug Fix - -- Stable connections: nicely handle incoming cursors with too many sort values #3581 - -# 1.18.1 (20 July 2021) - -### Bug Fix - -- Stable connections: improve handling of `SELECT` with `CASE` #3558 -- Defer: fix to support runtime wrapper objects in graphql-ruby 1.12.13 - -# 1.18.0 (31 May 2021) - -### New Features - -- Ably subscriptions: send `quickAck: true` for a faster response from Ably - -### Bug Fix - -- Defer: support dataloader inside deferred blocks - -# 1.17.15 (29 Apr 2021) - -### New Features - -- Defer: support `label:` argument which is returned in the patch for that deferral #3454 - -# 1.17.14 (14 Apr 2021) - -- Dashboard: fix stack error when OperationStore isn't configured on a class-based schema - -# 1.17.13 (14 Apr 2021) - -- Stable Connections: When using aliases and GROUP BY, replace the alias when building a HAVING condition. -- Pundit integration: Add a `use_owner_role(true)` configuration option - -# 1.17.12 (3 Apr 2021) - -### Bug Fix - -- Stable Connections: Re-select aliased fields that are referenced by ORDER BY. #3421 - -# 1.17.11 (12 Mar 2021) - -### Bug Fix - -- Pundit integration: properly halt when `unauthorized_by_pundit` returns errors-as-data after a mutation argument fails authorization #3384 - -# 1.17.10 (11 Mar 2021) - -### Bug Fix - -- Pundit, CanCan integrations: properly call configured auth hooks for arguments that are lists and input objects - -# 1.17.9 (3 Mar 2021) - -### Bug Fix - -- Fix OperationStore assignment on GraphQL-Ruby 1.9 - -# 1.17.8 (23 Feb 2021) - -### New Features - -- Subscriptions: change the default `cleanup_delay_s:` to 5 seconds (use `cleanup_delay_s: 0` to get the old behavior) - -### Bug Fix - -- Subscriptions: Handle unsubscribe race condition #3357 - -# 1.17.7 (19 Feb 2021) - -### New Features - -- CanCan integration: support `can_can_subject:` config for overriding the use of `object` as the CanCan subject #3350 - -### Bug Fixes - -- Subscriptions: Support `Redis::Namespace` without deprecation warnings for `script load` #3347 - -# 1.17.6 (18 Feb 2021) - -### New Features - -- Stable connections: implement `range_add_edge` to leverage GraphQL-Ruby 1.12.5's improved RangeAdd #2184 - -### Bug Fix - -- Defer: Update to work with Dataloader - -# 1.17.5 (13 Feb 2021) - -### Bug Fix - -- Subscriptions: Use `MULTI` instead of Lua for some operations -- Subscriptions: Use `EVAL_SHA` for duplicate scripts to reduce network overhead #3285 -- Subscriptions: Don't use `redis.call`, which is unsupported in the `redis-namespace` gem #3322 - -# 1.17.4 (4 Feb 2021) - -## Bug Fix - -- Stable Relation Connection: Don't emit `OR ... IS NULL` for columns that are known to be `null: false` (this improves index utilization) - -## 1.17.3 (2 Feb 2021) - -### New Features - -- Pusher subscriptions: `context[:compress_pusher_payload] = true` will cause the payload to be gzipped before being sent to Pusher - -## 1.17.2 (30 Jan 2021) - -### Bug Fix - -- Subscriptions: don't generate keys inside Lua scripts (for redis-namespace compatibility, and probably better support for Redis cluster) #3307 - -## 1.17.1 (25 Jan 2021) - -### New Features - -- OperationStore: add `OperationStore::AddOperationBatch.call` for adding data directly -- Subscriptions: use Lua scripts for more efficient Redis access - -## 1.17.0 (20 Jan 2021) - -### New Features - -- Updates for 1.12.0 compatibility - -### Bug Fix - -- OperationStore: improve performance by batching reads and writes during updates - -## 1.16.2 (21 Dec 2020) - -### New Features - -- Subscriptions: Add `stale_ttl_s:` and `cleanup_delay_s:` to customize persistence in Redis #3252 - -## 1.16.1 (3 Dec 2020) - -### Bug Fix - -- Fix duplicate calls to `Argument#authorized?` in CanCan and Pundit integrations #3242 - -## 1.16.0 (10 Nov 2020) - -### New Features - -- Ably Subscriptions: `cipher_base:` sets up end-to-end encryption - -## 1.15.7 (29 Sept 2020) - -### Bug Fix - -- Encoder: fix Ruby 2.7 warning #3161 -- Stable connections: Handle `ARRAY[...]` selections and cursors on Postgres #3166 -- Pundit: properly lookup policies for list inputs #3146 - -## 1.15.6 (17 Sept 2020) - -### Bug Fix - -- Stable Connections: Use method access to get `.cursor_#{idx}` values instead of `.attributes[:cursor_#{idx}]`, fixes #3149 - -## 1.15.5 - -### New Features - -- Stable Connections: use `.to_sql` to handle orderings that use complex Arel expressions (#3109) - -## 1.15.4 (28 July 2020) - -### New Features - -- Pundit: add `pundit_policy_class_for(object, context)` and `pundit_role_for(object, context)` for custom runtime lookups - -## 1.15.3 (17 July 2020) - -### Bug Fix - -- Subscriptions: don't send empty updates when subscriptions return `:no_update` - -## 1.15.2 (16 July 2020) - -### New Features - -- OperationStore: improve handling of archived operations in index views - -## 1.15.1 (16 July 2020) - -(Oops, bad release!) - -## 1.15.0 (15 July 2020) - -- OperationStore: Store & display `last_used_at` for operation store clients and operations. To upgrade, add the column to your ActiveRecord table: - - ```ruby - add_column :graphql_client_operations, :last_used_at, :datetime - ``` - - (It works out-of-the-box with the Redis backend.) - - You can opt out of this feature by adding `use GraphQL::Pro::OperationStore, ... default_touch_last_used_at: false` to your schema setup. - -- OperationStore: Add archive/unarchive workflow for operations. To upgrade, add the column to your table: - - ```ruby - add_column :graphql_client_operations, :is_archived, :boolean, index: true - ``` - - (It works out-of-the-box with the Redis backend.) - -- OperationStore: Fix indexing of enum values - -## 1.14.1 (29 June 2020) - -- CanCan: Accept `can_can_attribute:` configuration, which is passed as the third input to `.can?(...)` - -## 1.14.0 (13 June 2020) - -### New Features - -- Add PubnubSubscriptions -- Update subscription implementations to support `broadcast: true` when available - -### Bug Fix - -- More Ruby 2.7 warning fixes - -## 1.13.6 (8 June 2020) - -### Bug Fix - -- Return the proper `pageInfo` values when it's requested before `edges` or `nodes` (#2972) - -## 1.13.5 (11 May 2020) - -### Bug Fix - -- Fix some warnings on Ruby 2.7 - -## 1.13.4 (17 Apr 2020) - -### Bug Fix - -- StableRelationConnection: properly return `hasNextPage: true` when `before` and `max_page_size` are used. - -## 1.13.3 (2 Apr 2020) - -### New Features - -- `GraphQL::Pro::OperationStore::Migration` can be used to copy persisted operations from one backend to another (eg, ActiveRecord to Redis). See the source file, `lib/graphql/pro/operation_store/migration.rb` for docs. - -## 1.13.2 (28 Mar 2020) - -### Deprecations - -- `GraphQL::Pro::Subscriptions` is deprecated; use `GraphQL::Pro::PusherSubscriptions` instead which works the same, but better (see below). This new name avoids confusion with the later-added `AblySubscriptions`. - -### New Features - -- `GraphQL::Pro::PusherSubscriptions` replaces `GraphQL::Pro::Subscriptions` and adds orphaned record cleanup. (No more dangling records in Redis.) - -## 1.13.1 (12 Mar 2020) - -- Use `nonce: true` when working with cursors in new stable connections - -## 1.13.0 (10 Feb 2020) - -### New Features - -- OperationStore supports a `redis:` backend -- OperationStore supports an arbitrary `backend_class:` for persistence operations - -### Bug Fix - -- Use a loop when clearing Redis subscription state to avoid large stack traces #2701 -- Handle empty subscription keys when publishing updates #2061 - -## 1.12.2 (22 Jan 2020) - -### Bug Fix - -- Improve backwards compat with OperationStore (Improve adding `.tracer`, use `.graphql_name` when indexing) - -## 1.12.1 (20 Jan 2020) - -### Bug Fix - -- Fix OperationStore on class-based schemas with query instrumenters that use the query string - -## 1.12.0 (20 Jan 2020) - -### Deprecations - -- `GraphQL::Pro::Monitoring` is deprecated; see Tracing for a replacement: https://graphql-ruby.org/queries/tracing.html -- `GraphQL::Pro::Repository` is deprecated; see OperationStore for a replacement: https://graphql-ruby.org/operation_store/overview.html - -### New Features - -- New stable connection support based on GraphQL-Ruby 1.10's new pagination implementation. New classes provide better handling of `NULL` values in order-by columns and they can be applied on a field-by-field basis(`GraphQL::Pro::SqliteStableRelationConnection`, `GraphQL::Pro::MySQLStableRelationConnection`, `GraphQL::Pro::PostgresStableRelationConnection`). - -### Bug Fix - -- Add the Access query analyzer to class-based schemas - -## 1.11.0 (10 Oct 2019) - -### New Features - -- Forwards-compatibility for graphql 1.10.0 -- Support 1.10.0.pre1's input object argument `loads:` authorization - -## 1.10.8 (8 Oct 2019) - -### Bug Fix - -- Continue authorizing input object arguments -- Use millisecond-aware string format for datetimes in cursors - -## 1.10.7 (22 Jul 2019) - -### Bug Fix - -- Support multiple subscriptions in one document - -## 1.10.6 (27 Jun 2019) - -### New Features - -- Support custom `#can_can_ability` methods on query context for CanCanIntegration -- Support custom `#pundit_user` method on query context for PunditIntegration - -### Bug Fix - -- Fix off-by-one error when paginating backwards from the last item in a stable relation connection - -## 1.10.5 (11 May 2019) - -### New Features - -- Include expected HMAC digest in OperationStore debug output - -## 1.10.4 (26 Mar 2019) - -### Bug Fix - -- Include content-length and content-type headers in OperationStore JSON responses - -## 1.10.3 (13 Mar 2019) - -### Bug Fix - -- Support stable connections ordered by Arel SQL literals - -## 1.10.2 (11 Mar 2019) - -### Bug Fix - -- Support stable connections on realized views (which don't have primary keys) - -## 1.10.1 (8 Mar 2019) - -### Bug Fix - -- Pundit integration: support `pundit_policy_class` String names when scoping connections - -## 1.10.0 (5 Mar 2019) - -### New Features - -- Add `GraphQL::Pro::Defer`, implementing `@defer` for streaming responses - -## 1.9.13 (4 Mar 2019) - -### Bug Fix - -- Pundit integration: correctly authorize fields when Query root is nil - -## 1.9.12 (22 Feb 2019) - -### Bug Fix - -- Pundit integration: use overridden `pundit_policy_class` for scoping and mutation authorization - -## 1.9.11 (20 Feb 2019) - -### Bug Fix - -- Pundit integration: Fields use the owner's configured `pundit_policy_class` if there is one -- Pundit integration: avoid conflicts with `#initialize` for schema classes that don't need it - -## 1.9.10 (19 Feb 2019) - -### Bug Fix - -- Support inheritance with `pundit_policy_class(...)` - -## 1.9.9 (13 Feb 2019) - -### New Features - -- Support `pundit_policy_class(...)` and `pundit_policy_class:` to manually specify a class or class name. - -## 1.9.8 (30 Jan 2019) - -### New Features - -- Inject `context` into policy lookup hooks instead of just the user - -## 1.9.7 (30 Jan 2019) - -### New Features - -- Extract `pundit_policy` and `scope_by_pundit_policy` hooks for user override - -## 1.9.6 (18 Jan 2019) - -### Bug Fix - -- Properly render subscription context in dashboard - -## 1.9.5 (14 Jan 2019) - -## Bug Fix - -- Don't pass arrays to Pundit scopes (fixes https://github.com/rmosolgo/graphql-ruby/issues/2008) - -## 1.9.4 (11 Jan 2019) - -## Bug Fix - -- Prepare for future compat with graphql-ruby 1.9 - -## 1.9.3 (3 Dec 2018) - -### Bug Fix - -- Include table name when adding a default order-by-id to ActiveRecord Relations -- Raise if a required cursor attribute is missing -- Improve `rake routes` output for operation store endpoint -- Support already-parsed queries in subscription RedisStorage - -## 1.9.2 (2 Nov 2018) - -### Bug Fix - -- Derp, remove the dummy app's `.log` files from the gem bundle -- Fix ordering bug when a SQL function call doesn't have an explicit order - -## 1.9.1 (1 Nov 2018) - -### Bug Fix - -- Fix Pusher reference in AblySubscriptions - -## 1.9.0 (27 Oct 2018) - -### New Features - -- Add `GraphQL::Pro::AblySubscriptions` for GraphQL subscriptions over Ably.io transport - -## 1.8.2 (22 Oct 2018) - -### Bug Fix - -- Support `NULLS LAST` in stable cursors - -## 1.8.1 (16 Oct 2018) - -### Bug Fix - -- Improve operation store models to work when `config.active_record.primary_key_prefix_type` is set - -## 1.8.0 (11 Oct 2018) - -### New Features - -- Support Rails 3.2 with OperationStore -- Use `.select` to filter items in CanCanIntegration - -### Bug Fix - -- Properly send an _ability_ and the configured `can_can_action` to `.accessible_by` -- Use a string (not integer) for `Content-Length` header in the dashboard - -## 1.7.13 (2 Oct 2018) - -### Breaking Change - -- `PunditIntegration`: instead of raising `MutationAuthorizationFailed` when an argument fails authorization, it will send a `GraphQL::UnauthorizedError` to your `Schema.unauthorized_object` hook. (This is what all other authorization failures do.) To retain the previous behavior, in your base mutation, add: - - ```ruby - def unauthorized_by_pundit(owner, value) - # Raise a runtime error to halt query execution - raise "#{value} failed #{owner}'s auth check" - end - ``` - - Otherwise, customize the handling of this behavior with `Schema.unauthorized_object`. - -### Bug Fix - -- Auth: mutation arguments which have authorization constraints but _don't_ load an object from the database will have _mutation instance_ passed to the auth check, not the input value. - -## 1.7.12 (29 Aug 2018) - -### New Features - -- Add `GraphQL::Pro::CanCanIntegration` which leverages GraphQL-Ruby's built-in auth - -## 1.7.11 (21 Aug 2018) - -### Bug Fix - -- `PunditIntegration`: Don't try to authorize loaded objects when they're `nil` - -## 1.7.10 (10 Aug 2018) - -### New Features - -- Update `PunditIntegration` for arguments, unions, interfaces and mutations - -## 1.7.9 (9 Aug 2018) - -### New Features - -- Add a new `PunditIntegration` which leverages the built-in authorization methods - -## 1.7.8 (10 July 2018) - -### Bug Fix - -- Authorization: fix scoping lists of abstract type when there's no `#scope` method on the strategy - -## 1.7.7 (10 May 2018) - -### Bug Fix - -- Fix ordering of authorization field instrumenter (put it at the end, not the beginning of the list) - -## 1.7.6 (2 May 2018) - -### New Features - -- Authorization: Add `view`/`access`/`authorize` methods to `GraphQL::Schema::Mutation` - -## 1.7.5 (19 Apr 2018) - -### New Features - -- Authorization: when a `fallback:` configuration is given, apply it to each field which doesn't have a configuration of its own or from its return type. _Don't_ apply that configuration at schema level (it's applied to each otherwise uncovered field instead). - -## 1.7.4 (16 Apr 2018) - -### New Features - -- Support Mongoid::Criteria in authorization scoping - -## 1.7.3 (12 Apr 2018) - -### Bug Fix - -- Fix authorization code for when `ActiveRecord` is not defined - -## 1.7.2 (10 Apr 2018) - -### Bug Fix - -- Use a more permissive regexp (`/^\s*((?:[a-z._]+)\(.*\))\s*(asc|desc)?\s*$/im`) to parse SQL functions - -## 1.7.1 (4 Apr 2018) - -### Bug Fix - -- Fix route helpers to support class-based schemas - -## 1.7.0 (25 Mar 2018) - -### New Features - -- Support `1.8-pre` versions of GraphQL-Ruby - -### Bug Fix - -- Fix OperationStore when other query instrumenters need `.query_string` - -## 1.6.5 (7 Feb 2018) - -### Bug Fix - -- Support `LEAST(...)` in stable cursors - -## 1.6.4 (7 Feb 2018) - -### Bug Fix - -- Support `CASE ... END` in stable cursors - -## 1.6.3 (26 Jan 2018) - -### Bug Fix - -- Support `FIELD(...)` in stable cursors - -## 1.6.2 (13 Jan 2018) - -### Bug Fix - -- Improve detection of `OperationStore` for the dashboard -- Serve `Content-Type` and `Content-Length` headers with dashboard pages -- Better `Dashboard#inspect` for Rails routes output -- Use a string to apply order-by-primary-key for better Rails 3 support - -## 1.6.1 (22 Nov 2017) - -### New Features - -- Support `composite_primary_keys` gem - -## 1.6.0 (13 Nov 2017) - -### Breaking Changes - -- `GraphQL::Pro::UI` renamed to `GraphQL::Pro::Dashboard` - -### Deprecations - -- Routing method `.ui` was renamed to `.dashboard` - -### New Features - -- Added `GraphQL::Pro::Subscriptions` -- Added subscriptions component to Dashboard - -## 1.5.9 (10 Oct 2017) - -### Bug Fix - -- Don't crash when scoping lists of abstract types with Pundit - -## 1.5.8 (2 Oct 2017) - -### New Features - -- Use `authorize(:pundit, namespace: )` to lookup policies in a namespace instead of the global namespace. - -### Bug Fix - -- Introspection data is allowed through `fallback:` `authorize:` and `access:` filters. (It can be hidden with a `view:` filter.) - -## 1.5.7 (20 Sept 2017) - -### Bug Fix - -- Properly return `nil` when a list of authorized objects returns `nil` - -## 1.5.6 (19 Sept 2017) - -### New Features - -- Add `authorization(..., operation_store:)` option for authorizing operation store requests - -## 1.5.5 (18 Sept 2017) - -### New Features - -- Support `ConnectionType.bidrectional_pagination?` in stable RelationConnection - -## 1.5.4 (18 Sept 2017) - -### Bug Fix - -- Fix load issue when Rails is not present - -## 1.5.3 (4 Sept 2017) - -### Bug Fix - -- Fix OperationStore views on PostgresQL -- Fix stable cursors when joined tables have the same column names - - __Note:__ This is implemented by adding extra fields to the `SELECT` - clause with aliases like `cursor_#{idx}`, so you'll notice this in your - SQL logs. - -## 1.5.2 (4 Aug 2017) - -### Bug Fix - -- Bump `graphql` dependency to `1.6` - -## 1.5.1 (2 Aug 2017) - -### New Features - -- Routing extensions moved to `using GraphQL::Pro::Routes` - -### Deprecations - -- Deprecate `using GraphQL::Pro`, move extensions to `GraphQL::Pro::Routes` - -## 1.5.0 (31 Jul 2017) - -### New Features - -- Add `GraphQL::Pro::OperationStore` for persisted queries with Rails - -## 1.4.8 (14 Jul 2017) - -### Bug Fix - -- Update `authorization` to use type-level `resolve_type` hooks - -## 1.4.7 (13 Jul 2017) - -### Bug Fix - -- Update authorization instrumentation for `graphql >= 1.6.5` - -## 1.4.6 (6 Jul 2017) - -### Bug Fix - -- Fix typo in RelationConnection source - -## 1.4.5 (6 Jul 2017) - -### Bug Fix - -- Correctly fall back to offset-based cursors with `before:` argument - -## 1.4.4 (15 Jun 2017) - -### New Features - -- Add `Schema#unauthorized_object(obj, ctx)` hook for failed runtime checks - -### Bug Fix - -- Prevent usage of `parent_role:` with `view:` or `access:` (since parent role requires a runtime check) -- Fix versioned, encrypted cursors with 16-byte legacy cursors - -## 1.4.3 (13 Jun 2017) - -### New Features - -- `OrderedRelationConnection` supports ordering by joined fields - -### Bug Fix - -- Update auth plugin for new Relay instrumenters -- `Pro::Encoder` supports `encoder(...)` as documented - -## 1.4.2 (2 May 2017) - -### Bug Fix - -- Fix compatibility of `RelationConnection` and `RangeAdd` helper - -## 1.4.1 (19 Apr 2017) - -### New Features - -- Add `:datadog` monitoring - -## 1.4.0 (19 Apr 2017) - -### New Features - -- `ActiveRecord::Relation`s can be scoped by Pundit `Scope`s, CanCan `accessible_by`, or custom strategy's `#scope(gate, relation)` methods -- Default authorization configuration can be provided with `authorization(..., fallback: { ... })` -- Authorization's `:current_user` key can be customized with `authorization(..., current_user: ...)` - -## 1.3.0 (7 Mar 2017) - -### New Features - -- Serve static, persisted queries with `GraphQL::Pro::Repository` - -## 1.2.3 (2 May 2017) - -### Bug Fix - -- Fix compatibility of `RelationConnection` and `RangeAdd` helper - -## 1.2.2 (6 Mar 2017) - -### Bug Fix - -- Raise `GraphQL::Pro::RelationConnection::InvalidRelationError` when a grouped, unordered relation is returned from a field. (This relation can't be stably paginated.) - -## 1.2.1 (3 Mar 2017) - -### New Features - -- Formally support ActiveRecord `>= 4.1.0` - -### Bug Fix - -- Support grouped relations in `GraphQL::Pro::RelationConnection` - -## 1.2.0 (1 Mar 2017) - -### New Features - -- Authorize fields based on their parent object, for example: - - ```ruby - AccountType = GraphQL::ObjectType.define do - name "Account" - # This field is visible to all users: - field :name, types.String - # This is only visible when the current user is an `:owner` - # of this account - field :account_balance, types.Int, authorize: { parent_role: :owner } - end - ``` - -## 1.1.1 (22 Feb 2017) - -### Bug Fixes - -- Fix monitoring when `Query#selected_operation` is nil - -## 1.1.0 (9 Feb 2017) - -### New Features - -- Add AppSignal monitoring platform -- Add type- and field-level opting in and opting out of monitoring -- Add `monitor_scalars: false` to skip monitoring on scalars - -### Bug Fixes - -- Fix `OrderedRelationConnection` when neither `first` nor `last` are provided (use `max_page_size` or don't limit) - -## 1.0.4 (23 Jan 2017) - -### Bug Fixes - -- `OrderedRelationConnection` exposes more metadata methods: `parent`, `field`, `arguments`, `max_page_size`, `first`, `after`, `last`, `before` - -## 1.0.3 (23 Jan 2017) - -### Bug Fixes - -- When an authorization check fails on a non-null field, propagate the null and add a response to the errors key (as if the field had returned null). It previously leaked the internal symbol `__graphql_pro_access_not_allowed__`. -- Apply a custom Pundit policy even when the value isn't `nil`. (It previously fell back to `Pundit.policy`, skipping a `pundit_policy_name` configuration.) - -## 1.0.2 - -### Bug Fixes - -- `OrderedRelationConnection` exposes the underlying relation as `#nodes` (like `RelationConnection` does), supporting custom connection fields. - -## 1.0.1 - -### New Features - -- CanCan integration now supports a custom `Ability` class with the `ability_class:` option: - - ```ruby - authorize :cancan, ability_class: CustomAbility - ``` - -## 1.0.0 - -- `GraphQL::Pro` released diff --git a/vendor/gems/graphql/CHANGELOG-relay.md b/vendor/gems/graphql/CHANGELOG-relay.md deleted file mode 100644 index f22006fba43..00000000000 --- a/vendor/gems/graphql/CHANGELOG-relay.md +++ /dev/null @@ -1,176 +0,0 @@ -# graphql-relay - -### Breaking Changes - -### Deprecations - -### New Features - -### Bug Fix - -## 0.12.0 (21 Jul 2016) - -### Breaking Changes - -- Don't cache a global node identification config #51 - - To migrate, assign your node identification helper to the schema: - - ```ruby - NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define { ... } - MySchema.node_identification = NodeIdentification - ``` - -### New Features - -- Support lazy definition blocks from graphql-ruby 0.17 -- Add `startCursor` and `endCursor` to `PageInfo` #60 - -### Bug Fix - -- Support `field:` keyword for connection helper #58 - -## 0.11.2 (6 Jul 2016) - -### New Features - -- Include description for built-in objects #55 - -## 0.11.1 (24 Jun 2016) - -### Bug Fix - -- Correctly pass parent object to Connections #53 - -## 0.11.0 (19 Jun 2016) - -### Breaking Changes - -- `BaseType.define_connection` no longer caches the result to use as the default `BaseType.connection_type`. Now, store the result of `.define_connection` in a variable and pass that variable into the schema: - - ```ruby - # Capture the returned type: - SomethingCustomConnectionType = SomethingType.define_connection { ... } - - DifferentThingType = GraphQL::ObjectType.define do - # And pass it to the connection helper: - connection :somethings, SomethingCustomConnectionType - end - ``` - -### New Features - -- Support for custom edge types / classes #50 -- Support for multiple connection classes #50 - -## 0.10.0 (31 May 2016) - -### New Feature - -- Support `graphql` 0.14.0 #47 - -### Bug Fix - -- Use strings as argument names, not symbols #47 - -## 0.9.5 - -### New Feature - -- Root `id` field may have a description #43 - -## 0.9.4 (29 Apr 2016) - -### Bug Fix - -- Fix Node interface to support GraphQL 0.13.0+ - -## 0.9.2 (29 Apr 2016) - -### Bug Fix - -- Fix Node interface when type_from_object returns nil - -## 0.9.1 (6 Apr 2016) - -### Bug Fix - -- Respond to connection fields without any pagination arguments -- Limit by `max_page_size` even when no arguments are present - -## 0.9.0 (30 Mar 2016) - -### Breaking change - -- Remove the `order` argument from connection fields. This isn't part of the spec and shouldn't have been there in the first place! - - You can implement this behavior with a custom argument, for example: - - ```ruby - field :cities, CityType.connection_type do - argument :order, types.String, default_value: "name" - resolve ->(obj, args, ctx) { - obj.order(args[:order]) - } - end - ``` - -### Bug Fix - -- Include the MIT license in the project's source - -## 0.8.1 (22 Mar 2016) - -### Bug Fix - -- Accept description for Mutations - -## 0.8.0 (20 Mar 2016) - -### New Feature - -- Accept configs for `to_global_id` and `from_global_id` -- Support `graphql` 0.12+ - -## 0.7.1 (29 Feb 2016) - -### Bug Fix - -- Limit the `count(*)` when testing next page with ActiveRecord #28 - -## 0.7.0 (20 Feb 2016) - -### New Feature - -- `max_page_size` option for connections -- Support ActiveSupport 5.0.0.beta2 - -## 0.6.2 (11 Feb 2016) - -### Bug Fix - -- Correctly cast values from connection cursors #21 -- Use class _name_ instead of class _object_ when finding a connection implementation (to support Rails autoloading) #16 - -## 0.6.1 (14 Dec 2015) - -### Bug Fix - -- Stringify `id` when passed into `to_global_id` - -## 0.6.0 (11 Dec 2015) - -### Breaking Change - -- `GlobalNodeIdentification#object_from_id(id, ctx)` now accepts context as the second argument #9 - -## 0.5.1 (11 Dec 2015) - - -### Feature - -- Allow custom UUID join string #15 - -### Bug Fix - -- Remove implicit ActiveSupport dependency #14 diff --git a/vendor/gems/graphql/CHANGELOG.md b/vendor/gems/graphql/CHANGELOG.md deleted file mode 100644 index d17189e0f5c..00000000000 --- a/vendor/gems/graphql/CHANGELOG.md +++ /dev/null @@ -1,4646 +0,0 @@ -# Changelog - -[Versioning guidelines](https://graphql-ruby.org/development.html#versioning) - -### Breaking changes - -### Deprecations - -### New features - -### Bug fixes - -# 2.4.11 (28 Feb 2025) - -### New features - -- `InvalidNullError`: Improve default handling to add path and locations #5257 -- `DetailedTrace`: Add a sampling profiler for creating detailed traces #5244 - -### Bug fixes - -- `Enum`: Make value methods optional; Add `value_methods(true)` to your base enum class to opt back in. #5255 -- `InvalidNullError`: use `GraphQL::Error` as a base class #5248 -- CI: test on Mongoid 8 and 9 #5251 - -# 2.4.10 (18 Feb 2025) - -### New features - -- Dataloader: improve built-in Rails integration #5213 - -### Bug fixes - -- `NewRelicTrace`: don't double-count time waiting on Dataloader fibers -- Fix possible type memberships inherited from superclass #5236 -- `Visibility`: properly use configured contexts for visibility profiles #5235 -- `Enum`: reduce needless `value_method` warnings #5230 #5220 -- `Backtrace`: fix error handling with `rescue_from` #5227 -- Parser: return a proper error when variable type is missing #5225 - -# 2.4.9 (29 Jan 2025) - -### New features - -- Enum: Enum types now have methods to access GraphQL-ready values directly #5206 #5218 - -### Bug fixes - -- Validation: fix order dependency and mutual exclusion bug in `required: { one_of: [ ... ] }` -- Backtrace: simplify trace setup and rendering code -- Fix dependencies for Ruby 3.4 #5199 -- Resolver: inherit description from superclass #5195 -- Visibility: fix for when multiple implementations are all hidden #5191 - -# 2.4.8 (10 Dec 2024) - -### New features - -- Subscriptions: support calling `write_subscription` within `resolve` #5142 - -### Bug fixes - -- Autoloading: improve autoloading of `Tracing` classes #5190 - -# 2.4.7 (7 Dec 2024) - -### Bug fixes - -- Remove warning when code isn't eager-loaded #5187 -- Add missing `require "ostruct"` in ActionCableSubscriptions #5184 - -# 2.4.6 (5 Dec 2024) - -### Bug fixes - -- Autoloading: fix referencing built-in types #5181 -- Autoloading: use Rails `config.before_eager_load` hook for better integration #5182 -- `loads:`: Check possible types for `loads:`-only unions #5180 - -# 2.4.5 (2 Dec 2024) - -### Breaking changes - -- In non-Rails production environments, GraphQL-Ruby will emit a warning about calling `.eager_load!` for better boot performance. #5178 - -### New features - -- Loading: GraphQL-Ruby now uses Ruby's `autoload ...` for many constants. #5178 -- Input objects may be pattern matched (they implement `#deconstruct_keys`) #5170 - -### Bug fixes - -- Visibility: hide definition directives in SDL #5175 -- Internals: use `Fiber[...]` for internal state instead of `Thread.current` #5176 -- Dataloader: properly handle arrays of all falsey values #5167 #5169 -- Visibility: hide directives when their uses are all hidden #5163 -- Require object types to have fields and require input objects to have arguments (to comply with the GraphQL spec) #5137 -- Improve error message when a misplaced `-` is encountered #5115 - -# 2.4.4 (18 Nov 2024) - -- Visibility: improve performance with `sync` #5161 - -# 2.4.3 (11 Nov 2024) - -### Bug fixes - -- Lookahead: return an empty hash for `.arguments` when they raised a `GraphQL::ExecutionError` #5155 -- Visibility: fix error when Mutation is lazy-loaded #5158 -- Visibility: improve performance of `Schema.types` #5157 - -# 2.4.2 (7 Nov 2024) - -### Bug fixes - -- Validation: fix error message when selections are made on an enum #5144 #5145 -- Visibility: fix preloading when no profiles are named #5148 - -# 2.4.1 (4 Nov 2024) - -### Bug fixes - -- Visibility: support dynamically-generated `#enum_values` #5141 - -# 2.4.0 (31 Oct 2024) - -### Deprecations - -- Visibility: Implementing `visible?` now requires `use GraphQL::Schema::Visibility` or `use GraphQL::Schema::Warden` in your schema definition #5123 - -### New features - -- Validation: Add "did you mean" to error messages when `DidYouMean` is available #4966 -- Schema: types can be lazy-loaded when using `GraphQL::Schema::Visibility` #4919 - -# 2.3.20 (31 Oct 2024) - -### Bug fixes - -- Arguments: suppress warning for `objectId` arguments #5124 -- Arguments: don't require input object arguments when a default value is configured - -# 2.3.19 (24 Oct 2024) - -### New features - -- Dataloader: accept a `fiber_limit:` option #5132 - -### Bug fixes - -- Argument Validation: improve the `one_of:` error message #5130 -- Lookahead: return a null lookahead from `Query#lookahead` when no operation is selected #5129 -- Static Validation: speed up FieldsWillMerge when some fields are not defined #5125 - -# 2.3.18 (7 Oct 2024) - -### Bug fixes - -- Properly use trace options when `trace_with` is used after `trace_class` #5118 - -# 2.3.17 (4 Oct 2024) - -### Bug fixes - -- Fix `InvalidNullError#inspect` #5103 -- Add server-side tests for ActionCableSubscriptions #5108 -- RuboCop: Fix FieldTypeInBlock for list types and interface types #5107 #5112 -- Subscriptions: Fix triggering with nested input objects #5117 -- Extensions: fix extensions which add other extensions #5116 - - -# 2.3.16 (12 Sept 2024) - -### Bug fixes - -- RuboCop: fix `FieldTypeInBlock` for single-line classes #5098 -- Testing: Add `context[:current_field]` to testing helpers #5096 - -# 2.3.15 (10 Sept 2024) - -### New features - -- Type definitions accept `comment("...")` for annotating SDL #5067 -- Parser: add `tokens_count` method #5066 -- Schema: allow `validate_timeout` to be reset #5062 - -### Bug fixes - -- Optimize `Language.escape_single_quoted_newlines` #5095 -- Generators: Add `# frozen_string_literal: true` to base resolver #5092 -- Parser: Properly handle minus followed by name #5090 -- Migrate some attr_reader methods #5080 -- Handle variable definition directives #5072 -- Handle `GraphQL::ExecutionError` when loading arguments during analysis #5071 -- NotificationsTrace: properly call `super` -- Use symbols for namespaced_types generator option #5068 -- Reduce memory usage in lazy resolution #5061 -- Fix default trace inheritance #5045 - -# 2.3.14 (13 Aug 2024) - -### Bug fixes - -- Subscriptions: fix subscriptions when subscription type is added after subscription plug-in #5063 - -# 2.3.13 (12 Aug 2024) - -### New features - -- Authorization: Call `EnumValue#authorized?` during execution #5058 -- `Subset`: support lazy-loading root types and field return types (not documented yet) #5055, #5054 - -### Bug fixes - -- Validation: don't validate `nil` if null value is permitted for incoming lists #5048 -- Multiplex: fix `Mutation#ready?` dataloader cache in multiplexes #5059 - -# 2.3.12 (5 Aug 2024) - -### Bug fixes - -- Add `fiber-storage` dependency for Ruby < 3.2 support - -# 2.3.11 (2 Aug 2024) - -### New features - -- `GraphQL::Current` offers globally-available methods for runtime metadata #5034 -- Continue improving `Schema::Subset` (not production-ready yet, though) #5018 #5039 - -### Bug fixes - -- Fix `Node#line` and `Node#col` when nodes are created by manually #5047 -- Remove unused `interpreter?`, `using_ast_analysis?` and `new_connections?` flag methods #5039 -- Clean up `.compare_by_identity` usages #5037 - -# 2.3.10 (19 Jul 2024) - -### Bug fixes - -- Parser: fix parsing operation names that match keywords #5033 -- Parser: support leading pipes in Union type definitions #5027 -- Validation: remove rule that prohibits non-null variables from having default values #5030 -- Dataloader: raise fresh error instances when sources return errors #5021 -- Enum and Union: don't create nested error classes in anonymous classes (eg, when parsing SDL -- to improve bug tracker integration) #5022 - -# 2.3.9 (13 Jul 2024) - -### Bug fixes - -- Subscriptions: fix `subscriptionType` in introspection #5019 - -# 2.3.8 (12 Jul 2024) - -### New features - -- Input validation: Add `all: { ... }` validator #5013 -- Visibility: Add `Query#types` for future type filtering improvements #4998 -- Broadcast: Add `default_broadcast(true)` option for Connection and Edge types #5012 - -### Bug fixes - -- Remove unused `InvalidTypeError` #5003 -- Parser: remove unused `previous_token` and `Token` #5015 - -# 2.3.7 (27 Jun 2024) - -### Bug fixes - -- Properly merge field directives and resolver directives #5001 - -# 2.3.6 (25 Jun 2024) - -### New features - -- Analysis classes are now in `GraphQL::Analysis` (`GraphQL::Analysis::AST` still works, too) #4996 -- Resolvers and Mutations accept `directive ...` configurations #4995 - -### Bug fixes - -- `AsyncDataloader`: Copy Fiber-local variables into Async tasks #4994 -- `Dataloader`: properly batch `fetch` calls with `loads:` arguments that call Dataloader sources during `.authorized?` #4997 - -# 2.3.5 (13 Jun 2024) - -### Breaking changes - -- Remove default `load_*` implementations in arguments -- this could break calls to `super` if you have redefined this method in subclasses #4978 -- `Schema.possible_types` and `Schema.references_to` now use type classes as keys instead of type names (Strings). You can create a new Hash with the old structure using `.transform_keys(&:graphql_name)`. #4986 #4971 - -### Bug fixes - -- Enums: fix parsing enum values that match GraphQL keywords (eg `type`, `extend`) #4987 -- Consolidate runtime state #4969 -- Simplify schema type indexes #4971 #4986 -- Remove duplicate when clause #4976 -- Address many Ruby warnings #4978 -- Remove needless `ruby2_keywords` usage #4989 -- Fix some YARD docs #4984 - -# 2.3.4 (21 May 2024) - -### New features - -- Async Dataloader: document integration with Rails database connections #4944 #4964 - -### Bug fixes - -- `Query#fingerprint`: handle `nil` query strings like `""` #4963 -- `Language::Nodes`: support marshalling parsed ASTs #4959 -- Directives: fix directives in nested fragment spreads #4958 -- Tracing: fix conflicts between Sentry and Prometheus traces #4957 - -# 2.3.3 (9 May 2024) - -### New features - -- Max Complexity: add `count_introspection:` option #4939 - -### Bug fixes - -- Language: Fix regression in `Nodes#line` and `Nodes#col` #4949 -- Runtime: Simplify runtime state management #4935 - -# 2.3.2 (26 Apr 2024) - -### Bug fixes - -- Properly `.prepare` lists of input objects #4933 -- Fix deleting directives using the AST visitor #4931 - -# 2.3.1 (22 Apr 2024) - -### New features - -- `Schema.max_query_string_tokens`: support a limit on the number of tokens the lexer should identify #4929 -- Parser: add an option to reject numbers followed immediately by argument names #4924 -- Parser and CParser: reduce allocated and retained strings when parsing schemas #4899 -- `run_graphql_field`: support `:lookahead` and `:ast_node` field extras #4930 - -### Bug fixes - -- Rescue when trying to print integers that are too big for Ruby #4923 -- Mutation: clear the Dataloader cache before resolving #4903 -- Fix `FieldUsage` analyzer when InputObjects return a prepared value #4902 -- Add a minimal query string for `run_graphql_field` #4891 -- Fix PrometheusTrace with multiple tracers #4888 - -# 2.3.0 (20 Mar 2024) - -### Breaking Changes - -- `orphan_types`: Only object types are accepted here; other types may be added to the schema through `extra_types` instead. #4869 -- Parser: line terminators are no longer allowed in single-quoted strings (as per the GraphQL spec). Escape newline characters instead; see `GraphQL::Language.escape_single_quoted_newline(query_str)` if you need to transform incoming query strings #4834 - -### Deprecations - -- `.tracer(...)` is deprecated, use `.trace_with(...)` instead, using trace modules (https://graphql-ruby.org/queries/tracing.html) #4878 - -### Bug fixes - -- Parser: handle some escaped character edge cases according to the GraphQL spec #4824 -- Analyzers: fix fragment skip/include tracking #4865 -- Remove unused Context modules #4876 - -# 2.2.14 (18 Mar 2024) - -### Bug fixes - -- Parser: properly handle stray hyphens in query strings #4879 - -# 2.2.13 (11 Mar 2024) - -### Bug fixes - -- Tracing: when a new base `:default` trace class is added, merge already-configured trace modules into it #4875 - -# 2.2.12 (6 Mar 2024) - -### Deprecations - -- `Schema.{query|mutation|subscription}_execution_strategy` methods are deprecated without replacement #4867 - -### Breaking Changes - -- Connections: Revert changes to `hasNextPage` returning `false` when no `first` is given (previously changed in 2.2.6) #4866 - -### Bug fixes - -- Complexity: handle unauthorized argument errors better #4868 -- Pass `context` when fetching argument for `loads: ...` #4870 - -# 2.2.11 (27 Feb 2024) - -### New features - -- Sentry: support transaction names in tracing #4853 - -### Bug fixes - -- Tracing: handle unknown trace modes at runtime #4856 - -# 2.2.10 (20 Feb 2024) - -### New features - -- Parser: support directives on variable definitions #4847 - -### Bug fixes - -- Fix compatibility with Ruby 3.4 #4846 -- Tracing: Fix applying default options to non-default modes #4849, #4850 - -# 2.2.9 (15 Feb 2024) - -### New features - -- Complexity: Treat custom Connection fields as metadata (like `totalCount`), not as if they were evaluated for each item in the list #4842 -- Subscriptions: Serialize `ActiveRecord::Relation`s given to `.trigger` #4840 - -### Bug fixes - -- Complexity: apply configured `complexity ...` to connection fields #4841 -- Authorization: properly handle Resolver arguments that return `false` for `#authorized?` #4839 - -# 2.2.8 (7 Feb 2024) - -### New features - -- Responses have `"errors"` before `"data"`, as recommended by the GraphQL spec #4823 - -### Bug fixes - -- Sentry: fix integration with other trace modules #4830 -- Sentry: fix when child span is `nil` (test environments) #4828 -- Remove needless Base64 backport #4820 -- Fix module arrangement to support RDoc #4819 - -# 2.2.7 (29 Jan 2024) - -### Deprecations - -- Deprecate returning `.resolve` dataloader requests (use `.load` instead) #4807 -- Deprecate `error_bubbling(true)`, no replacement. Please open an issue if you need this option. #4813 - -### Bug fixes - -- Remove unused `racc` dependency #4814 -- Fix `backtrace: true` when used with `@defer` and batch-loaded lists #4815 -- Accept input objects when required arguments aren't provided but have default values #4811 - -# 2.2.6 (25 Jan 2024) - -### Deprecations - -- `instrument(:query | :multiplex, ...)` was deprecated, use a `trace_with` module instead. #4771 -- Legacy `PlatformTracing` classes are deprecated, use a `PlatformTrace` module instead #4779 - -### New features - -- `FieldUsage` analyzer: returns a `used_deprecated_enum_values: ...` array in its result Hash #4805 -- `validate_timeout` applies to query analysis as well as static validation #4800 -- `SentryTrace` is added for instrumenting with Sentry #4775 - -### Bug fixes - -- `FieldUsage` analyzer: properly find deprecated arguments in non-null input objects #4805 -- DataDog: replace usage of `span_type` setter with `span` setter #4776 -- Fix coercion error handing with given `null` values #4799 -- Raise a better error when variables are defined with non-input types #4791 -- Fix `hasNextPage` when `max_page_size` is set #4780 - -# 2.2.5 (10 Jan 2024) - -### Bug fixes - -- Parser: fix enum values named `type` #4772 -- GraphQL::Deprecation: remove this unused helper module #4769 - -# 2.2.4 (3 Jan 2024) - -### Bug fixes - -- AsyncDataloader: don't resolve fields with event loop #4757 -- Parser: properly parse some fields and args named after keywords #4759 -- Performance: use `all?` to check classes directly #4760 - -# 2.2.3 (28 Dec 2023) - -### Bug fixes - -- AsyncDataloader: avoid leftover `suspended` Fibers #4754 -- Generators: fix path and constant name of BaseResolver #4755 - -# 2.2.2 (27 Dec 2023) - -### Bug fixes - -- Dataloader: remove `Fiber#transfer` support because Ruby's control flow is unpredictable (#4748, #4752, #4743) -- Parser: fix handling of single-token document -- QueryComplexity: improve performance - -# 2.2.1 (20 Dec 2023) - -### Bug fixes - -- `AsyncDataloader`: re-raise errors from fields and sources #4736 -- Parser: fix parsing directives on interfaces in SDL #4738 - -# 2.2.0 (18 Dec 2023) - -### Breaking changes - -- `loads:` now requires a schema's `self.resolve_type` method to be implemented so that loaded objects can be verified to be of the expected type #4678 -- Tracing: the new Ruby-based parser doesn't emit a "lex" event. (`graphql/c_parser` still does.) - -### New features - -- `GraphQL::Dataloader::AsyncDataloader`: a Dataloader class that uses the `async` gem to run I/O from fields and Dataloader sources in parallel #4727 -- Parser: use a heavily-optimized lexer and a hand-written parser for better performance #4718 -- `run_graphql_field`: a helper method for running fields in tests #4732 - -# 2.1.10 (27 Dec 2023) - -- Dataloader: remove Fiber#transfer support because of unpredictable Ruby control flow #4753 - -# 2.1.9 (21 Dec 2023) - -### Bug fixes - -- Dataloader: fix some fiber scheduling bugs #4744 - -# 2.1.8 (18 Dec 2023) - -### New features - -- Rails generators: generate a base resolver class by default #4513 -- Dataloader: add some support for transfer-based Fiber schedulers, simplify algorithm #4625 #4729 -- `prepare`: check for the named method on the argument owner, too #4717 - -# 2.1.7 (4 Dec 2023) - -### New features - -- Make `NullContext` inherit from `Context`, to make typechecking easier #4709 -- Accept a custom `Schema.query_class` to use for executing queries #4679 - -### Bug fixes - -- Default `reauthorize_scoped_objects` to false #4720 -- Fix `subscriptions.trigger` with custom enum values #4713 -- Fix `backtrace: true` with GraphQL-Pro `@defer` #4708 -- Omit `to_h` from Input Object validation error message #4701 -- When trimming whitespace from block strings, remove first and last lines that contain only whitespace #4704 - -# 2.1.6 (2 Nov 2023) - -### Breaking Changes - -- The parser cache is now opt-in. Add `config.graphql.parser_cache = true` to your Rails environment setup to enable it. #4648 - -### New features - -- New `ISO8601Duration` scalar #4688 - -### Bug fixes - -- Trace: fix custom trace mode inheritance #4693 - -# 2.1.5 (25 Oct 2023) - -### Bug fixes - -- Logger: Fix `Schema.default_logger` when Rails is present but doesn't have a logger #4686 - -# 2.1.4 (24 Oct 2023) - -### New features - -- Add `Query#logger` #4674 -- Lookahead: Add `with_alias:` option #2912 -- Improve support for `load_application_object_failed` #4667 - -### Bug fixes - -- Execution: Fix runtime loop in some cases with fragments #4684 -- Fix `Connection#initialize` outside of execution #4675 -- Fix ParseError in `Subscriptions#trigger` #4673 -- Mongo: don't load all records in hasNextPage #4671 -- Interfaces: fix `definition_methods` when interfaces implement other interfaces #4670 -- Migrate `NullContext` to use the built-in Singleton module #4669 -- Speed up type lookup #4664 -- Fix `ScopeExtension#after_resolve` outside of execution #4685 -- Speed up `one_of?` checks #4680 - -# 2.1.3 (12 Oct 2023) - -### Bug fixes - -- Tracing: fix legacy tracers added to `GraphQL::Schema` #4663 -- Add `racc` as a dependency because it's not included by default in Ruby 3.3 #4661 -- Connections: don't add automatic connection behaviors for types named "Connection" #4668 - -# 2.1.2 (11 Oct 2023) - -### New features - -- Depth: accept `count_introspection_fields: false` #4658 -- Dataloader: add `get_fiber_variables` and `set_fiber_variables` #4593 -- Trace: Add `Schema.default_trace_mode` #4642 - -### Bug fixes - -- Fix merging results after calling directives #4639 #4660 -- Visibility: don't reveal implementers of hidden abstract types #4589 -- Bump required Ruby version to 2.7 since numbered block arguments are used #4659 -- `hash_key:`: use the configured hash key when the underlying Hash has a default value Proc #4656 - -# 2.1.1 (2 Oct 2023) - -### New features - -- Mutations: `HasSingleInput` provides Relay Classic-like `input: ...` argument behavior #4581 -- Add `@specifiedBy` default directive #4633 -- Analysis: support `visit?` hook to skip visit but still return a value -- Add `context.scoped` for a long-lived reference to the current scoped context #4605 - -### Bug fixes - -- Sanitized printer: Correctly print enum variable defaults #4652 -- Schema printer: use `extend schema` when the schema has directives #4647 -- Performance: pass runtime state through interpreter code #4621 -- Performance: add `StaticVisitor` for faster AST visits #4645 -- Performance: faster field lookup #4626 -- Improve generator templates #4627 -- Dataloader: clear cache between root mutation fields #4617 -- Performance: Improve argument checks #4622 -- Remove unused legacy connection code #4606 - -# 2.1.0 (30 Aug 2023) - -### Breaking changes - -- Visitor: legacy-style proc-based visitors are no longer supported #4577 #4583 -- Deprecated `GraphQL::Filter` is removed #4325 -- Language::Printer has been re-written to append to a buffer; custom printers will need to be updated #4394 - -### New features - -- Authorization: Items in a list can skip object-level `.authorized?` checks if the type is configured with `reauthorize_scoped_objects(false)` #3994 -- Subscriptions: `unsubscribe(...)` accepts a value to be used to return a result along with unsubscribing #4283 -- Language::Printer is much faster #4394 - -# 2.0.27 (30 Aug 2023) - -### New features - -- Validators: Support `%{value}` in custom messages #4601 - -### Bug fixes - -- Resolvers: Support `return false, nil` from `ready?` and `authorized?` #4585 -- Enums: properly load directives from Schema IDL #4596 -- Language: faster scanner #4576 -- Language: support fields and arguments named `"null"` #4586 -- Language: fix block string quote unescaping #4580 -- Generator: use generated node type in Relay-related fields #4598 - -# 2.0.26 (8 Aug 2023) - -### Bug fixes - -- Datadog Tracing: fix LocalJumpError #4579 - -# 2.0.25 (7 Aug 2023) - -### New features - -- Tracing: add trace modes #4571 -- Dataloader: add `Source#result_key_for` for customizing cache keys in sources #4569 - -### Bug fixes - -- Tracing: Support multiple tracing platforms at once #4543 - -# 2.0.24 (27 Jun 2023) - -### New features - -- `Schema::Object.wrap` can be used to customize how objects are (or aren't) wrapped by `GraphQL::Schema::Object` instances at runtime #4524 -- `Query`: accept a `static_validator:` option in `#initialize` to use instead of the default validation configuration. - -### Bug fixes - -- Performance: Reduce memory usage when adding types to a schema #4533 -- Performance, `Dataloader`: when loading specific keys, only run dataloader until those specific keys are resolved #4519 - -# 2.0.23 (19 Jun 2023) - -### New features - -- Printer: print extensions in SDL #4516 -- Trace: accept trace instances during query execution #4497 -- AlwaysVisible: Make a way to bypass type visibility #4442, #4491 - -### Bug fixes - -- Tests: fix assertion for Ruby 3.3.0-dev #4515 -- Performance: improve fragment possible type lookup #4506 -- Docs: document Timeout can handle floats #4505 -- Performance: use a dedicated object for field extension state #4401 -- Backtrace: fix `backtrace: true` with other trace modules #4505 -- Handle `context.warden` being nil #4503 -- Dev: disable Minitest::Reporters for RubyMin #4494 -- Trace: fix compatibility with inheritance #4487 -- Context: fix NullContext compatibility with fetch, dig and key? #4483 - -# 2.0.22 (17 May 2023) - -### New features - -- Warden: manually instantiating doesn't require a `filter` instance #4462 - -### Bug fixes - -- Enum: fix procs for enum values #4474 -- Lexer: force UTF-8 encoding #4467 -- Trace: inherit superclass `trace_options` #4470 -- Dataloader: properly run mutations in sequence #4461 -- NotificationsTrace: Add `execute_multiplex.graphql` event #4460 -- Fix `Context#dig` when called with one key #4458 -- Performance: Use a plain hash for selection sets at runtime #4453 -- Performance: Memoize current trace #4450, #4452 -- Performance: Pass is_non_null to runtime check #4449 -- Performance: Use `compare_by_identity` on some runtime caches -- Properly support nested queries (fix `Thread.current` clash) #4445 - -# 2.0.21 (11 April 2023) - -### Deprecations - -- Deprecate `GraphQL::Filter` (use `visible?` methods instead) #4424 - -### New features - -- PrometheusTracing: support histograms #4418 - -### Bug fixes - -- Backtrace: improve compatibility with `trace_with` #4437 -- Consolidate internally-used empty value constants #4434 -- Fix some warnings #4422 -- Performance: improve runtime speed #4436 #4433 #4428 #4430 #4427 #4399 -- Validation: fix inline fragment selection on scalar #4429 -- `@oneOf`: print definition in the SDL when it's used -- SDL: load schema directives when they're used -- Appsignal tracing: Fix `resolve_type` definition - -# 2.0.20 (30 March 2023) - -### Bug fixes - -- `.resolve_type`: fix returning `[Type, false]` from resolve_type #4412 -- Parsing: improve usage of `GraphQL.default_parser` #4411 -- AppsignalTrace: implement missing methods #4390 -- Runtime: Fix `current_depth` method in some lazy lists #4386 -- Performance: improve `Object` object shape #4365 -- Tracing: return execution errors raised from field resolution to `execute_field` hooks #4398 - -# 2.0.19 (14 March 2023) - -### Bug fixes - -- Scoped context: fix `context.scoped_context.current_path` #4376 -- Tracing: fix `tracer` inheritance in Schema classes #4379 -- Timeout: fix `Timeout` plugin when other tracers are used #4383 -- Performance: use Arrays instead of `GraphQL::Language::Token`s when scanning #4366 - -# 2.0.18 (9 March 2023) - -### Breaking Changes - -- Tracing: `"execute_field"` events on fields defined on interface types will now receive the _interface_ type as `data[:owner]` instead of the current object type. To get the old behavior, use `data[:object].class` instead. #4292 - -### New features - -- Add `TypeKind#leaf?` #4352 - -### Bug fixes - -- Tracing: use the interface type as `data[:owner]` instead of the object type #4292 -- Performance: improve Shape compatibility of `GraphQL::Schema::Field` #4360 -- Performance: improve Shape compatibility of `GraphQL::Schema::Warden` #4361 -- Performance: rewrite the token scanner in plain Ruby #4369 -- Performance: make `deprecation_reason` faster #4356 -- Performance: improve lazy value resolution in execution #4333 -- Performance: create `current_path` only when the application needs it #4342 -- Performance: add `GraphQL::Tracing::Trace` as a lower-overhead tracing API #4344 -- Connections: fix `hasNextPage` for already-loaded ActiveRecord Relations #4349 - - -# 2.0.17.2 (29 March 2023) - -### Bug fixes - -- Unions and Interfaces: support returning `[type_module, false]` from `resolve_type` #4413 - -# 2.0.17.1 (27 March 2023) - -### Bug fixes - -- Tracing: restore behavior returning execution errors raised during field resolution #4402 - -# 2.0.17 (14 February 2023) - -### Breaking changes - -- Enums: require at least one value in a definition #4278 - -### New features - -- Enums: support `nil` as a Ruby value #4311 - -### Bug fixes - -- Don't re-encode ASCII strings as UTF-8 #4319, #4343 -- Fix `handle_or_reraise` with arguments validation #4341 -- Performance: Remove error handling from `Lazy#value` (unused) #4335 -- Performance: Use codegen instead of dynamic dispatch in `Language::Visitor` and `Analysis::AST::Visitor` #4338 -- Performance: reduce indirection in `#introspection?` and `#graphql_name` #4327 -- Clean up thread-based state after running queries #4329 -- JSON types: don't pass raw NullValue AST nodes to `coerce_input` #4324, #4320 -- Performance: reduce `.is_a?` calls at runtime #4318 -- Performance: cache interface type memberships #4311 -- Performance: eagerly define some type instance variables for Shape friendliness #4300 #4295 #4297 -- Performance: reduce argument overhead, don't scope introspection by default, reduce duplicate call to Field#type #4317 -- Fix anonymous `eval` usage #4288 -- Authorization: fix field auth fail call after lazy #4289 -- Subscriptions: fix `loads:`/`as:` - -# 2.0.16 (19 December 2022) - -### Breaking changes - -- `Union`: Only accept Object types in `possible_types` (previously, other types were also accepted, but this was against the spec) #4269 - -### New features - -- Rake: support introspection query options in the `RakeTask` #4247 -- Subscriptions: Merge `.trigger(... context: { ... })` into the query context when running updates #4242 - -### Bug fixes - -- Make BaseEdge and subclasses return true for `.default_relay?` #4272 -- Validation: return a proper error for duplicate-named fragments when used indirectly #4268 -- Don't re-apply `scope_items` to `nodes { ... }` or `edges { ... }` arrays #4263 -- Fix `Concurrent::Map` initialization to prevent race conditions -- Speed up scoped context lookup #4245 -- Support overriding built-in context keys #4239 -- Context: properly `dig` into `:current_arguments` #4249 - -# 2.0.15 (22 October 2022) - -### New features - -- SDL: support extensions on the schema itself #4203 -- SDL: recognize `.graphqls` files in `.from_definition` #4204 -- Schema: add a reader method of `TypeMembership#options` #4209 - -### Bug fixes - -- Node Behaviors: call the id-from-object hook with the type definition, not the type instance #4233 -- RelayClassicMutation: add a period to the generated description of the payload type #4229 -- Dataloader: make scoped context work with Dataloader #4220 -- SDL: fix parsing repeatable directives #4218 -- Lookahead: reduce more allocations in `.selects?` #4212 -- Introspection Query: strip blank lines from generated query strings #4208 -- Enums: Add error handling to result coercion #4206 -- Lookahead: add `selected_type:` to `.selects?` #4194 -- Lookahead: fix `.selects?` on unions #4193 -- Fields: use field-local `connection:` config over resolver config #4191 - -# 2.0.14 (8 September 2022) - -### New features - -- Input Objects: support `one_of` for input objects that allow exactly one argument #4184 -- Dataloader: add `source.merge({ ... })` for adding objects to dataloader source caches #4186 -- Validation: generate new schemas with a suggested `validate_max_errors` of 100 #4179 - -### Bug fixes - -- Lookahead: improve performance when field names are given as symbols #4189 -- Runtime: simplify some internal code #4183 -- Datadog tracing: remove deprecated options #4159 - -# 2.0.13 (12 August 2022) - -### New features - -- Fields: add configuration methods for `default_value` and `prepare` #4156 -- Static validation: merge directive errors when they're on the same location or directive - -### Bug fixes - -- Subscriptions: properly use the given `.trigger(... context: )` for determining subscription root field visibility #4160 -- Fix fields that use `hash_key:` and have a falsy value for that key #4132 -- Variable validation: respect `validate_max_error` limit -- Performance: use `Array#+` to add objects during execution #4142 - -# 2.0.12 (19 July 2022) - -### New features - -- Support returning `[Type, nil]` from `resolve_type` #4130 - -### Bug fixes - -- SDL: Don't print empty braces for input objects with no arguments #4138 -- Arguments: always call `prepare` before loading objects based on ID (`loads:`) #4128 -- Don't support re-assigning `Query#validate=` after validation has run #4127 - -# 2.0.11 (20 June 2022) - -### New features - -- Support full unicode range #4090 - -### Bug fixes - -- Subscriptions: support overriding subscriptions in subclasses #4108 -- Schema: support types with duplicate names and cyclical references #4107 -- Connections: don't exceed application-applied `LIMIT` with `max_page_size` #4104 -- Field: add `Field#relay_nodes_field` config reader #4103 -- Remove partial `opentelementry` implementation, oops #4086 -- Remove unused method `Lazy.resolve` - -# 2.0.10 (20 June 2022) - -Oops, this version was accidentally released to RubyGems as "2.10.0". I yanked it. See 2.0.11 instead. - -# 2.0.9 (31 May 2022) - -### New features - -- Connections: use `Schema.default_page_size`, `Field#default_page_size`, or `Resolver.default_page_size` when one of them is available and no `first` or `last` is given #4081 -- Tracing: Add `OpenTelementryTracing` #4077 - -### Bug fixes - -- Field usage analyzer: don't crash on null input objects #4078 -- Complexity: properly handle `ExecutionError`s raised in `prepare:` hooks #4079 - -# 2.0.8 (24 May 2022) - -### New Features - -- Fields: return `fallback_value:` when method or hash key field resolution fails #4069 -- Support `hash_key:` lookups on Hash-like objects #4072 -- Datadog tracing: support `prepare_span` hook for adding custom tags #4067 - -### Bug fixes - -- Fields: When `hash_key:` is given, populate `#method_str` based on it #4072 -- Errors: rescue errors raised when calling `.each` on list values #4052 -- Date type: continue accepting dates without hyphens #4061 -- Parser: properly parse empty type definitions #4046 - -# 2.0.7 (25 April 2022) - -### New Features - -- Subscriptions: support `validate_update: false` to disable validation when running subscription updates #4039 -- Expose duplicated name on `DuplicateNamesError` #4022 - -### Bug Fixes - -- Datadog: improve tracer #4038 -- `hash_key:` try stringified hash key when resolving fields (this restores previous behavior) #4043 -- Printer: Don't print empty field set when types have no fields (`{\n}`) #4042 -- Dataloader: improve handoff between lazy resolution and dataloader resolution #4036 -- Remove unused `Lazy::Resolve` module from legacy execution code #4035 - -# 2.0.6 (14 April 2022) - -### Bug fixes - -- Dataloader: make multiplexes use custom dataloaders #4026 -- ISO8601Date: properly accept `nil` as input #4025 -- Mutation: fix error message when `ready?` returns an invalid result #4029 -- ISO8601 scalars: add `specified_by_url` configs #4014 -- Array connection: don't return all items when `before` is the first cursor #4012 -- Introspection: fix typo `specifiedByUrl` -> `specifiedByURL` -- Fields: fix `hash_key` to take priority over method lookup #4015 - -# 2.0.5 (28 March 2022) - -### Bug Fixes - -- Resolvers: fix inheriting arguments when parent classes aren't hooked up directly to the schema #4006 - -# 2.0.4 (21 March 2022) - -### Bug fixes - -- Fields: make sure `null:` config overrides a default from a resolver #4000 - -# 2.0.3 (21 March 2022) - -### Bug fixes - -- Fields: make sure field configs override resolver defaults #3975 -- Fix `Field#scoped?` when the field uses a resolver #3990 -- Allow schema members to have multiple of `repeatable` directives #3986 -- Remove some legacy code #3979 #9995 -- SDL: fix indirect interface implementation when loading a schema #3982 -- Datadog tracing: Support ddtrace 1.0 #3978 -- Fix `Node` implementation when connection types include built-in behavior modules #3967 -- Small stack trace size reduction #3957 - -# 2.0.2 (1 March 2022) - -### New features - -- Reduce schema memory footprint #3959 - -### Bug fixes - -- Mutation: Correctly use a configured `type(...)` #3965 -- Interfaces: De-duplicate indirectly implemented interfaces #3932 -- Remove an unnecessary require #3961 - -# 2.0.1 (21 February 2022) - -### Breaking changes - -- Resolvers: refactored so that, instead of _copying_ configurations to `field ...` instances, `GraphQL::Schema::Field`s reference their provided `resolver: ...`, `mutation: ...`, or `subscription: ...` classes for many properties. This _shouldn't_ break anything -- all of graphql-ruby's own tests passed just fine -- but it's mentioned here in case you notice anything out-of-sorts in your own application #3916 -- Remove deprecated field options `field:`, `function:`, and `resolve:` (these were already no-ops, but they were overlooked in 2.0.0) #3917 - -### Bug fixes - -- Scoped context: fix usage with dataloader #3950 -- Subscriptions: support multiple definitions for subscription root fields with `.trigger` #3897 #3935 -- Improve some error messages #3920 #3923 -- Clean up scalar validation code #3982 - -# 2.0.0 (9 February 2022) - -### Breaking Changes - -- __None, ideally.__ If you have an application that ran without warnings on v1.13, you should be able to update to 2.0.0 without a hitch. If this isn't the case, please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?template=bug_report.md&title=[2.0%20update]%20describe%20your%20problem) and let me know what happened! I plan to maintain 1.13 for a while in order to ensure a smooth transition. -- But, many legacy code components were removed, so if there are any more references to those, there will be name errors! See #3729 for a list of removed components. - -# 1.13.19 (2 February 2023) - -### Bug fixes - -- Performance: don't re-encode schema member names #4323 -- Performance: fix a duplicate field.type call #4316 -- Performance: use `scope: false` for introspection types #4315 -- Performance: improve argument coercion and validation #4312 -- Performance: improve interface type membership lookup #4309 - -# 1.13.18 (10 January 2023) - -### New Features - -- `hash_key:`: perform `[...]` lookups even when the underlying object isn't a Hash #4286 - -# 1.13.17 (17 November 2022) - -### Bug fixes - -- Handle ExecutionErrors from prepare hooks when calculating complexity #4248 - -# 1.13.16 (31 August 2022) - -### New Features - -- Make variable validation respect `validate_max_errors` #4178 - -# 1.13.15 (30 June 2022) - -### Bug fixes - -- Remove partial OpenTelementry tracing #4086 -- Properly use `Query#validate` to skip static validation #3881 - -# 1.13.14 (20 June 2022) - -### New Features - -- Add `Field#relay_nodes_field` reader #4103 -- Datadog: detect tracing module #4100 - -# 1.13.13 (31 May 2022) - -### New features - -- Datadog: update tracer for ddtrace 1.0 #4038 -- Datadog: Add `#prepare_span` hook for custom tags #4067 -- Tracing: Add `OpenTelementry` tracing #4077 - -# 1.13.12 (14 April 2022) - -- Pass `context[:dataloader]` to multiplex context #4026 -- Add a deprecation warning to `.accepts_definitions` #4002 - -# 1.13.11 (21 March 2022) - -### Deprecations - -- `RangeAdd` warns when `context:` isn't provided (it's required in GraphQL-Ruby 2.0) #3996 - -# 1.13.10 - -### Breaking changes - -- `id` fields: #3914 Previously, when a field was created with `global_id_field`, it would pass a _legacy-style_ type definition (an instance of `GraphQL::ObjectType`) to `Schema.id_from_object(...)`. Now, it passes a class-based definition instead. If your `id_from_object(...)` method was using any methods from those legacy definitions, they should be migrated. (Most notably, uses of `type.name` should be migrated to `type.graphql_name`.) - -### Deprecations - -- Connections: deprecation warnings were added to configuration methods `.bidirectional_pagination = ...` and `.default_nodes_field = ...`. These two configurations don't apply to the new pagination implementation, so they can be removed. #3918 - -# 1.13.9 (9 February 2022) - -### Breaking changes - -- Authorization: #3903 In graphql-ruby v1.12.17-1.13.8, when input objects used `prepare: -> { ... }` , the returned values were not authorized at all. However, this release goes back to the behavior from 1.12.16 and before, where a returned `Hash` is validated just like an input object that didn't have a `prepare:` hook. To get the previous behavior, you can implement `def self.authorized?` in the input object you want to skip authorization in: - - ```ruby - class Types::BaseInputObject < GraphQL::Schema::InputObject - def self.authorized?(obj, value, ctx) - if value.is_a?(self) - super - else - true # graphql-ruby skipped auth in this case for v1.12.17-v1.13.8 - end - end - end - ``` - -### Bug fixes - -- Support re-setting `query.validate = ...` after a query is initialized #3881 -- Handle validation errors in connection complexity calculations #3906 -- Input Objects: try to authorize values when `prepare:` returns a Hash (this was default < v1.12.16) #3903 -- SDL: fix when a type has two directives - -# 1.13.8 (1 February 2022) - -### Bug fixes - -- Introspection query: hide newly-supported fields behind arguments, maintain backwards-compatible INTROSPECTION_QUERY #3877 - -# 1.13.7 (28 January 2022) - -### New Features - -- Arguments: `replace_null_with_default: true` replaces incoming `null`s with the configured `default_value:` #3871 -- Arguments: support `dig: [key1, key2, ...]` for nested hash key access #3856 -- Generators: support more Postgresql field types #3577 -- Generators: support downcased generator argument types #3577 -- Generators: add an input type generator #3577 -- Generators: support namespaces in generators #3577 - -### Bug Fixes - -- Field: better error for nil `owner` #3870 -- ISO8601DateTime: don't accept inputs with partial time parts #3862 -- SDL: fix for base connection classes that implement interfaces #3859 -- Cops: find `required: true` on `f.argument` calls (with explicit receiver) #3858 -- Analysis: handle undefined or hidden fields with `nil` in `visitor.field_definition` #3857 - -# 1.13.6 (20 January 2022) - -### New features - -- Introspection: support `__Schema.description`, `__Directive.isRepeatable`, `__Type.specifiedByUrl`, and `__DirectiveLocation.VARIABLE_DEFINITION` #3854 -- Directives: Call `Directive.resolve_each` for list items #3853 -- Dataloader: Run each list item in its own fiber (to support batching across list items) #3841 - -### Bug fixes - -- RelationConnection: Preserve `OFFSET` when it's already set on the relation #3846 -- `Types::ISO8601Date`: Accept default values as Ruby date objects #3563 - -# 1.13.5 (13 January 2022) - -### New features - -- Directives: support `repeatable` directives #3837 -- Tracing: use `context[:fallback_transaction_name]` when operations aren't named #3778 - -### Bug fixes - -- Performance: improve performance of queries with directives #3835 -- Fix crash on undefined constant `NodeField` #3832 -- Fix crash on partially-required `ActiveSupport` #3829 - -# 1.13.4 (7 January 2022) - -### Bug fixes - -- Connections: Fix regression in 1.13.3 on unbounded Relation connections #3822 - -# 1.13.3 (6 January 2022) - -### Deprecations - -- `GraphQL::Relay::NodeField` and `GraphQL::Relay::NodesField` are deprecated; use `GraphQL::Types::Relay::HasNodesField` or `GraphQL::Types::Relay::HasNodeField` instead. (The underlying field instances require a reference to their owner type, but `NodeField` and `NodesField` can't do that, since they're shared instances) #3791 - -### New features - -- Arguments: support `required: :nullable` to make an argument required to be _present_, even if it's `null` #3784 -- Connections: When paginating an AR::Relation, use already-loaded results if possible #3790 -- Tracing: Support DRY::Notifications #3776 -- Improve the error when a Ruby method doesn't support the defined GraphQL arguments #3785 -- Input Objects: call `.authorized?` on them at runtime #3786 -- Field extensions: add `extras(...)` for extension-related extras with automatic cleanup #3787 - -### Bug fixes - -- Validation: accept nullable variable types for arguments with default values #3819 -- Validation: raise a better error when a schema receives a `query { ... }` but has no query root #3815 -- Improve the error message when `Schema.get_field` can't make sense of the arguments #3815 -- Subscriptions: losslessly serialize Rails 7 TimeWithZone #3774 -- Field Usage analyzer: handle errors from `prepare:` hooks #3794 -- Schema from definition: fix default values with camelized arguments #3780 - -# 1.13.2 (15 December 2021) - -### Bug fixes - -- Authorization: only authorize arguments _once_, after they've been loaded with `loads:` #3782 -- Execution: always provide an `Interpreter::Arguments` instance as `context[:current_arguments]` #3783 - -# 1.13.1 (13 December 2021) - -### Deprecations - -- `.to_graphql` and `.graphql_definition` are deprecated and will be removed in GraphQL-Ruby 2.0. All features using those legacy definitions are already removed and all behaviors should have been ported to class-based definitions. So, you should be able to remove those calls entirely. Please open an issue if you have trouble with it! #3750 #3765 - -### New features - -- `context.response_extensions[...] = ...` adds key-value pairs to the `"extensions" => {...}` hash in the final response #3770 -- Connections: `node_type` and `edge_type` accept `field_options:` to pass custom options to generated fields #3756 -- Field extensions: Support `default_argument ...` configuration for adding arguments if the field doesn't already have them #3751 - -### Bug fixes - -- fix `rails destroy graphql:install` #3739 -- ActionCable subscriptions: close channel when unsubscribing from server #3737 -- Mutations: call `.authorized?` on arguments from `input_object_class`, `input_type`, too #3738 -- Prevent blank strings with `validates: { length: ... }, allow_blank: false` #3747 -- Lexer: return mutable strings when strings are empty #3741 -- Errors: don't send execution errors to schema-defined handlers from inside lazies #3749 -- Complexity: don't multiple `edges` and `nodes` fields by page size #3758 -- Performance: fix validation performance degradation from 1.12.20 #3762 - -# 1.13.0 (24 November 2021) - -Since this version, GraphQL-Ruby is tested on Ruby 2.4+ and Rails 4+ only. - -### Breaking changes - -- ActionCable Subscriptions: No update is delivered if all subscriptions return `NO_UPDATE` #3713 -- Subscription classes: If a subscription has a `scope ...` configuration, then a `scope:` option is required in `.trigger(...)`. Use `scope ..., optional: true` to get the old behavior. #3692 -- Arguments whose default values are used aren't checked for authorization #3665 -- Complexity: Connection fields have a default complexity implementation based on `first`/`last`/`max_page_size` #3609 -- Arguments: if arguments are configured to return `false` for `.visible?(context)`, their default values won't be applied - -### New features - -- Visibility: A schema may contain multiple members with the same name. For each name, GraphQL-Ruby will use the one that returns true for `.visible?(context)` for each query (and raise an error if multiple objects with the same name are visible). #3651 #3716 #3725 -- Dataloader: `nonblocking: true` will make GraphQL::Dataloader use `Fiber.scheduler` to run fields and load data with sources, supporting non-blocking IO. #3482 -- `null: true` and `required: true` are now default. GraphQL-Ruby includes some RuboCop cops, `GraphQL/DefaultNullTrue` and `GraphQL/DefaultRequiredTrue`, which identify and remove those needless configurations. #3612 -- Interfaces may `implement ...` other interfaces #3613 - -### Bug fixes - -- Enum `value(...)` and Input Object `argument(...)` methods return the defined object #3727 -- When a field returns an array of mixed errors and values, the result will contain `nil` where there were errors in the list #3656 - -# 1.12.24 (4 February 2022) - -### Bug fixes - -- SDL: fix parsing schemas where types have multiple directives #3886 - -# 1.12.23 (20 December 2021) - -### Bug fixes - -- FieldUsage analyzer: handle arguments that raise an error during `prepare:` #3795 - -# 1.12.22 (8 December 2021) - -### Bug fixes - -- Static validation: fix regression and improve performance of fields_will_merge validation #3761 - -# 1.12.21 (23 November 2021) - -### Bug fixes - -- Validators: Fix `format:`/`allow_blank: true` to correctly accept a blank string #3726 -- Generators: generate a correct `Schema.type_error` hook #3722 - -# 1.12.20 (17 November 2021) - -### New Features - -- Static validation: improve error messages when fields won't merge #3698 -- Generators: improve id_from_object and type_error suggested implementations #3710 -- Connections: make the new connections module fall back to old connections #3704 - -### Bug fixes - -- Dataloader: re-enqueue sources when one call to `yield` didn't satisfy their pending requests #3707 -- Subscriptions: Fix when JSON-typed arguments are used #3705 - -# 1.12.19 (5 November 2021) - -### New Features - -- Argument validation: Make `allow_null` and `allow_blank` work standalone #3671 -- Add field and path info to Encoding errors #3697 -- Add `Resolver#unauthorized_object` for handling loaded but unauthorized objects #3689 - -### Bug fixes - -- Properly hook up `Schema.validate_max_errors` at runtime #3691 - -# 1.12.18 (2 November 2021) - -### New features - -- Subscriptions: Add `NO_UPDATE` constant for skipping subscription updates #3664 -- Validation: Add `Schema.validate_max_errors(integer)` for halting validation when it reaches a certain number #3683 -- Call `self.load_...` methods on Input objects for loading arguments #3682 -- Use `import_methods` in Refinements when available #3674 -- `AppsignalTracing`: Add `set_action_name` #3659 - -### Bug fixes - -- Authorize objects returned from custom `def load_...` methods #3682 -- Fix `context[:current_field]` when argument `prepare:` hooks raise an error #3666 -- Raise a helpful error when a Resolver doesn't have a configured `type(...)` #3679 -- Better error message when subscription clients are using ActionCable #3668 -- Dataloader: Fix dataloading of input object arguments #3666 -- Subscriptions: Fix parsing time zones #3667 -- Subscriptions: Fix parsing with non-null arguments #3620 -- Authorization: Call `schema.unauthorized_field` for unauthorized resolvers -- Fix when literal `null` is used as a value for a list argument #3660 - -# 1.12.17 (15 October 2021) - -### New features - -- Support `extras: [:parent]` #3645 -- Support ranges in `NumericalityValidator` #3635 -- Add some Dataloader methods for testing #3335 - -### Bug fixes - -- Support input object arguments called `context` #3654 -- Support single-item default values for list arguments #3652 -- Ensure query strings are strings before running a query #3628 -- Fix empty hash kwargs for Ruby 3 #3610 -- Fix wrongly detecting Ipnut objects in authorization #3606 - -# 1.12.16 (31 August 2021) - -### New features - -- Connections: automatically support Mongoid 7.3 #3599 -- Support `def self.topic_for` in Subscription classes for server-filtered streams #3597 -- When a list item or object field has an invalid null, stop executing that list or - -### Bug fixes - -- Perf: don't refine String when unnecessary #3593 -- BigInt: always parse as base 10 #3586 -- Errors: only return one error when a node in a non-null connection has an invalid null #3601 - -# 1.12.15 (23 August 2021) - -### New Features - -- Subscriptions: add support for multi-tenant setups when deserializing context #3574 -- Analyzers: also track deprecated arguments #3549 - -# 1.12.14 (22 July 2021) - -### Bug fixes - -- SDL: support directive arguments referencing overridden built-in scalars #3564 -- Use `"_"` as the name for `field :_, ...` fields #3560 -- Support `sanitized_printer(...)` in the schema definition for `Query#sanitized_query_string` -- `GraphQL::Backtrace`: fix multiplex support - -# 1.12.13 (20 June 2021) - -### Breaking changes - -- Add a trailing newline to the `Schema.to_definition` output string #3541 - -### Bug fixes - -- Properly handled list results in GraphQL::Backtrace #3540 -- Return plain `Hash`es and `Array`s from queries instead of customized subclasses #3533 -- Fix errors raised from non-null fields #3537 -- Resolver: don't pass frozen array of extensions when none were configured #3515 -- Configure the right `owner` for `node` and `nodes` fields #3509 -- Improve error message for invalid enum value #3507 -- Properly halt on lazily-returned `context.skip`s #3514 -- Fix: call overridden `to_h` methods on InputObject classes #3539 -- Halt execution when a runtime directive argument raises a `GraphQL::ExecutionError` #3542 - -# 1.12.12 (31 May 2021) - -### Bug fixes - -- Directives on inline fragments and fragment spreads receive `.resolve(...)` calls #3499 - -# 1.12.11 (28 May 2021) - -### Bug fixes - -- Validate argument default values when adding them to the schema #3496 -- Resolvers inherit extensions from superclasses #3500 -- Greatly reduce runtime overhead #3494, #3505 -- Remove hidden directives from introspection #3488 - -# 1.12.10 (18 May 2021) - -### New features - -- Use `GlobalID::Locator.locate_many` for arrays of global Ids #3481 -- Support runtime directives (call `.resolve`) on `QUERY` #3474 - -### Bug fixes - -- Don't override Resolver `#load_*` methods when they're inherited #3486 -- Fix validation of runtime directive arguments that have input objects #3485 -- Add a final newline to rake task output -- Don't add connection arguments to fields loaded from introspection responses #3470 -- Fix `rescue_from` on loading arguments #3471 - -# 1.12.9 (7 May 2021) - -### New features - -- Overriding `.authorized_new(...)` to call `.new(...)` effectively skips object authorization #3446 -- Dataloader copies Fiber-local values from `Thread.current[...]` when initializing new Fibers #3461 - -### Bug fixes - -- Fix introspection of default value input objects #3456 -- Add `StandardError => ...` condition to the generated GraphqlController #3460 -- Fix `Dataloader::Source` on Ruby 3 with keyword arguments -- Respect directive visibility at runtime #3450 -- ActionCable subscriptions: only deserialize the broadcast payload once #3443 -- Don't re-add `graphiql-rails` when `generate graphql:install` is run twice #3441 -- Allow differing selections on mutually exclusive interfaces #3063 -- Respect `max_page_size: nil` override in fields #3438 - -# 1.12.8 (12 Apr 2021) - -### Bug fixes - -- Fix loading single-key hashes in Subscriptions #3428 -- Fix looking up `rescue_from` handlers inherited from parent schema classes #3431 - -# 1.12.7 (7 Apr 2021) - -### Breaking changes - -- `Execution::Errors` (which implements `rescue_from`) was refactored so that, when an error matches more than one registered handler, it picks the _most specific_ handler instead of the _first match_ in the underlying Hash. This might "break" your code if your application registered a handler for a parent class and a child class, but expects instances of the child class to be handled by the handler for the parent class. (This seems very unlikely -- I consider the change to be a "breaking fix.") #3404 - -### New features - -- Errors: pick the most specific error handlers (instead of an order-dependent selection) #3404 -- Add `node_nullable(...)` connection configuration options #3389 -- Add `has_nodes_field(true|false)` connection configuration option #3388 -- Store more metadata in argument-related static validation errors #3406 - -### Bug fixes - -- Fix connection nullability settings to properly handle `false` #3386 -- Fix returning `RawValue`s as part of a list #3403 -- Fix introspection for deprecated directive arguments #3416 -- Optimize `has_next_page` for ActiveRecord::Relation connections #3414 -- Tracing: consistent event sequencing when queries are executed with `Query#result` #3408 - -# 1.12.6 (11 March 2021) - -### Breaking changes - -- Static validation: previously, variables passed as arguments to input objects were not properly type-checked. #3370 fixed type checking in this case, but may case existing (invalid) queries to break. - -### New features - -- Connection types: support `edges_nullable(false)` and `edge_nullable(false)` for non-null fields #3376 -- Connections: add `.arguments` reader to new `Pagination::Connection` classes #3360 - -### Bug fixes - -- Relation connection: Remove extra `COUNT` query from some scenarios #3373 -- Add a Bootsnap-style parsing cache when Bootsnap is detected #3156 -- Fix input validation for input object variables #3370 - -# 1.12.5 (18 February 2021) - -### New features - -- Resolvers: support `max_page_size` config #3338 -- RangeAdd: call `range_add_edge` (if supported) to improve stable connection support #3341 - -### Bug fixes - -- Backtrace: fix new tracer when analyzing multiplex without executing it #3342 -- Dataloader: pass along `throw`s #3333 -- Skip possible_types filtering for non-interface types #3336 -- Improve debugging message for ListResultFailedError #3339 - -# 1.12.4 (8 February 2021) - -### Bug fixes - -- Allow prepended modules to add fields #3325 -- Fix ConnectionExtension when another extension short-circuits `resolve` #3326 -- Backtrace: Fix GraphQL::Backtrace with static validation (used by graphql-client) #3324 -- Dataloader: Fix yield from root fiber when accessing arguments from analyzers. Fix arguments sometimes containing unresolved `Execution::Lazy`s #3320 -- Dataloader: properly pass raised errors to `handle_error` handlers #3319 -- Fix NameError in validation error #3303 -- Dataloader: properly batch when parent fields were not batched #3312 - -# 1.12.3 (27 January 2021) - -### Bug fixes - -- Fix constant names for legacy scalar types - -# 1.12.2 (26 January 2021) - -### New features - -- `GraphQL::Deprecation.warn` is used for GraphQL-Ruby 2.0 deprecation warnings (and calls through to `ActiveSupport::Deprecation.warn` if it's available) #3292 - -# 1.12.1 (25 January 2021) - -### Bug fixes - -- `GraphQL::Dataloader`: properly support selections with multiple fields #3297 - -# 1.12.0 (20 January 2021) - -### Breaking changes - -- `GraphQL::Schema` defaults to `GraphQL::Execution::Interpreter`, `GraphQL::Analysis::AST`, `GraphQL::Pagination::Connections`, and `GraphQL::Execution::Errors`. (#3145) To get the previous (deprecated) behaviors: - - ```ruby - # Revert to deprecated execution behaviors: - use GraphQL::Execution::Execute - use GraphQL::Analysis - # Disable the new connection implementation: - self.connections = nil - ``` - -- `GraphQL::Execution::Interpreter::Arguments` instances are frozen (#3138). (Usually, GraphQL code doesn't interact with these objects, but they're used some places under the hood.) - -### Deprecations - -- Many, many legacy classes and methods were deprecated. #3275 Deprecation errors include links to migration documentation. For a full list, see: https://github.com/rmosolgo/graphql-ruby/issues/3056 - -### New features - -- Rails-like argument validations (#3207) -- Fiber-based `GraphQL::Dataloader` for batch-loading data #3264 -- Connection and edge behaviors are available as mixins #3071 -- Schema definition supports schema directives #3224 - -### Bug fixes - -# 1.11.10 (5 Nov 2021) - -### Bug fixes - -- Properly hook up `Schema.max_validation_errors` at query runtime #3690 - -# 1.11.9 (1 Nov 2021) - -### New Features - -- `Schema.max_validation_errors(val)` limits the number of errors that can be added during static validation #3675 - -# 1.11.8 (12 Feb 2021) - -### Bug fixes - -- Improve performance of `Schema.possible_types(t)` for object types #3172 - -# 1.11.7 (18 January 2021) - -### Breaking changes - -- Incoming integer values are properly bound (as per the spec) #3206 To continue receiving out-of-bound integer values, add this to your schema's `def self.type_error(err, ctx)` hook: - - ```ruby - def self.type_error(err, ctx) - if err.is_a?(GraphQL::IntegerDecodingError) - return err.integer_value # return it anyways, since this is how graphql-ruby used to work - end - # ... - end - ``` - -### New features - -- Support Ruby 3.0 #3278 -- Add validation timeout option #3234 -- Support Prometheus custom_labels in GraphQLCollector #3215 - -### Bug fixes - -- Handle `GraphQL::UnauthorizedError` in interpreter in from arguments #3276 -- Set description for auto-generated `input:` argument #3141 -- Improve performance of fields will merge validation #3228 -- Use `Float` graphql type for ActiveRecord decimal columns #3246 -- Add some custom methods to ArrayConnection #3238 -- Fix generated fields for types ending Connection #3223 -- Improve runtime performance #3217 -- Improve argument handling when extensions shortcut the defined resolve #3212 -- Bind scalar ints as per the spec #3206 -- Validate that input object names are unique #3205 - -## 1.11.6 (29 October 2020) - -### Breaking changes - -FieldExtension: pass extended values instead of originals to `after_resolve` #3168 - -### Deprecations - -### New features - -- Accept additional options in `global_id_field` macro #3196 - -### Bug fixes - -- Use `graphql_name` in `UnauthorizedError` default message (fixes #3174) #3176 -- Improve error handling for base 64 decoding (in `UniqueWithinType`) #3179 -- Fix `.valid_isolated_input?` on parsed schemas (fixes #3181) #3182 -- Fix fields nullability in subscriptions documentation #3194 -- Update `RangeAdd` to use new connections when available #3195 - -## 1.11.5 (30 September 2020) - -### New features - -- SanitizedPrinter: accept `inline_variables: false` option and add `#redact_argument_value?` and `#redacted_argument_value` hooks #3167 -- GraphQL::Schema::Timeoout#max_seconds(query) can provide a per-query timeout duration #3167 -- Implement Interpreter::Arguments#fetch -- Assign `current_{path,field,arguments,object}` in `query.context` #3139. The values at these keys change while the query is running. -- ActionCableSubscriptions: accept `use(..., namespace: "...")` for running multiple schemas in the same application #3076 -- Add `deprecation_reason:` to arguments #3015 - -### Bug fixes - -- SanitizedPrinter: Fix lists and JSON scalars #3171 -- Improve retained memory in Schema.from_definition #3153 -- Make it easier to cache schema parsing #3153 -- Make sure deprecated arguments aren't required #3137 -- Use `.empty?` instead of `.length.zero?` in lexer #3134 -- Return a proper error when a stack error happens #3129 -- Assert valid input types on arguments #3120 -- Improve Validator#validate performance #3125 -- Don't wrap `RawValue` in ConnectionExtension #3122 -- Fix interface possible types visibility #3124 - -## 1.11.4 (24 August 2020) - -### Breaking changes - -### New features - -- Add `node_nullable` option for `edge_type` #3083 -- Use module namespacing for template generators #3098 - -### Bug fixes - -- Rescue `SystemStackError`s during validation #3107 -- Add `require 'digest/sha2'` for fingerprint #3103 -- Optimize `GraphQL::Query::Context#dig` #3090 -- Check if new connections before calling method on it (fixes #3059) #3100 -- Thread field owner type through interpreter runtime (fixes #3086) #3099 -- Check for visible interfaces on the type in warden #3096 -- Update `AppOpticsTracing` with latest changes in `PlatformTracing` #3097 -- Use throw instead of raise to halt subscriptions early #3084 -- Optimize `GraphQL::Query::Context#fetch` #3081 - -## 1.11.3 (13 August 2020) - -### Breaking changes - -- Reverted the `required` and `default_value` argument behaviour change in 1.11.2 since it was not spec compliant #3066 - -### New features - -- Improve resolver method conflict warning #3069, #3062 -- Store arguments on `Mutation` instances after they're loaded #3073 - -### Bug fixes - -- Fix connection wrappers on lazy lists #3070 - -## 1.11.2 (1 August 2020) - -### Breaking changes - -- Previously, GraphQL-Ruby allowed _both_ `default_value: ...` and `required: true` in argument definitions. However, this definition doesn't make sense -- a default value is never used for a `required: true` argument. This configuration now raises an error. Remove the `default_value:` to get rid of the error. #3011 - -### New features - -- Support Date, Time and OpenStruct in Subscription::Serialize #3057 - -### Bug fixes - -- Speed up `DELETE_NODE` check #3053 -- Reject invalid enum values during definition #3055 -- Fix `.trigger` from unsubscribed ActionCable channel #3051 -- Fix error message from VariablesAreUsedAndDefined for anonymous queries #3050 -- Fix renaming variable identifiers in AST visitor #3045 -- Reject `default_value: ...` used with `required: true` during definition #3011 -- Use the configured `edge_class:` with new connections #3036 -- Don't call visible for unused arguments #3030, #3031 -- Properly load directives from introspection results #3021 -- Reject interfaces as members of unions #3024 -- Load deprecation reason from introspection results #3014 -- Fix arguments caching when extension modify arguments #3009 - -## 1.11.1 (17 June 2020) - -### New Features - -- Add `StatsdTracing` #2996 - -### Bug Fixes - -- Raise the proper `InvalidNullError` when a mutation field returns an invalid `nil` #2997 - -## 1.11.0 (13 June 2020) - -### Breaking changes - -- Global tracers are removed (deprecated since 1.7.4) #2936 -- Fields defined in camel case (`field :doStuff`) will not line up to methods that are underscore case (`def do_stuff`). Instead, the given symbol is used _verbatim_. #2938 To work around this: - - - Change the name of the method to match the field (eg, `def doStuff`) - - Change the name of the field to match the method (eg, `field :do_stuff`, let graphql-ruby camelize it for you) - - Or, add `resolver_method: :do_stuff` to explicitly map the field to a method on the object type definition - - You can probably find instances of this in your application with a regexp like `/field :[a-z]+[A-Z]/`, and review them. - -### New features - -- `extend SubscriptionRoot` is no longer necessary #2770 -- Add `broadcast: true` option to subscriptions #2959 -- Add `Edge#parent` to new connection classes #2961 - -### Bug fixes - -- Use the field name as configured for hash key or method name #2906 - -## 1.10.12 (13 June 2020) - -### Bug fixes - -- Fix compatibility of `YYYY-mm-dd` with `Types::ISO8601DateTime` #2989 -- Remove unused ivar in InputObject #2987 - -## 1.9.21 (12 June 2020) - -### Bug fixes - -- Fix `extras:` on subscription fields #2983 - -## 1.10.11 (11 June 2020) - -### New features - -- Scout tracer adds transaction name to traces #2969 -- `resolve_type` can optionally return a resolved object #2976 -- DateTime scalar returns a `Time` for better timezone handling #2973 -- Interpreter memory improvements #2980, #2978 -- Support lazy values from field-level authorization hooks #2977 -- Object generator infers fields from model classes #2954 -- Add type-specific runtime errors #2957 - -### Bug fixes - -- Fix for error when using `extras:` with subscription fields #2984 -- Improve Schema.error_handler inheritance #2975 -- Add raw_value to conflict warning list #2958 -- Arguments#each_value yields ArgumentValues #2956 - -## 1.10.10 (20 May 2020) - -### Bug Fixes - -- Fix lazy `loads:` with list arguments #2949 -- Show object fields even when inherited ones are hidden #2950 -- Use `reverse_each` in instrumenters #2945 -- Fix underscored names in introspection loader #2941 -- Fix array input to Date/DateTime types #2927 -- Fix method conflict warnings on schema loader #2934 -- Fix some Ruby 2.7 warnings #2925 - -## 1.9.20 (20 May 2020) - -### Bug fixes - -- Fix `default_value: {}` on Ruby 2.7 - -## 1.10.9 (4 May 2020) - -### New features - -- Add `Interpreter::Arguments#dig` #2912 - -## 1.10.8 (27 April 2020) - -### Breaking changes - -- With the interpreter, `Query#arguments_for` returns `Interpreter::Arguments` instances instead of plain hashes. (They should work mostly the same, though.) #2881 - -### New features - -- `Schema::Field#introspection?` returns true for built-in introspection-related fields - -### Bug fixes - -- Fix Ruby 2.7 warning on `Schema.to_json` #2905 -- Pass `&block` to nested method calls to reduce stack depths #2900 -- Fix lazy `loads:` with list arguments #2894 -- Fix `loads:` on nested input object #2895 -- Rescue base64 encoding errors in the encoder #2896 - -## 1.10.7 (16 April 2020) - -### Breaking changes - -- `Schema.from_introspection(...)` builds class-based schemas #2876 - -### New features - -- `Date` and `DateTime` types also accept well-formatted strings #2848 -- `Schema.from_introspection(...)` builds class-based schemas #2876 -- `Schema#to_definition` now dumps all directives that were part of the original IDL, if the schema was parsed with `.from_definition` #2879 - -### Bug fixes - -- Fix memory leak in legacy runtime #2884 -- Fix interface inheritance in legacy runtime #2882 -- Fix description on `List` and `NonNull` types (for introspection) #2875 -- Fix over-rescue of NoMethodError when building list responses #2887 - -## 1.10.6 (6 April 2020) - -### New features - -- Add options to `implements(...)` and interface type visibility #2791 -- Add `Query#fingerprint` for logging #2859 -- Add `--playground` option to install generator #2839 -- Support lazy-loaded objects from input object `loads:` #2834 - -### Bug fixes - -- Fix `Language::Nodes` equality: move `eql?` to `==` #2861 -- Make rake task properly detect rails `environment` task #2862 -- Fix `nil` override for `max_page_size` #2843 -- Fix `pageInfo` methods when they're called before `nodes` #2845 -- Make the default development error match a normal GraphQL error #2825 -- Fix `loads:` with `require: false` #2833 -- Fix typeerror for `BigInt` given `nil` #2827 - -## 1.10.5 (12 March 2020) - -### New features - -- Add `#field_complexity` hook to `AST::QueryComplexity` analyzer #2807 - -### Bug fixes - -- Pass `nonce: true` when encoding cursors #2821 -- Ignore empty-string cursors #2821 -- Properly pass along `Analysis::AST` to schema instances #2820 -- Support filtering unreachable types in schemas from IDL #2816 -- Use `Query#arguments_for` for lookahead arguments #2811 -- Fix pagination bug on old connections #2799 -- Support new connection system on old runtime #2798 -- Add details to raise CoercionErrors #2796 - -## 1.10.4 (3 March 2020) - -### Breaking changes - -- When an argument is defined with a symbol (`argument :my_arg, ...`), that symbol is used _verbatim_ to build Ruby keyword arguments. Previously it was converted to underscore-case, but this autotransform was confusing and wrong in some cases. You may have to change the symbol in your `argument(...)` configuration if you were depending on that underscorization. #2792 -- Schemas from `.from_definition` previously had half-way connection support. It's now completely removed, so you have to add connection wrappers manually. See #2782 for migration notes. - -### New features - -- Add `Appoptics` tracing #2789 -- Add `Query#sanitized_query_string` #2785 -- Improved duplicate type error message #2777 - -### Bug fixes - -- Fix arguments ending in numbers, so they're injected with the same name that they're configured with #2792 -- Improve `Query#arguments_for` with interpreter #2781 -- Fix visitor replacement of variable definitions #2752 -- Remove half-broken connection handling from `Schema.from_definition` #2782 - -## 1.10.3 (17 Feb 2020) - -### New features - -- Support `loads:` with plain field arguments #2720 -- Support `raw_value(...)` to halt execution with a certain value #2699 -- `.read_subscription` can return `nil` to bypass executing a subscription #2741 - -### Bug fixes - -- Connection wrappers are properly inherited #2750 -- `prepare(...)` is properly applied to default values in subscription fields #2748 -- Code tidying for RSpec warnings #2741 -- Include new analysis module when generating a schema #2734 -- Include directive argument types in printed schemas #2733 -- Use `module_parent_name` in Rails #2713 -- Fix overriding default scalars in build_from_definition #2722 -- Fix some non-null errors in lists #2651 - -## 1.10.2 (31 Jan 2020) - -### Bug fixes - -- Properly wrap nested input objects in instances #2710 - -## 1.10.1 (28 Jan 2020) - -### Bug fixes - -- Include Interface-level `orphan_types` when building a schema #2705 -- Properly re-enter selections in complexity analyzer #2595 -- Fix input objects with null values #2690 -- Fix default values of `{}` in `.define`-based schemas #2703 -- Fix field extension presence check #2689 -- Make new relation connections more efficient #2697 -- Don't include fields `@skip(if: true)` or `@include(if: false)` in lookahead #2700 - -## 1.9.19 (28 Jan 2020) - -### Bug Fixes - -- Fix argument default value of `{}` with Ruby 2.7 argument handling #2704 - -## 1.10.0 (20 Jan 2020) - -### Breaking Changes - -- Class-based schemas using the new interpreter will now use _definition classes_ at runtime. #2363 (Previously, `.to_graphql` methods were used to generate singletons which were used at runtime.) This means: - - Methods that used to receive types at runtime will now receive classes instead of those singletons. - - `.name` will now call `Class#name`, which will give the class name. Use `.graphql_name` to get the name of a GraphQL type. (Fields, arguments and directives have `.graphql_name` too, so you can use it everywhere.) - - Some methods that return hashes are slow because they merge hashes according to class inheritance, for example `MySchema.types` and `MyObjectType.fields`. Instead: - - If you only need one item out of the Hash, use `.get_type(type_name)` or `.get_field(field_name)` instead. Those methods find a match without performing Hash merges. - - If you need the whole Hash, get a cached value from `context.warden` (an instance of `GraphQL::Schema::Warden`) at runtime. Those values reflect the types and fields which are permitted for the current query, and they're cached for life of the query. Check the API docs to see methods on the `warden`. -- Class-based schemas using the interpreter _must_ add `use GraphQL::Analysis::AST` to their schema (and update their custom analyzers, see https://graphql-ruby.org/queries/ast_analysis.html) #2363 -- ActiveSupport::Notifications events are correctly named in event.library format #2562 -- Field and Argument `#authorized?` methods now accept _three_ arguments (instead of 2). They now accept `(obj, args, ctx)`, where `args` is the arguments (for a field) or the argument value (for an argument). #2520 -- Double-null `!!` is disallowed by the parser #2397 -- (Non-interpreter only) The return value of subscription fields is passed along to execute the subscription. Return `nil` to get the previous behavior. #2536 -- `Schema.from_definition` builds a _class-based schema_ from the definition string #2178 -- Only integers are accepted for `Int` type #2404 -- Custom scalars now call `.coerce_input` on all input values - previously this call was skipped for `null` values. - -### Deprecations - -- `.define` is deprecated; class-based schema definitions should be used instead. If you're having trouble or you can't find information about an upgrade path, please open an issue on GitHub! - -### New Features - -- Add tracing events for `.authorized?` and `.resolve_type` calls #2660 -- `Schema.from_definition` accepts `using:` for installing plugins (equivalent to `use ...` in class-based schemas) #2307 -- Add `$` to variable names in error messages #2531 -- Add invalid value to argument error message #2531 -- Input object arguments with `loads:` get the loaded object in their `authorized?` hook, as `arg` in `authorized?(obj, args, ctx)`. #2536 -- `GraphQL::Pagination` auto-pagination system #2143 -- `Schema.from_definition` builds a _class-based schema_ from the definition string #2178 - -### Bug Fixes - -- Fix warnings on Ruby 2.7 #2668 -- Fix Ruby keyword list to support Ruby 2.7 #2640 -- Reduce memory of class-based schema #2636 -- Improve runtime performance of interpreter #2630 -- Big numbers (ie, greater than Ruby's `Infinity`) no longer :boom: when being reserialized #2320 -- Fix `hasNextPage`/`hasPrevious` page when max_page_size limits the items returned #2608 -- Return parse errors for empty documents and empty argument lists #2344 -- Properly serialize `defaultValue` of input objects containing enum values #2439 -- Don't crash when a query contains `!!`. #2397 -- Resolver `loads:` assign the value to argument `@loads` #2364 -- Only integers are accepted for `Int` type #2404 - -## 1.9.18 (15 Jan 2020) - -### New features - -- Support disabling `__type` or `__schema` individually #2657 -- Support Ruby 2.7, and turn on CI for it :tada: #2665 - -### Bug fixes - -- Fix Ruby 2.7 warnings #2653 #2669 -- Properly build camelized names for directive classes #2666 -- Use schema-defined context class for SDL generation #2656 -- Apply visibility checks when generating SDL #2637 - -## 1.9.17 (17 Dec 2019) - -### New features - -- Scoped context for propagating values to child fields #2634 -- Add `type_membership_class` with possible_type visibility #2391 - -### Bug fixes - -- Don't return unreachable types in introspection response #2596 -- Wrap more of execution with error handling #2632 -- Fix InputObject `.prepare` for the interpreter #2624 -- Fix Ruby keyword list to support Ruby 2.7 #2640 -- Fix performance of urlsafe_encode64 backport #2643 - -## 1.9.16 (2 Dec 2019) - -### Breaking changes - -- `GraphQL::Schema::Resolver#initialize` accepts a new keyword argument, `field:`. If you have overridden this method, you'll have to add that keyword to your argument list (and pass it along to `super`.) #2605 - -### Deprecations - -- `SkylightTracing` is disabled; the Skylight agent contains its own GraphQL support. See Skylight's docs for migration. #2601 - -### New features - -### Bug fixes - -- Fix multiplex max_depth calculation #2613 -- Use monotonic time in TimeoutMiddleware #2622 -- Use underscored names in Mutation generator #2617 -- Fix lookahead when added to mutations in their `field(...)` definitions #2605 -- Handle returned lists of errors from Mutations #2567 -- Fix lexer error on block strings containing only newlines #2598 -- Fix mutation generator to reference the new base class #2580 -- Use the right camelization configuration when generating subscription topics #2552 - -## 1.9.15 (30 Oct 2019) - -### New features - -- Improve parser performance #2572 -- Add `def prepare` API for input objects #1869 -- Support `extensions` config in Resolver classes #2570 -- Support custom `.connection_extension` in field classes #2561 -- Warn when a field name is a Ruby keyword #2559 -- Improve performance for ActiveRecord connection #2547 - -### Bug fixes - -- Fix errantly generated `def resolve_field` method in `BaseField` #2578 -- Comment out the `null_session` handling in the generated controller, for better compat with Rails API mode #2557 -- Fix validation error with duplicate, self-referencing fragment #2577 -- Revert the `.authorized?` behavior of InputObjects to handle cyclical references. See 1.10.0.pre1 for a better behavior. #2576 -- Replace `NotImplementedError` (which is meant for operating system APIs) with `GraphQL::RequiredImplementationMissingError` #2543 - -## 1.9.14 (14 Oct 2019) - -### New features - -- Add `null_session` CSRF handing in `install` generator #2524 -- Correctly report InputObjects without arguments and Objects without fields as invalid #2539 #2462 - -### Bug fixes - -- Fix argument incompatibility #2541 -- Add a `require` for `Types::ISO8691Date` #2528 -- Fix errors re-raised after lazy fields #2525 - -## 1.9.13 (8 Oct 2019) - -### Breaking changes - -- Enum values were (erroneously) accepted as ID or String values, but they aren't anymore. #2505 - -### New features - -- Add `Query#executed?` #2486 -- Add `Types::ISO8601Date` #2471 - -### Bug fixes - -- Don't accept Enums as IDs or Strings #2505 -- Call `.authorized?` hooks on arguments that belong to input objects #2519 -- Fix backslash parsing edge case #2510 -- Improve performance #2504 #2498 -- Properly stringify keys in error extensions #2508 -- Fix `extras:` handling in RelayClassicMutation #2484 -- Use `Types::BaseField` in scaffold #2470 - -## 1.9.12 (9 Sept 2019) - -### Breaking Changes - -- AST Analyzers follow fragments spreads as if they were inline fragments. #2463 - -### New Features - -- `use GraphQL::Execution::Errors` provides error handling for the new interpreter. #2458 - -### Bug Fixes - -- Fix false positive on enum value validation #2454 - -## 1.9.11 (29 Aug 2019) - -### Breaking Changes - -- Introspection fields are now considered for query depth validations, so you'll need at least `max_depth: 13` to run the introspection query #2437 - -### New features - -- Add `extras` setter to `GraphQL::Schema::Field` #2450 -- Add extensions in `CoercionError` #2431 - -### Bug fixes - -- Make `extensions` kwarg on field on more flexible for extensions with options #2443 -- Fix list validation error handling #2441 -- Include introspective fields in query depth calculations #2437 -- Correct the example for using 'a class method to generate fields' #2435 -- Enable multiple execution errors for Fields defined to return a list #2433 - -## 1.9.10 (20 Aug 2019) - -### New features - -- Support required arguments with default values #2416 - -### Bug fixes - -- Properly disable `max_complexity` and `max_depth` when `nil` is passed #2409 -- Fix printing class-based schemas #2406 -- Improve field method naming conflict check #2420 - -## 1.9.9 (30 July 2019) - -### New features - -- Memoize generated strings in `.to_query_string` #2400 -- Memoize generated strings in platform tracing #2401 - -### Bug fixes - -- Support class-based subscription type in `.define`-based schema #2403 - -## 1.9.8 (24 July 2019) - -### New features - -- Schema classes pass their configuration to subclasses #2384 -- Improve memory consumption of lexer and complexity validator #2389 -- The `install` generator creates a BaseArgument #2379 -- When a field name conflicts with a built-in method name, give a warning #2376 - -### Bug fixes - -- When a resolver argument uses `loads:`, the argument definition will preserve the type in `.loads` #2365 -- When an required argument is hidden, it won't add a validation error #2393 -- Fix handling of invalid UTF-8 #2372, #2377 -- Empty block strings are parsed correctly #2381 -- For resolvers, only authorize arguments once #2378 - -## 1.9.7 (25 June 2019) - -### Breaking changes - -- `Analysis::AST::Visitor#argument_definition` no longer returns the _previous_ argument definition. Instead, it returns the _current_ argument definition and `#previous_argument_definition` returns the previous one. You might have to replace calls to `.argument_definition` with `.previous_argument_definition` for compatibility. #2226 - -### New features - -- Accept a `subscription_scope` configuration in Subscription classes #2297 -- Add a `disable_introspection_entry_points` configuration in Schema classes #2327 -- Add `Analysis::AST::Visitor#argument_definition` which returns the _current_ argument definition, `#previous_argument_definition` returns the _previous_ one #2226 -- Run CI on Ruby 2.6 #2328 -- Autogenerate base field class #2216 -- Add timeout support with interpreter #2220 - -### Bug fixes - -- Fix Stack overflow when calling `.to_json` on input objects #2343 -- Fix off-by-one error with hasNextPage and ArrayConnections #2349 -- Fix GraphQL-Pro operation store compatibility #2350 -- Fix class-based transformer when multiple mutations are in one file #2309 -- Use `default_graphql_name` for Edge classes #2224 -- Support nested `loads:` with input objects #2323 -- Support `max_complexity` with multiplex & AST analysis #2306 - -## 1.9.6 (23 May 2019) - -### Bug fixes - -- Backport `String#-@` for Ruby 2.2 support #2305 - -## 1.9.5 (22 May 2019) - -### New features - -- Support `rescue_from` returning `GraphQL::ExecutionError` #2140 -- Accept `context:` in `Schema.validate` #2256 -- Include `query:` in interpreter tracing for `execute_field` and `execute_field_lazy` #2236 -- Add `Types::JSON` #2227 -- Add `null:` option to `BaseEdge.node_type` #2249 - -### Bug fixes - -- Fix Ruby 2.2 compatibility #2302 -- Distinguish aliased selections in lookahead #2266 -- Properly show list enum default values in introspection #2263 -- Performance improvements: #2289, #2244, #2258, #2257, #2240 -- Don't recursively unwrap inputs for RelayClassicMutation #2236 -- Fix `Schema::Field#scoped?` when no return type #2255 -- Properly forward more authorization errors #2165 -- Raise `ParseError` for `.parse(nil)` #2238 - -## 1.9.4 (5 Apr 2019) - -### Breaking Changes - -- `GraphQL::Schema::Resolver::LoadApplicationObjectFailedError` was renamed to `GraphQL::LoadApplicationObjectFailedError`. (This will only break if you're referencing the class by name and running Ruby 2.5+) #2080 - -### New features - -- Add `Types::BigInt` #2150 -- Add auto-loading arguments support in Input Object types #2080 -- Add analytics tag to Datadog tracing #2154 - -### Bug fixes - -- Fix `Query#execute` when no explicit query string is passed in #2142 -- Fix when a root type returns nil because unauthorized #2144 -- Fix tracing `node` by threading `owner:` through field tracing #2156 -- Fix interpreter handling of exceptions raised during argument preparation #2198 -- Fix ActionCableLink when there are errors but no data #2176 -- Provide empty hash as default option for field resolvers #2189 -- Prevent argument names from overwriting Arguments methods #2171 -- Include array indices in error paths #2162 -- Handle non-node arrays in AST visitor #2161 - -## 1.9.3 (20 Feb 2019) - -### Bug fixes - -- Fix `Schema::Subscription` when it has no arguments #2135 -- Don't try to scope `nil`, just skip scoping altogether #2134 -- Fix when a root `.authorized?` returns `false` and there's no `root_value` #2136 -- Fix platform tracing with interpreter & introspection #2137 -- Support root Subscription types with name other than `Subscription` #2102 -- Fix nested list-type input object nullability validation #2123 - -## 1.9.2 (15 Feb 2019) - -### Bug fixes - -- Properly support connection fields with resolve procs #2115 - -## 1.9.1 (14 Feb 2019) - -### Bug fixes - -- Properly pass errors to Resolver `load_application_object_failed` methods #2110 - -## 1.9.0 (13 Feb 2019) - -### Breaking Changes - -- AST nodes are immutable. To modify a parsed GraphQL query, see `GraphQL::Language::Visitor` for its mutation API, which builds a new AST with the specified mutations applied. #1338, #1740 -- Cursors use urlsafe Base64. This won't break your clients (it's backwards-compatible), but it might break your tests, so it's listed here. #1698 -- Add `field(..., resolver_method:)` for when GraphQL-Ruby should call a method _other than_ the one whose name matches the field name (#1961). This means that if you're using `method:` to call a different method _on the Schema::Object subclass_, you should update that configuration to `resolver_method:`. (`method:` is still used to call a different method on the _underlying application object_.) -- `Int` type now applies boundaries as [described in the spec](https://facebook.github.io/graphql/June2018/#sec-Int) #2101. To preserve the previous, unbounded behavior, handle the error in your schema's `.type_error(err, ctx)` hook, for example: - - ```ruby - class MySchema < GraphQL::Schema - def self.type_error(err, ctx) - if err.is_a?(GraphQL::IntegerEncodingError) - # Preserve the previous unbounded behavior - # by returning the out-of-bounds value - err.integer_value - else - super - end - end - end - ``` - -- `field(...)` configurations don't create implicit method definitions (#1961). If one resolver method depended on the implicitly-created method from another field, you'll have to refactor that call or manually add a `def ...` for that field. -- Calling `super` in a field method doesn't work anymore (#1961) -- Error `"problems"` are now in `"extensions" : { "problems": ... }` #2077 -- Change schema default to `error_bubbling false` #2069 - -### New Features - -- Add class-based subscriptions with `GraphQL::Schema::Subscription` #1930 -- Add `GraphQL::Execution::Interpreter` (#1394) and `GraphQL::Analysis::AST` (#1824) which together cut GraphQL overhead by half (time and memory) -- Add `Schema.unauthorized_field(err)` for when `Field#authorized?` checks fail (#1994) -- Add class-based custom directives for the interpreter (#2055) -- Add `Schema::FieldExtension` for customizing field execution with class-based fields #1795 -- Add `Query#lookahead` for root-level selection info #1931 -- Validation errors have `"extensions": { ... }` which includes metadata about that error #1970 - -### Bug fixes - -- Fix list-type arguments passed with a single value #2085 -- Support `false` as an Enum value #2050 -- Support `hash_key:` fields when the key isn't a valid Ruby method name #2016 - -## 1.8.15 (13 Feb 2019) - -### Bug fixes - -- Fix unwrapping inputobject types when turning arguments to hashes #2094 -- Support lazy objects from `.resolve_type` hooks #2108 - -## 1.8.14 (9 Feb 2019) - -### Bug Fixes - -- Fix single-item list inputs that aren't passed as lists #2095 - -## 1.8.13 (4 Jan 2019) - -### Bug fixes - -- Fix regression in block string parsing #2032 - -## 1.8.12 (3 Jan 2019) - -### Breaking changes - -- When an input object's argument has a validation error, that error is reported on the _argument_ instead of its parent input object. #2013 - -### New features - -- Add `error_bubbling false` Schema configuration for nicer validation of compound inputs #2013 -- Print descriptions as block strings in SDL #2011 -- Improve string-to-constant resolution #1810 -- Add `Query::Context#to_hash` for splatting #1955 -- Add `#dig` to `Schema::InputObject` and `Query::Arguments` #1968 -- Add `.*_execution_strategy` methods to class-based schemas #1914 -- Accept multiple errors when adding `.rescue_from` handlers #1991 - -### Bug fixes - -- Fix scalar tracing in NewRelic and Skylight #1954 -- Fix lexer for multiple block strings #1937 -- Add `unscope(:order)` when counting relations #1911 -- Improve build-from-definition error message #1998 -- Fix regression in legacy compat #2000 - -## 1.8.11 (16 Oct 2018) - -### New features - -- `extras: [:lookahead]` injects a `GraphQL::Execution::Lookahead` - -### Bug fixes - -- Fix type printing in Printer #1902 -- Rescue `GraphQL::ExecutionError` in `.before_query` hooks #1898 -- Properly load default values that are lists of input objects from the IDL #1874 - -## 1.8.10 (21 Sep 2018) - -### Bug fixes - -- When using `loads:` with a nullable mutation input field, allow `null` values to be provided. #1851 -- When an invalid Base64 encoded cursor is provided, raise a `GraphQL::ExecutionError` instead of `ArgumentError`. #1855 -- Fix an issue with `extras: [:path]` would use the field's `path` instead of the `context`. #1859 - -### New features - -- Add scalar type generator `rails g graphql:scalar` #1847 -- Add `#dig` method to `Query::Context` #1861 - -## 1.8.9 (13 Sep 2018) - -### Breaking changes - -- When `field ... ` is called with a block and the block has one argument, the field is yielded, but `self` inside the block is _not_ changed to the field. #1843 - -### New features - -- `extras: [...]` can inject values from the field instance #1808 -- Add `ISO8601DateTime.time_precision` for customization #1845 -- Fix input objects with default values of enum #1827 -- `Schema.sync_lazy(value)` hook for intercepting lazy-resolved objects #1784 - -### Bug fixes - -- When a field block is provided with an arity of `1`, yield the field #1843 - -## 1.8.8 (27 Aug 2018) - -### Bug fixes - -- When using `RelayClassicMutation`, `client_mutation_id` will no longer be passed to `authorized?` method #1771 -- Fix issue in schema upgrader script which would cause `.to_non_null_type` calls in type definition to be ignored #1783 -- Ensure enum values respond to `graphql_name` #1792 -- Fix infinite resolution bug that could occur when an exception not inheriting from `StandardError` is thrown #1804 - -### New features - -- Add `#path` method to schema members #1766 -- Add `as:` argument to allow overriding the name of the argument when using `loads:` #1773 -- Add support for list of IDs when using `loads:` in an argument definition #1797 - -## 1.8.7 (9 Aug 2018) - -### Breaking changes - -- Some mutation authorization hooks added in 1.8.5 were changed, see #1736 and #1737. Roughly: - - - `before_prepare` was changed to `#ready?` - - `validate_*` hooks were replaced with a single `#authorized?` method - -### Bug fixes - -- Argument default values include nested default values #1728 -- Clean up duplicate method defs #1739 - -### New features - -- Built-in support for Mongoid 5, 6, 7 #1754 -- Mutation `#ready?` and `#authorized?` may halt flow and/or return data #1736, #1737 -- Add `.scope_items(items, ctx)` hook for filtering lists -- Add `#default_graphql_name` for overriding default logic #1729 -- Add `#add_argument` for building schemas #1732 -- Cursors are decoded using `urlsafe_decode64` to future-proof for urlsafe cursors #1748 - -## 1.8.6 (31 July 2018) - -### Breaking changes - -- Only allow Objects to implement actual Interfaces #1715. Use `include` instead for plain Ruby modules. -- Revert extending interface methods onto Objects #1716. If you were taking advantage of this feature, you can create a plain Ruby module with the functionality and include it in both the interface and object. - -### Deprecations - -### New features - -- Support string descriptions (from June 2018 GraphQL spec) #1725 -- Add some accessors to Schema members #1722 -- Yield argument for definition block with arity of one #1714 -- Yield field for definition blocks with arity of one #1712 -- Support grouping by "endpoint" with skylight instrumentation #1663 -- Validation: Don't traverse irep if no handlers are registered #1696 -- Add `nodes_field` option to `edge_type` to hide nodes field #1693 -- Add `GraphQL::Types::ISO8601DateTime` to documentation #1694 -- Conditional Analyzers #1690 -- Improve error messages in `ActionCableSubscriptions` #1675 -- Add Prometheus tracing #1672 -- Add `map` to `InputObject` #1669 - -### Bug fixes - -- Improve the mutation generator #1718 -- Fix method inheritance for interfaces #1709 -- Fix Interface inheritance chain #1686 -- Fix require in `tracing.rb` #1685 -- Remove delegate for `FieldResolutionContext#schema` #1682 -- Remove duplicated `object_class` method #1667 - -## 1.8.5 (10 July 2018) - -### Breaking changes - -- GraphQL validation errors now include `"filename"` if the parsed document had a `filename` #1618 - -### Deprecations - -- `TypeKind#resolves?` is deprecated in favor of `TypeKind#abstract?` #1619 - -### New features - -- Add Mutation loading/authorization system #1609 -- Interface `definition_methods` are inherited by object type classes #1635 -- include `"filename"` in GraphQL errors if the parsed document has a filename #1618 -- Add `Schema::InputObject#empty?` #1651 -- require `ISO8601DateTime` by default #1660 -- Support `extend` in the parser #1620 -- Improve generator to have nicer error handling in development - -### Bug fixes - -- Fix `@skip`/`@include` with default value of `false` #1617 -- Fix lists of abstract types with promises #1613 -- Don't check the type of `nil` when it's in a list #1610 -- Fix NoMethodError when `variables: nil` is passed to `execute(...)` #1661 -- Objects returned from `Schema.unauthorized_objects` are properly wrapped by their type proxies #1662 - -## 1.8.4 (21 June 2018) - -### New features - -- Add class-based definitions for Relay types #1568 -- Add a built-in auth system #1494 - -### Bug fixes - -- Properly rescue coercion errors in variable values #1602 - -## 1.8.3 (14 June 2018) - -### New features - -- Add an ISO 8601 DateTime scalar: `Types::ISO8601DateTime`. #1566 -- Use classes under the hood for built-in scalars. These are now accessible via `Types::` namespace. #1565 -- Add `possible_types` helpers to abstract types #1580 - -### Bug fixes - -- Fix `Language::Visitor` when visiting `InputObjectTypeDefinition` nodes to include child `Directive` nodes. #1584 -- Fix an issue preventing proper subclassing of `TimeoutMiddleware`. #1579 -- Fix `graphql:interface` generator such that it generates working code. #1577 -- Update the description of auto-generated `before` and `after` arguments to better describe their input type. #1572 -- Add `Language::Nodes::DirectiveLocation` AST node to represent directive locations in directive definitions. #1564 - -## 1.8.2 (6 June 2018) - -### Breaking changes - -- `Schema::InputObject#to_h` recursively transforms hashes to underscorized, symbolized keys. #1555 - -### New features - -- Generators create class-based types #1562 -- `Schema::InputObject#to_h` returns a underscorized, symbolized hash #1555 - -### Bug fixes - -- Support `default_mask` in class-based schemas #1563 -- Fix null propagation for list types #1558 -- Validate unique arguments in queries #1557 -- Fix `RelayClassicMutation`s with no arguments #1543 - -## 1.8.1 (1 June 2018) - -### Breaking changes - -- When filtering items out of a schema, Unions will now be hidden if their possible types are all hidden or if all fields returning it are hidden. #1515 - -### New features - -- `GraphQL::ExecutionError.new` accepts an `extensions:` option which will be merged into the `"extensions"` key in that error's JSON #1552 - -### Bug fixes - -- When filtering items out of a schema, Unions will now be hidden if their possible types are all hidden or if all fields returning it are hidden. #1515 -- Require that fields returning interfaces have selections made on them #1551 -- Correctly mark introspection types and fields as `introspection?` #1535 -- Remove unused introspection objects #1534 -- use `object`/`context` in the upgrader instead of `@object`/`@context` #1529 -- (Development) Don't require mongodb for non-mongo tests #1548 -- Track position of union member nodes in the parser #1541 - -## 1.8.0 (17 May 2018) - -`1.8.0` has been in prerelease for 6 months. See the prerelease changelog for change-by-change details. Here's a high-level changelog, followed by a detailed list of changes since the last prerelease. - -### High-level changes - -#### Breaking Changes - -- GraphQL-Ruby is not tested on Ruby 2.1. #1070 Because Ruby 2.1 doesn't garbage collect Symbols, it's possible that GraphQL-Ruby will introduce a OOM vulnerability where unique symbols are dynamically created, for example, turning user input into Symbols. No instances of this are known in GraphQL-Ruby ... yet! -- `GraphQL::Delegate`, a duplicate of Ruby's `Forwardable`, was removed. Use `Forwardable` instead, and update your Ruby if you're on `2.4.0`, due to a performance regression in `Forwardable` in that version. -- `MySchema.subscriptions.trigger` asserts that its inputs are valid arguments #1400. So if you were previously passing invalid options there, you'll get an error. Remove those options. - -#### New Features - -- A new class-based API for schema definition. The old API is completely supported, but the new one is much nicer to use. If you migrate, some schema extensions may require a bit of extra work. -- Built-in support for Mongoid-backed Relay connections -- `.execute(variables: ...)` and `subscriptions.trigger` both accept Symbol-keyed hashes -- Lots of other small things around SDL parsing, tracing, runtime ... everything. Read the details below for a full list. - -#### Bug Fixes - -- Many, many bug fixes. See the detailed list if you're curious about specific bugs. - -### Changes since `1.8.0.pre11`: - -#### Breaking Changes - -- `GraphQL::Schema::Field#initialize`'s signature changed to accept keywords and a block only. `type:`, `description:` and `name:` were moved to keywords. See `Field.from_options` for how the `field(...)` helper's arguments are merged to go to `Field.new`. #1508 - -#### New Features - -- `Schema::Resolver` is a replacement for `GraphQL::Function` #1472 -- Fix subscriptions with class-based schema #1478 -- `Tracing::NewRelicTracing` accepts `set_transaction_name:` to use the GraphQL operation name as the NewRelic transaction name #1430 - -#### Bug fixes - -- Backported `accepts_definition`s are inherited #1514 -- Fix Schema generator's `resolve_type` method #1481 -- Fix constant assignment warnings with interfaces including multiple other interfaces #1465 -- InputObject types loaded from SDL have the proper AST node assigned to them #1512 - -## 1.8.0.pre11 (3 May 2018) - -### Breaking changes - -- `Schema::Mutation.resolve_mutation` was moved to an instance method; see changes to `Schema::RelayClassicMutation` in #1469 for an example refactor -- `GraphQL::Delegate` was removed, use Ruby's `Forwardable` instead (warning: bad performance on Ruby 2.4.0) -- `GraphQL::Schema::Interface` is a module, not a class #1372. To refactor, use a base module instead of a base class: - - ```ruby - module BaseInterface - include GraphQL::Schema::Interface - end - ``` - - And include that in your interface types: - - ```ruby - module Reservable - include BaseInterface - field :reservations, ... - end - ``` - - In object types, no change is required; use `implements` as before: - - ```ruby - class EventVenue < BaseObject - implements Reservable - end - ``` - -### New features - -- `GraphQL::Schema::Interface` is a module -- Support `prepare:` and `as:` argument options #1469 -- First-class support for Mongoid connections #1452 -- More type inspection helpers for class-based types #1446 -- Field methods may call `super` to get the default behavior #1437 -- `variables:` accepts symbol keys #1401 -- Reprint any directives which were parsed from SDL #1417 -- Support custom JSON scalars #1398 -- Subscription `trigger` accepts symbol, underscored arguments and validates their presence #1400 -- Mutations accept a `null(true | false)` setting to affect field nullability #1406 -- `RescueMiddleware` uses inheritance to match errors #1393 -- Resolvers may return a list of errors #1231 - -### Bug fixes - -- Better error for anonymous class names #1459 -- Input Objects correctly inherit arguments #1432 -- Fix `.subscriptions` for class-based Schemas #1391 - -## 1.8.0.pre10 (4 Apr 2018) - -### New features - -- Add `Schema::Mutation` and `Schema::RelayClassicMutation` base classes #1360 - -### Bug fixes - -- Fix using anonymous classes for field types #1358 - -## 1.8.0.pre9 (19 Mar 2018) - -- New version number. (I needed this because I messed up build tooling for 1.8.0.pre8). - -## 1.8.0.pre8 (19 Mar 2018) - -### New Features - -- Backport `accepts_definition` for configurations #1357 -- Add `#owner` method to Schema objects -- Add `Interface.orphan_types` config for orphan types #1346 -- Add `extras: :execution_errors` for `add_error` #1313 -- Accept a block to `Schema::Argument#initialize` #1356 - -### Bug Fixes - -- Support `cursor_encoder` #1357 -- Don't double-count lazy/eager field time in Tracing #1321 -- Fix camelization to support single leading underscore #1315 -- Fix `.resolve_type` for Union and Interface classes #1342 -- Apply kwargs before block in `Argument.from_dsl` #1350 - -## 1.8.0.pre7 (27 Feb 2018) - -### New features - -- Upgrader improvements #1305 -- Support `global_id_field` for interfaces #1299 -- Add `camelize: false` #1300 -- Add readers for `context`, `object` and `arguments` #1283 -- Replace `Schema.method_missing` with explicit whitelist #1265 - -## 1.8.0.pre6 (1 Feb 2018) - -### New features - -- Custom enum value classes #1264 - -### Bug fixes - -- Properly print SDL type directives #1255 - -## 1.8.0.pre5 (1 Feb 2018) - -### New features - -- Upgrade argument access with the upgrader #1251 -- Add `Schema#find(str)` for finding schema members by name #1232 - -### Bug fixes - -- Fix `Schema.max_complexity` #1246 -- Support cyclical connections/edges #1253 - -## 1.8.0.pre4 (18 Jan 2018) - -### Breaking changes - -- `Type.fields`, `Field.arguments`, `Enum.values` and `InputObject.arguments` return a Hash instead of an Array #1222 - -### New features - -- By default, fields try hash keys which match their name, as either a symbol or a string #1225 -- `field do ... end` instance_evals on the Field instance, not a FieldProxy #1227 -- `[T, null: true]` creates lists with nullable items #1229 -- Upgrader improvements #1223 - -### Bug fixes - -- Don't require `parser` unless the upgrader is run #1218 - -## 1.8.0.pre3 (12 Jan 2018) - -### New Features - -- Custom `Context` classes for class-based schemas #1161 -- Custom introspection for class-based schemas #1170 -- Improvements to upgrader tasks and internals #1151, #1178, #1212 -- Allow description inside field blocks #1175 - -## 1.8.0.pre2 (29 Nov 2017) - -### New Features - -- Add `rake graphql:upgrade[app/graphql]` for automatic upgrade #1110 -- Automatically camelize field names and argument names #1143, #1126 -- Improved error message when defining `name` instead of `graphql_name` #1104 - -### Bug fixes - -- Fix list wrapping when value is `nil` #1117 -- Fix ArgumentError typo #1098 - -## 1.8.0.pre1 (14 Nov 2017) - -### Breaking changes - -- Stop official support for Ruby 2.1 #1070 - -### New features - -- Add class-based schema definition API #1037 - -## 1.7.14 (4 Apr 2018) - -### New features - -- Support new IDL spec for `&` for interfaces #1304 -- Schema members built from IDL have an `#ast_node` #1367 - -### Bug fixes - -- Fix paging backwards with `hasNextPage` #1319 -- Add hint for `orphan_types` in error message #1380 -- Use an empty hash for `result` when a query has unhandled errors #1382 - -## 1.7.13 (28 Feb 2018) - -### Bug fixes - -- `Schema#as_json` returns a hash, not a `GraphQL::Query::Result` #1288 - -## 1.7.12 (13 Feb 2018) - -### Bug fixes - -- `typed_children` should always return a Hash #1278 - -## 1.7.11 (13 Feb 2018) - -### Bug fixes - -- Fix compatibility of `irep_node.typed_children` on leaf nodes #1277 - -## 1.7.10 (13 Feb 2018) - -### Breaking Changes - -- Empty selections (`{ }`) are invalid in the GraphQL spec, but were previously allowed by graphql-ruby. They now return a parse error. #1268 - -### Bug fixes - -- Fix error when inline fragments are spread on scalars #1268 -- Fix printing SDL when types have interfaces and directives #1255 - -## 1.7.9 (1 Feb 2018) - -## New Features - -- Support block string inputs #1219 - -## Bug fixes - -- Fix deprecation regression in schema printer #1250 -- Fix resource names in DataDog tracing #1208 -- Fix passing `context` to multiplex in `Query#result` #1200 - -## 1.7.8 (11 Jan 2018) - -### New features - -- Refactor `Schema::Printer` to use `Language::Printer` #1159 -- Add `ArgumentValue#default_used?` and `Arguments#default_used?` #1152 - -### Bug fixes - -- Fix Scout Tracing #1187 -- Call `#inspect` for `EnumType::UnresolvedValueError` #1179 -- Parse empty field sets in IDL parser #1145 - -## 1.7.7 (29 Nov 2017) - -### New features - -- `Schema#to_document` returns a `Language::Nodes::Document` #1134 -- Add `trace_scalars` and `trace: true|false` to monitoring #1103 -- Add `Tracing::DataDogPlatform` monitoring #1129 -- Support namespaces in `rails g graphql:function` and `:loader` #1127 -- Support `serializer:` option for `ActionCableSubscriptions` #1085 - -### Bug fixes - -- Properly count the column after a closing quote #1136 -- Fix default value input objects in `Schema.from_definition` #1135 -- Fix `rails destroy graphql:mutation` #1119 -- Avoid unneeded query in RelationConnection with Sequel #1101 -- Improve & document instrumentation stack behavior #1101 - -## 1.7.6 (13 Nov 2017) - -### Bug fixes - -- Serialize symbols in with `GraphQL::Subscriptions::Serialize` #1084 - -## 1.7.5 (7 Nov 2017) - -### Breaking changes - -- Rename `Backtrace::InspectResult#inspect` to `#inspect_result` #1022 - -### New features - -- Improved website search with Algolia #934 -- Support customized generator directory #1047 -- Recursively serialize `GlobalID`-compliant objects in Arrays and hashes #1030 -- Add `Subscriptions#build_id` helper #1046 -- Add `#non_null?` and `#list?` helper methods to type objects #1054 - -### Bug fixes - -- Fix infinite loop in query instrumentation when error is raised #1074 -- Don't try to trace error when it's not raised during execution -- Improve validation of query variable definitions #1073 -- Fix Scout tracing module load order #1064 - -## 1.7.4 (9 Oct 2017) - -### Deprecations - -- `GraphQL::Tracing.install` is deprecated, use schema-local or query-local tracers instead #996 - -### New features - -- Add monitoring plugins for AppSignal, New Relic, Scout and Skylight #994, #1013 -- Custom coercion errors for custom scalars #988 -- Extra `options` for `GraphQL::ExecutionError` #1002 -- Use `GlobalID` for subscription serialization when available #1004 -- Schema- and query-local, threadsafe tracers #996 - -### Bug fixes - -- Accept symbol-keyed arguments to `.trigger` #1009 - -## 1.7.3 (20 Sept 2017) - -### Bug fixes - -- Fix arguments on `Query.__type` field #978 -- Fix `Relay::Edge` objects in `Backtrace` tables #975 - -## 1.7.2 (20 Sept 2017) - -### Bug fixes - -- Correctly skip connections that return `ctx.skip` #972 - -## 1.7.1 (18 Sept 2017) - -### Bug fixes - -- Properly release changes from 1.7.0 - -## 1.7.0 (18 Sept 2017) - -### Breaking changes - -- `GraphQL::Result` is the returned from GraphQL execution. #898 `Schema#execute` and `Query#result` both return a `GraphQL::Result`. It implements Hash-like methods to preserve compatibility. - -### New features - -- `puts ctx.backtrace` prints out a GraphQL backtrace table #946 -- `GraphQL::Backtrace.enable` wraps unhandled errors with GraphQL backtraces #946 -- `GraphQL::Relay::ConnectionType.bidrectional_pagination = true` turns on _true_ bi-directional pagination checks for `hasNextPage`/`hasPreviousPage` fields. This will become the default behavior in a future version. #960 -- Field arguments may be accessed as methods on the `args` object. This is an alternative to `#[]` syntax which provides did-you-mean behavior instead of returning `nil` on a typo. #924 For example: - - ```ruby - # using hash syntax: - args[:limit] # => 10 - args[:limittt] # => nil - # using method syntax: - args.limit # => 10 - args.limittt # => NoMethodError - ``` - - The old syntax is _not_ deprecated. - -- Improvements to schema filters #919 - - If a type is not referenced by anything, it's hidden - - If a type is an abstract type, but has no visible members, it's hidden - -- `GraphQL::Argument.define` builds re-usable arguments #948 -- `GraphQL::Subscriptions` provides hooks for subscription platforms #672 -- `GraphQL::Subscriptions::ActionCableSubscriptions` implements subscriptions over ActionCable #672 -- More runtime values are accessible from a `ctx` object #923 : - - `ctx.parent` returns the `ctx` from the parent field - - `ctx.object` returns the current `obj` for that field - - `ctx.value` returns the resolved GraphQL value for that field - - These can be used together, for example, `ctx.parent.object` to get the parent object. -- `GraphQL::Tracing` provides more hooks into gem internals for performance monitoring #917 -- `GraphQL::Result` provides access to the original `query` and `context` after executing a query #898 - -### Bug fixes - -- Prevent passing _both_ query string and parsed document to `Schema#execute` #957 -- Prevent invalid names for types #947 - -## 1.6.8 (8 Sept 2017) - -### Breaking changes - -- Validate against EnumType value names to match `/^[_a-zA-Z][_a-zA-Z0-9]*$/` #915 - -### New features - -- Use stdlib `forwardable` when it's not Ruby 2.4.0 #926 -- Improve `UnresolvedTypeError` message #928 -- Add a default field to the Rails generated mutation type #922 - -### Bug fixes - -- Find types via directive arguments when traversing the schema #944 -- Assign `#connection?` when building a schema from IDL #941 -- Initialize `@edge_class` to `nil` #942 -- Disallow invalid enum values #915 -- Disallow doubly-nested non-null types #916 -- Fix `Query#selected_operation_name` when no selections are present #899 -- Fix needless `COUNT` query for `hasNextPage` #906 -- Fix negative offset with `last` argument #907 -- Fix line/col for `ArgumentsAreDefined` validation #890 -- Fix Sequel error when limit is `0` #892 - -## 1.6.7 (11 Aug 2017) - -### New features - -- Add `GraphQL.parse_file` and `AbstractNode#filename` #873 -- Support `.graphql` filepaths with `Schema.from_definition` #872 - -### Bug fixes - -- Fix variable usage inside non-null list #888 -- Fix unqualified usage of ActiveRecord::Relation #885 -- Fix `FieldsWillMerge` handling of equivalent input objects -- Fix to call `prepare:` on nested input types - -## 1.6.6 (14 Jul 2017) - -### New features - -- Validate `graphql-pro` downloads with `rake graphql:pro:validate[$VERSION]` #846 - -### Bug fixes - -- Remove usage of Rails-only `Array.wrap` #840 -- Fix `RelationConnection` to count properly when relation contains an alias #838 -- Print name of Enum type when a duplicate value is added #843 - -## 1.6.5 (13 Jul 2017) - -### Breaking changes - -- `Schema#types[](type_name)` returns `nil` when there's no type named `type_name` (it used to raise `RuntimeError`). To get an error for missing types, use `.fetch` instead, for example: - - ```ruby - # Old way: - MySchema.types[type_name] # => may raise RuntimeError - # New way: - MySchema.types.fetch(type_name) # => may raise KeyError - ``` - -- Schema build steps happen in one pass instead of two passes #819 . This means that `instrument(:field)` hooks may not access `Schema#types`, `Schema#possible_types` or `Schema#get_field`, since the underlying data hasn't been prepared yet. There's not really a clear upgrade path here. It's a bit of a mess. If you're affected by this, feel free to open an issue and we'll try to find something that works! - -### Deprecations - -- `Schema#resolve_type` is now called with `(abstract_type, obj, ctx)` instead of `(obj, ctx)` #834 . To update, add an unused parameter to the beginning of your `resolve_type` hook: - - ```ruby - MySchema = GraphQL::Schema.define do - # Old way: - resolve_type ->(obj, ctx) { ... } - # New way: - resolve_type ->(type, obj, ctx) { ... } - end - ``` - -### New features - -- `rails g graphql:mutation` will add Mutation boilerplate if it wasn't added already #812 -- `InterfaceType` and `UnionType` both accept `resolve_type ->(obj, ctx) { ... }` functions for type-specific resolution. This function takes precedence over `Schema#resolve_type` #829 #834 -- `Schema#resolve_type` is called with three arguments, `(abstract_type, obj, ctx)`, so you can distinguish object type based on interface or union. -- `Query#operation_name=` may be assigned during query instrumentation #833 -- `query.context.add_error(err)` may be used to add query-level errors #833 - -### Bug fixes - -- `argument(...)` DSL accepts custom keywords #809 -- Use single-query `max_complexity` overrides #812 -- Return a client error when `InputObjectType` receives an array as input #803 -- Properly handle raised errors in `prepare` functions #805 -- Fix using `as` and `prepare` in `argument do ... end` blocks #817 -- When types are added to the schema with `instrument(:field, ...)`, make sure they're in `Schema#types` #819 -- Raise an error when duplicate `EnumValue` is created #831 -- Properly resolve all query levels breadth-first when using `lazy_resolve` #835 -- Fix tests to run on PostgresQL; Run CI on PostgresQL #814 -- When no query string is present, return a client error instead of raising `ArgumentError` #833 -- Properly validate lists containing variables #824 - -## 1.6.4 (20 Jun 2017) - -### New features - -- `Schema.to_definition` sorts fields and arguments alphabetically #775 -- `validate: false` skips static validations in query execution #790 - -### Bug fixes - -- `graphql:install` adds `operation_name: params[:operationName]` #786 -- `graphql:install` skips `graphiql-rails` for API-only apps #772 -- `SerialExecution` calls `.is_a?(Skip)` to avoid user-defined `#==` methods #794 -- `prepare:` functions which return `ExecutionError` are properly handled when default values are present #801 - -## 1.6.3 (7 Jun 2017) - -### Bug fixes - -- Run multiplex instrumentation when running a single query with a legacy execution strategy #766 -- Check _each_ strategy when looking for overridden execution strategy #765 -- Correctly wrap `Method`s with BackwardsCompatibility #763 -- Various performance improvements #764 -- Don't call `#==(other)` on user-provided objects (use `.is_a?` instead) #761 -- Support lazy object from custom connection `#edge_nodes` #762 -- If a lazy field returns an invalid null, stop evaluating its siblings #767 - -## 1.6.2 (2 Jun 2017) - -### New features - -- `Schema.define { default_max_page_size(...) }` provides a Connection `max_page_size` when no other is provided #752 -- `Schema#get_field(type, field)` accepts a string type name #756 -- `Schema.define { rescue_from(...) }` accepts multiple error classes for the handler #758 - -### Bug fixes - -- Use `*_execution_strategy` when executing a single query (doesn't support `Schema#multiplex`) #755 -- Fix NameError when `ActiveRecord` isn't loaded #747 -- Fix `Query#mutation?` etc to support lazily-loaded AST #754 - -## 1.6.1 (28 May 2017) - -### New Features - -- `Query#selected_operation_name` returns the operation to execute, even if it was inferred (not provided as `operation_name:`) #746 - -### Bug fixes - -- Return `nil` from `Query#operation_name` if no `operation_name:` was provided #746 - -## 1.6.0 (27 May 2017) - -### Breaking changes - -- `InternalRepresentation::Node#return_type` will now return the wrapping type. Use `return_type.unwrap` to access the old value #704 -- `instrument(:query, ...)` instrumenters are applied as a stack instead of a queue #735. If you depend on queue-based behavior, move your `before_query` and `after_query` hooks to separate instrumenters. -- In a `Relay::Mutation`, Raising or returning a `GraphQL::Execution` will nullify the mutation field, not the field's children. #731 -- `args.to_h` returns a slightly different hash #714 - - keys are always `String`s - - if an argument is aliased with `as:`, the alias is used as the key -- `InternalRepresentation::Node#return_type` includes the original "wrapper" types (non-null or list types), call `.unwrap` to get the inner type #20 - - ```ruby - # before - irep_node.return_type - # after - irep_node.return_type.unwrap - ``` - -### Deprecations - -- Argument `prepare` functions which take one argument are deprecated #730 - - ```ruby - # before - argument :id, !types.ID, prepare: ->(val) { ... } - # after - argument :id, !types.ID, prepare: ->(val, ctx) { ... } - ``` - -### New features - -- `Schema#multiplex(queries)` runs multiple queries concurrently #691 -- `GraphQL::RakeTask` supports dumping the schema to IDL or JSON #687 -- Improved support for `Schema.from_definition` #699 : - - Custom scalars are supported with `coerce_input` and `coerce_result` functions - - `resolve_type` function will be used for abstract types - - Default resolve behavior is to check `obj` for a method and call it with 0, 1, or 2 arguments. -- `ctx.skip` may be returned from field resolve functions to exclude the field from the response entirely #688 -- `instrument(:field, ..., after_built_ins: true)` to apply field instrumentation after Relay wrappers #740 -- Argument `prepare` functions are invoked with `(val, ctx)` (previously, it was only `(val)`) #730 -- `args.to_h` returns stringified, aliased arguments #714 -- `ctx.namespace(:my_namespace)` provides namespaced key-value storage #689 -- `GraphQL::Query` can be initialized without a query_string; it can be added after initialization #710 -- Improved filter support #713 - - `Schema.execute(only:, except:)` accept a callable _or_ an array of callables (multiple filters) - - Filters can be added to a query via `Query#merge_filters(only:, except:)`. You can add a filter to every query by merging it in during query instrumentation. - -### Bug fixes - -- Correctly apply cursors and `max_page_size` in `Relay::RelationConnection` and `Relay::ArrayConnection` #728 -- Nullify a mutation field when it raises or returns an error #731 - -## 1.5.14 (27 May 2017) - -### New features - -- `UniqueWithinType` Relay ID generator supports `-` in the ID #742 -- `assign_metadata_key` assigns `true` when the definition method is called without arguments #724 -- Improved lexer performance #737 - -### Bug fixes - -- Assign proper `parent` when a `connection` resolve returns a promise #736 - -## 1.5.13 (11 May 2017) - -- Fix raising `ExecutionError` inside mutation resolve functions (it nullifies the field) #722 - -## 1.5.12 (9 May 2017) - -- Fix returning `nil` from connection resolve functions (now they become `null`) #719 -- Fix duplicate AST nodes when merging fragments #721 - -## 1.5.11 (8 May 2017) - -### New features - -- `Schema.from_definition` accepts a `parser:` option (to work around lack of schema parser in `graphql-libgraphqlparser`) #712 -- `Query#internal_representation` exposes an `InternalRepresentation::Document` #701 -- Update generator usage of `graphql-batch` #697 - -### Bug fixes - -- Handle fragments with the same name as operations #706 -- Fix type generator: ensure type name is camelized #718 -- Fix `Query#operation_name` to return the operation name #707 -- Fix pretty-print of non-null & list types #705 -- Fix single input objects passed to list-type arguments #716 - -## 1.5.10 (25 Apr 2017) - -### New features - -- Support Rails 5.1 #693 -- Fall back to `String#encode` for non-UTF-8/non-ASCII strings #676 - -### Bug Fixes - -- Correctly apply `Relay::Mutation`'s `return_field ... property:` argument #692 -- Handle Rails 5.1's `ActionController::Parameters` #693 - -## 1.5.9 (19 Apr 2017) - -### Bug Fixes - -- Include instrumentation-related changes in introspection result #681 - -## 1.5.8 (18 Apr 2017) - -### New features - -- Use Relay PageInfo descriptions from graphql-js #673 - -### Bug Fixes - -- Allow fields with different arguments when fragments are included within inline fragments of non-overlapping types #680 -- Run `lazy_resolve` instrumentation for `connection` fields #679 - -## 1.5.7 (14 Apr 2017) - -### Bug fixes - -- `InternalRepresentation::Node#definition` returns `nil` instead of raising NoMethodError for operation fields #675 -- `Field#function` is properly populated for fields derived from `GraphQL::Function`s #674 - -## 1.5.6 (9 Apr 2017) - -## Breaking Changes - -- Returned strings which aren't encoded as UTF-8 or ASCII will raise `GraphQL::StringEncodingError` instead of becoming `nil` #661 - - To preserve the previous behavior, Implement `Schema#type_error` to return `nil` for this error, eg: - - ```ruby - GraphQL::Schema.define do - type_error ->(err, ctx) { - case err - # ... - when GraphQL::StringEncodingError - nil - end - } - ``` - -- `coerce_non_null_input` and `validate_non_null_input` are private #667 - -## Deprecations - -- One-argument `coerce_input` and `coerce_result` functions for custom scalars are deprecated. #667 Those functions now accept a second argument, `ctx`. - - ```ruby - # From - ->(val) { val.to_i } - # To: - ->(val, ctx) { val.to_i } - ``` - -- Calling `coerce_result`, `coerce_input`, `valid_input?` or `validate_input` without a `ctx` is deprecated. #667 Use `coerce_isolated_result` `coerce_isolated_input`, `valid_isolated_input?`, `validate_input` to explicitly bypass `ctx`. - -## New Features - -- Include `#types` in `GraphQL::Function` #654 -- Accept `prepare:` function for arguments #646 -- Scalar coerce functions receive `ctx` #667 - -## Bug Fixes - -- Properly apply default values of `false` #658 -- Fix application of argument options in `GraphQL::Relay::Mutation` #660 -- Support concurrent-ruby `>1.0.0` #663 -- Only raise schema validation errors on `#execute` to avoid messing with Rails constant loading #665 - -## 1.5.5 (31 Mar 2017) - -### Bug Fixes - -- Improve threadsafety of `lazy_resolve` cache, use `Concurrent::Map` if it's available #631 -- Properly handle unexpeced input objects #638 -- Handle errors during definition by preseriving the definition #632 -- Fix `nil` input for nullable list types #637, #639 -- Handle invalid schema IDL with a validation error #647 -- Properly serialize input object default values #635 -- Fix `as:` on mutation `input_field` #650 -- Fix null propagation for `nil` members of non-null list types #649 - -## 1.5.4 (22 Mar 2017) - -### Breaking Changes - -- Stop supporting deprecated one-argument schema masks #616 - -### Bug Fixes - -- Return a client error for unknown variable types when default value is provided or when directives are present #627 -- Fix validation performance regression on nested abstract fragment conditions #622, #624 -- Put back `InternalRepresentation::Node#parent` and fix it for fragment fields #621 -- Ensure enum names are strings #619 - -## 1.5.3 (20 Mar 2017) - -### Bug Fixes - -- Fix infinite loop triggered by user input. #620 This query would cause an infinite loop: - - ```graphql - query { ...frag } - fragment frag on Query { __typename } - fragment frag on Query { ...frag } - ``` - -- Validate fragment name uniqueness #618 - -## 1.5.2 (16 Mar 2017) - -### Breaking Changes - -- Parse errors are no longer raised to the application. #607 Instead, they're returned to the client in the `"errors"` key. To preserve the previous behavior, you can implement `Schema#parse_error` to raise the error: - - ```ruby - MySchema = GraphQL::Schema.define do - # ... - parse_error ->(err, ctx) { raise(err) } - end - ``` - -### New Features - -- Add `graphq:enum` generator #611 -- Parse errors are returned to the client instead of raised #607 - -### Bug Fixes - -- Handle negative cursor pagination args as `0` #612 -- Properly handle returned `GraphQL::ExecutionError`s from connection resolves #610 -- Properly handle invalid nulls in lazy scalar fields #609 -- Properly handle invalid input objects passed to enum arguments #604 -- Fix introspection response of enum default values #605 -- Allow `Schema.from_definition` default resolver hashes to have defaults #608 - -## 1.5.1 (12 Mar 2017) - -### Bug fixes - -- Fix rewrite performance regressions from 1.5.0 #599 -- Remove unused `GraphQL::Execution::Lazy` initialization API #597 - -## 1.5.0 (10 Mar 2017), yanked - -### Breaking changes - -- _Only_ UTF-8-encoded strings will be returned by `String` fields. Strings with other encodings (or objects whose `#to_s` method returns a string with a different encoding) will return `nil` instead of that string. #517 - - To opt into the _previous_ behavior, you can modify `GraphQL::STRING_TYPE`: - - ```ruby - # app/graphql/my_schema.rb - # Restore previous string behavior: - GraphQL::STRING_TYPE.coerce_result = ->(value) { value.to_s } - - MySchema = GraphQL::Schema.define { ... } - ``` - -- Substantial changes to the internal query representation (#512, #536). Query analyzers may notice some changes: - - Nodes skipped by directives are not visited - - Nodes are always on object types, so `Node#owner_type` always returns an object type. (Interfaces and Unions are replaced with concrete object types which are valid in the current scope.) - - See [changes to `Analysis::QueryComplexity`](https://github.com/rmosolgo/graphql-ruby/compare/v1.4.5...v1.5.0#diff-8ff2cdf0fec46dfaab02363664d0d201) for an example migration. Here are some other specific changes: - - - Nodes are tracked on object types only, not interface or union types - - Deprecated, buggy `Node#children` and `Node#path` were removed - - Buggy `#included` was removed - - Nodes excluded by directives are entirely absent from the rewritten tree - - Internal `InternalRepresentation::Selection` was removed (no longer needed) - - `Node#spreads` was replaced by `Node#ast_spreads` which returns a Set - -### New features - -- `Schema#validate` returns a list of errors for a query string #513 -- `implements ...` adds interfaces to object types _without_ inherit-by-default #548, #574 -- `GraphQL::Relay::RangeAdd` for implementing `RANGE_ADD` mutations #587 -- `use ...` definition method for plugins #565 -- Rails generators #521, #580 -- `GraphQL::Function` for reusable resolve behavior with arguments & return type #545 -- Support for Ruby 2.4 #475 -- Relay `node` & `nodes` field can be extended with a custom block #552 -- Performance improvements: - - Resolve fragments only once when validating #504 - - Reuse `Arguments` objects #500 - - Skip needless `FieldResult`s #482 - - Remove overhead from `ensure_defined` #483 - - Benchmark & Profile tasks for gem maintenance #520, #579 - - Fetch `has_next_page` while fetching items in `RelationConnection` #556 - - Merge selections on concrete object types ahead of time #512 -- Support runnable schemas with `Schema.from_definition` #567, #584 - -### Bug fixes - -- Support different arguments on non-overlapping typed fragments #512 -- Don't include children of `@skip`ped nodes when parallel branches are not skipped #536 -- Fix offset in ArrayConnection when it's larger than the array #571 -- Add missing `frozen_string_literal` comments #589 - -## 1.4.5 (6 Mar 2017) - -### Bug Fixes - -- When an operation name is provided but no such operation is present, return an error (instead of executing the first operation) #563 -- Require unique operation names #563 -- Require selections on root type #563 -- If a non-null field returns `null`, don't resolve any more sibling fields. #575 - -## 1.4.4 (17 Feb 2017) - -### New features - -- `Relay::Node.field` and `Relay::Node.plural_field` accept a custom `resolve:` argument #550 -- `Relay::BaseConnection#context` provides access to the query context #537 -- Allow re-assigning `Field#name` #541 -- Support `return_interfaces` on `Relay::Mutation`s #533 -- `BaseType#to_definition` stringifies the type to IDL #539 -- `argument ... as:` can be used to alias an argument inside the resolve function #542 - -### Bug fixes - -- Fix negative offset from cursors on PostgresQL #510 -- Fix circular dependency issue on `.connection_type`s #535 -- Better error when `Relay::Mutation.resolve` doesn't return a Hash - -## 1.4.3 (8 Feb 2017) - -### New features - -- `GraphQL::Relay::Node.plural_field` finds multiple nodes by UUID #525 - -### Bug fixes - -- Properly handle errors from lazy mutation results #528 -- Encode all parsed strings as UTF-8 #516 -- Improve error messages #501 #519 - -## 1.4.2 (23 Jan 2017) - -### Bug fixes - -- Absent variables aren't present in `args` (_again_!) #494 -- Ensure definitions were executed when accessing `Field#resolve_proc` #502 (This could have caused errors when multiple instrumenters modified the same field in the schema.) - -## 1.4.1 (16 Jan 2017) - -### Bug fixes - -- Absent variables aren't present in `args` #479 -- Fix grouped ActiveRecord relation with `last` only #476 -- `Schema#default_mask` & query `only:`/`except:` are combined, not overridden #485 -- Root types can be hidden with dynamic filters #480 - -## 1.4.0 (8 Jan 2017) - -### Breaking changes - -### Deprecations - -- One-argument schema filters are deprecated. Schema filters are now called with _two_ arguments, `(member, ctx)`. #463 To update, add a second argument to your schema filter. -- The arity of middleware `#call` methods has changed. Instead of `next_middleware` being the last argument, it is passed as a block. To update, call `yield` to continue the middleware chain or use `&next_middleware` to capture `next_middleware` into a local variable. - - ```ruby - # Previous: - def call(*args, next_middleware) - next_middleware.call - end - - # Current - def call(*args) - yield - end - # Or - def call(*args, &next_middleware) - next_middleware.call - end - ``` - -### New features - -- You can add a `nodes` field directly to a connection. #451 That way you can say `{ friends { nodes } }` instead of `{ freinds { edges { node } } }`. Either pass `nodes_field: true` when defining a custom connection type, for example: - - ```ruby - FriendsConnectionType = FriendType.define_connection(nodes_field: true) - ``` - - Or, set `GraphQL::Relay::ConnectionType.default_nodes_field = true` before defining your schema, for example: - - ```ruby - GraphQL::Relay::ConnectionType.default_nodes_field = true - MySchema = GraphQL::Schema.define { ... } - ``` - -- Middleware performance was dramatically improved by reducing object allocations. #462 `next_middleware` is now passed as a block. In general, [`yield` is faster than calling a captured block](https://github.com/JuanitoFatas/fast-ruby#proccall-and-block-arguments-vs-yieldcode). -- Improve error messages for wrongly-typed variable values #423 -- Cache the value of `resolve_type` per object per query #462 -- Pass `ctx` to schema filters #463 -- Accept whitelist schema filters as `only:` #463 -- Add `Schema#to_definition` which accepts `only:/except:` to filter the schema when printing #463 -- Add `Schema#default_mask` as a default `except:` filter #463 -- Add reflection methods to types #473 - - `#introspection?` marks built-in introspection types - - `#default_scalar?` marks built-in scalars - - `#default_relay?` marks built-in Relay types - - `#default_directive?` marks built-in directives - -### Bug fixes - -- Fix ArrayConnection: gracefully handle out-of-bounds cursors #452 -- Fix ArrayConnection & RelationConnection: properly handle `last` without `before` #362 - -## 1.3.0 (8 Dec 2016) - -### Deprecations - -- As per the spec, `__` prefix is reserved for built-in names only. This is currently deprecated and will be invalid in a future version. #427, #450 - -### New features - -- `Schema#lazy_resolve` allows you to define handlers for a second pass of resolution #386 -- `Field#lazy_resolve` can be instrumented to track lazy resolution #429 -- `Schema#type_error` allows you to handle `InvalidNullError`s and `UnresolvedTypeErrors` in your own way #416 -- `Schema#cursor_encoder` can be specified for transforming cursors from built-in Connection implementations #345 -- Schema members `#dup` correctly: they shallowly copy their state into new instances #444 -- `Query#provided_variables` is now public #430 - -### Bug fixes - -- Schemas created from JSON or strings with custom scalars can validate queries (although they still can't check if inputs are valid for those custom scalars) #445 -- Always use `quirks_mode: true` when serializing values (to support non-stdlib `JSON`s) #449 -- Calling `#redefine` on a Schema member copies state outside of previous `#define` blocks (uses `#dup`) #444 - -## 1.2.6 (1 Dec 2016) - -### Bug fixes - -- Preserve connection behaviors after `redefine` #421 -- Implement `respond_to_missing?` on `DefinedObjectProxy` (which is `self` inside `.define { ... }`) #414 - -## 1.2.5 (22 Nov 2016) - -### Breaking changes - -- `Visitor` received some breaking changes, though these are largely-private APIs (#401): - - Global visitor hooks (`Visitor#enter` and `Visitor#leave`) have been removed - - Returning `SKIP` from a visitor hook no longer skips sibling nodes - -### New features - -- `Schema#instrument` may be called outside of `Schema.define` #399 -- Validation: assert that directives on a node are unique #409 -- `instrument(:query)` hooks are executed even if the query raises an error #412 - -### Bug fixes - -- `Mutation#input_fields` should trigger lazy definition #392 -- `ObjectType#connection` doesn't modify the provided `GraphQL::Field` #411 -- `Mutation#resolve` may return a `GraphQL::ExecutionError` #405 -- `Arguments` can handle nullable arguments passed as `nil` #410 - -## 1.2.4 (14 Nov 2016) - -### Bug fixes - -- For invalid enum values, print the enum name in the error message (not a Ruby object dump) #403 -- Improve detection of invalid UTF-8 escapes #394 - -## 1.2.3 (14 Nov 2016) - -### Bug fixes - -- `Lexer` previous token should be a local variable, not a method attribute #396 -- `Arguments` should wrap values according to their type, not their value #398 - -## 1.2.2 (7 Nov 2016) - -### New features - -- `Schema.execute` raises an error if `variables:` is a string - -### Bug fixes - -- Dynamic fields `__schema`, `__type` and `__typename` are properly validated #391 - -## 1.2.1 (7 Nov 2016) - -### Bug fixes - -- Implement `Query::Context#strategy` and `FieldResolutionContext#strategy` to support GraphQL::Batch #382 - -## 1.2.0 (7 Nov 2016) - -### Breaking changes - -- A breaking change from 1.1.0 was reverted: two-character `"\\u"` _is_ longer treated as the Unicode escape character #372 - -- Due to the execution bug described below, the internal representation of a query has changed. Although `Node` responds to the same methods, tree is built differently and query analyzers visit it differently. #373, #379 - - The difference is in cases like this: - - ```graphql - outer { - ... on A { inner1 { inner2 } } - ... on B { inner1 { inner3 } } - } - ``` - - Previously, visits would be: - - - `outer`, which has one child: - - `inner1`, which has two definitions (one on `A`, another on `B`), then visit its two `children`: - - `inner2` which has one definition (on the return type of `inner1`) - - `inner3` which has one definition (on the return type of `inner1`) - - This can be wrong for some cases. For example, if `A` and `B` are mutually exclusive (both object types, or union types with no shared members), then `inner2` and `inner3` will never be executed together. - - Now, the visit goes like this: - - - `outer` which has two entries in `typed_children`, one on `A` and another on `B`. Visit each `typed_chidren` branch: - - `inner1`, then its one `typed_children` branch: - - `inner2` - - `inner1`, then its one `typed_children` branch: - - `inner3` - - As you can see, we visit `inner1` twice, once for each type condition. `inner2` and `inner3` are no longer visited as siblings. Instead they're visited as ... cousins? (They share a grandparent, not a parent.) - - Although `Node#children` is still present, it may not contain all children actually resolved at runtime, since multiple `typed_children` branches could apply to the same runtime type (eg, two branches on interface types can apply to the same object type). To track all children, you have to do some bookkeeping during visitation, see `QueryComplexity` for an example. - - You can see PR #373 for how built-in analyzers were changed to reflect this. - -### Deprecations - -- `InternalRepresentation::Node#children` and `InternalRepresentation::Node#definitions` are deprecated due to the bug described below and the breaking change described above. Instead, use `InternalRepresentation::Node#typed_children` and `InternalRepresentation::Node#definition`. #373 - -### New features - -- `null` support for the whole library: as a query literal, variable value, and argument default value. To check for the presence of a nullable, use `Arguments#key?` #369 - -- `GraphQL::Schema::UniqueWithinType.default_id_separator` may be assigned to a custom value #381 - -- `Context#add_error(err)` may be used to add a `GraphQL::ExecutionError` to the response's `"errors"` key (and the resolve function can still return a value) #367 - -- The third argument of `resolve` is now a `FieldResolutionContext`, which behaves just like a `Query::Context`, except that it is not modified during query execution. This means you can capture a reference to that context and access some field-level details after the fact: `#path`, `#ast_node`, `#irep_node`. (Other methods are delegated to the underlying `Query::Context`) #379 - -- `TimeoutMiddleware`'s second argument is a _proxied_ query object: it's `#context` method returns the `FieldResolutionContext` (see above) for the timed-out field. Other methods are delegated to the underlying `Query` #379 - -### Bug fixes - -- Fix deep selection merging on divergently-typed fragments. #370, #373, #379 Previously, nested selections on different fragments were not distinguished. Consider a case like this: - - ```graphql - ... on A { inner1 { inner2 } } - ... on B { inner1 { inner3 } } - ``` - - Previously, an object of type `A` would resolve `inner1`, then the result would receive _both_ `inner2` and `inner3`. The same was true for an object of type `B`. - - Now, those are properly distinguished. An object of type `A` resolves `inner1`, then its result receives `inner2`. An object of type `B` receives `inner1`, then `inner3`. - -## 1.1.0 (1 Nov 2016) - -### Breaking changes - -- Two-character `"\\u"` is no longer treated as the Unicode escape character, only the Unicode escape character `"\u"` is treated that way. (This behavior was a bug, the migration path is to use the Unicode escape character.) #366 -- `GraphQL::Language::ParserTests` was removed, use `GraphQL::Compatibility` instead. #366 -- Non-null arguments can't be defined with default values, because those values would never be used #361 - -### New features - -- `Schema.from_definition(definition_string)` builds a `GraphQL::Schema` out of a schema definition. #346 -- Schema members (types, fields, arguments, enum values) can be hidden on a per-query basis with the `except:` option #300 -- `GraphQL::Compatibility` contains `.build_suite` functions for testing user-provided parsers and execution strategies with GraphQL internals. #366 -- Schema members respond to `#redefine { ... }` for making shallow copies with extended definitions. #357 -- `Schema#instrument` provides an avenue for observing query and field resolution with no overhead. -- Some `SerialExecution` objects were converted to functions, resulting in a modest performance improvement for query resolution. - -### Bug fixes - -- `NonNullType` and `ListType` have no name (`nil`), as per the spec #355 -- Non-null arguments can't be defined with default values, because those values would never be used #361 - -## 1.0.0 (25 Oct 2016) - -### Breaking changes - -- `validate: false` option removed from `Schema.execute` (it didn't work anyways) #338 -- Some deprecated methods were removed: #349 - - `BaseConnection#object` was removed, use `BaseConnection#nodes` - - `BaseConnection.connection_for_items` was removed, use `BaseConnection#connection_for_nodes` - - Two-argument resolve functions for `Relay::Mutation`s are not supported, use three arguments instead: `(root_obj, input, ctx)` - - `Schema.new` no longer accepts initialization options, use `Schema.define` instead - - `GraphQL::ObjectType::UnresolvedTypeError` was removed, use `GraphQL::UnresolvedTypeError` instead -- Fragment type conditions should be parsed as `TypeName` nodes, not strings. (Users of `graphql-libgraphqlparser` should update to `1.0.0` of that gem.) #342 - -### New Features - -- Set `ast_node` and `irep_node` on query context before sending it to middleware #348 -- Enum values can be extended with `.define` #341 - -### Bug Fixes - -- Use `RelationConnection` for Rails 3 relations (which also extend `Array`) #343 -- Fix schema printout when arguments have comments #335 - -## 0.19.4 (18 Oct 2016) - -### Breaking changes - -- `Relay::BaseConnection#order` was removed (it always returned `nil`) #313 -- In the IDL, Interface names & Union members are parsed as `TypeName` nodes instead of Strings #322 - -### New features - -- Print and parse descriptions in the IDL #305 -- Schema roots from IDL are omitted when their names match convention #320 -- Don't add `rescue_middleware` to a schema if it's not using `rescue_from` #328 -- `Query::Arguments#each_value` yields `Query::Argument::ArgumentValue` instances which contain key, value and argument definition #331 - -### Bug fixes - -- Use `JSON.generate(val, quirks_mode: true)` for compatibility with other JSON implementations #316 -- Improvements for compatibility with 1.9.3 branch #315 #314 #313 -- Raise a descriptive error when calculating a `cursor` for a node which isn't present in the connection's members #327 - -## 0.19.3 (13 Oct 2016) - -### Breaking Changes - -- `GraphQL::Query::Arguments.new` requires `argument_definitions:` of type `{String => GraphQL::Argument }` #304 - -### Deprecations - -- `Relay::Mutation#resolve` has a new signature. #301 - - Previously, it was called with two arguments: - - ```ruby - resolve ->(inputs, ctx) { ... } - ``` - - Now, it's called with three inputs: - - ```ruby - resolve ->(obj, inputs, ctx) { ... } - ``` - - `obj` is the value of `root_value:` given to `Schema#execute`, as with other root-level fields. - - Two-argument resolvers are still supported, but they are deprecated and will be removed in a future version. - -### New features - -- `Relay::Mutation` accepts a user-defined `return_type` #310 -- `Relay::Mutation#resolve` receives the `root_value` passed to `Schema#execute` #301 -- Derived `Relay` objects have descriptions #303 - -### Bug fixes - -- Introspection query is 7 levels deep instead of 3 #308 -- Unknown variable types cause validation errors, not runtime errors #310 -- `Query::Arguments` doesn't wrap hashes from parsed scalars (fix for user-defined "JSONScalar") #304 - -## 0.19.2 (6 Oct 2016) - -### New features - -- If a list entry has a `GraphQL::ExecutionError`, replace the entry with `nil` and return the error #295 - -### Bug fixes - -- Support graphql-batch rescuing `InvalidNullError`s #296 -- Schema printer prints Enum names, not Ruby values for enums #297 - -## 0.19.1 (4 Oct 2016) - -### Breaking changes - -- Previously-deprecated `InterfaceType#resolve_type` hook has been removed, use `Schema#resolve_type` instead #290 - -### New features - -- Eager-load schemas at definition time, validating types & schema-level hooks #289 -- `InvalidNullError`s contain the type & field name that returned null #293 -- If an object is resolved with `Schema#resolve_type` and the resulting type is not a member of the expected possible types, raise an error #291 - -### Bug fixes - -- Allow `directive` as field or argument name #288 - -## 0.19.0 (30 Sep 2016) - -### Breaking changes - -- `GraphQL::Relay::GlobalNodeIdentification` was removed. Its features were moved to `GraphQL::Schema` or `GraphQL::Relay::Node`. The new hooks support more robust & flexible global IDs. #243 - - - Relay's `"Node"` interface and `node(id: "...")` field were both moved to `GraphQL::Relay::Node`. To use them in your schema, call `.field` and `.interface`. For example: - - ```ruby - # Adding a Relay-compliant `node` field: - field :node, GraphQL::Relay::Node.field - ``` - - ```ruby - # This object type implements Relay's `Node` interface: - interfaces [GraphQL::Relay::Node.interface] - ``` - - - UUID hooks were renamed and moved to `GraphQL::Schema`. You should define `id_from_object` and `object_from_id` in your `Schema.define { ... }` block. For example: - - ```ruby - MySchema = GraphQL::Schema.define do - # Fetch an object by UUID - object_from_id ->(id, ctx) { - MyApp::RelayLookup.find(id) - } - # Generate a UUID for this object - id_from_object ->(obj, type_defn, ctx) { - MyApp::RelayLookup.to_id(obj) - } - end - ``` - - - The new hooks have no default implementation. To use the previous default, use `GraphQL::Schema::UniqueWithinType`, for example: - - ```ruby - MySchema = GraphQL::Schema.define do - object_from_id ->(id, ctx) { - # Break the id into its parts: - type_name, object_id = GraphQL::Schema::UniqueWithinType.decode(id) - # Fetch the identified object - # ... - } - - id_from_object ->(obj, type_defn, ctx) { - # Provide the type name & the object's `id`: - GraphQL::Schema::UniqueWithinType.encode(type_defn.name, obj.id) - } - end - ``` - - If you were using a custom `id_separator`, it's now accepted as an input to `UniqueWithinType`'s methods, as `separator:`. For example: - - ```ruby - # use "---" as a ID separator - GraphQL::Schema::UniqueWithinType.encode(type_name, object_id, separator: "---") - GraphQL::Schema::UniqueWithinType.decode(relay_id, separator: "---") - ``` - - - `type_from_object` was previously deprecated and has been replaced by `Schema#resolve_type`. You should define this hook in your schema to return a type definition for a given object: - - ```ruby - MySchema = GraphQL::Schema.define do - # ... - resolve_type ->(obj, ctx) { - # based on `obj` and `ctx`, - # figure out which GraphQL type to use - # and return the type - } - end - ``` - - - `Schema#node_identification` has been removed. - -- `Argument` default values have been changed to be consistent with `InputObjectType` default values. #267 - - Previously, arguments expected GraphQL values as `default_value`s. Now, they expect application values. (`InputObjectType`s always worked this way.) - - Consider an enum like this one, where custom values are provided: - - ```ruby - PowerStateEnum = GraphQL::EnumType.define do - name "PowerState" - value("ON", value: 1) - value("OFF", value: 0) - end - ``` - - __Previously__, enum _names_ were provided as default values, for example: - - ```ruby - field :setPowerState, PowerStateEnum do - # Previously, the string name went here: - argument :newValue, default_value: "ON" - end - ``` - - __Now__, enum _values_ are provided as default values, for example: - - ```ruby - field :setPowerState, PowerStateEnum do - # Now, use the application value as `default_value`: - argument :newValue, default_value: 1 - end - ``` - - Note that if you __don't have custom values__, then there's no change, because the name and value are the same. - - Here are types that are affected by this change: - - - Custom scalars (previously, the `default_value` was a string, now it should be the application value, eg `Date` or `BigDecimal`) - - Enums with custom `value:`s (previously, the `default_value` was the name, now it's the value) - - If you can't replace `default_value`s, you can also use a type's `#coerce_input` method to translate a GraphQL value into an application value. For example: - - ```ruby - # Using a custom scalar, "Date" - # PREVIOUSLY, provide a string: - argument :starts_on, DateType, default_value: "2016-01-01" - # NOW, transform the string into a Date: - argument :starts_on, DateType, default_value: DateType.coerce_input("2016-01-01") - ``` - -### New features - -- Support `@deprecated` in the Schema language #275 -- Support `directive` definitions in the Schema language #280 -- Use the same introspection field descriptions as `graphql-js` #284 - -### Bug fixes - -- Operation name is no longer present in execution error `"path"` values #276 -- Default values are correctly dumped & reloaded in the Schema language #267 - -## 0.18.15 (20 Sep 2016) - -### Breaking changes - -- Validation errors no longer have a `"path"` key in their JSON. It was renamed to `"fields"` #264 -- `@skip` and `@include` over multiple selections are handled according to the spec: if the same field is selected multiple times and _one or more_ of them would be included, the field will be present in the response. Previously, if _one or more_ of them would be skipped, it was absent from the response. #256 - -### New features - -- Execution errors include a `"path"` key which points to the field in the response where the error occurred. #259 -- Parsing directives from the Schema language is now supported #273 - -### Bug fixes - -- `@skip` and `@include` over multiple selections are now handled according to the spec #256 - -## 0.18.14 (20 Sep 2016) - -### Breaking changes - -- Directives are no longer considered as "conflicts" in query validation. This is in conformity with the spec, but a change for graphql-ruby #263 - -### Features - -- Query analyzers may emit errors by raising `GraphQL::AnalysisError`s during `#call` or returning a single error or an array of errors from `#final_value` #262 - -### Bug fixes - -- Merge fields even when `@skip` / `@include` are not identical #263 -- Fix possible infinite loop in `FieldsWillMerge` validation #261 - -## 0.18.13 (19 Sep 2016) - -### Bug fixes - -- Find infinite loops in nested contexts, too #258 - -## 0.18.12 (19 Sep 2016) - -### New features - -- `GraphQL::Analysis::FieldUsage` can be used to check for deprecated fields in the query analysis phase #245 - -### Bug fixes - -- If a schema receives a query on `mutation` or `subscription` but that root doesn't exist, return a validation error #254 -- `Query::Arguments#to_h` only includes keys that were provided in the query or have a default value #251 - -## 0.18.11 (11 Sep 2016) - -### New features - -- `GraphQL::Language::Nodes::Document#slice(operation_name)` finds that operation and its dependencies and puts them in a new `Document` #241 - -### Bug fixes - -- Validation errors for non-existent fields have the location of the field usage, not the parent field #247 -- Properly `require "forwardable"` #242 -- Remove `ALLOWED_CONSTANTS` for boolean input, use a plain comparison #240 - -## 0.18.10 (9 Sep 2016) - -### New features - -- Assign `#mutation` on objects which are derived from a `Relay::Mutation` #239 - -## 0.18.9 (6 Sep 2016) - -### Bug fixes - -- fix backward compatibility for `type_from_object` #238 - -## 0.18.8 (6 Sep 2016) - -### New features - -- AST nodes now respond to `#eql?(other)` to test value equality #231 - -### Bug fixes - -- The `connection` helper no longer adds a duplicate field #235 - -## 0.18.7 (6 Sep 2016) - -### New features - -- Support parsing nameless fragments (but not executing them) #232 - -### Bug fixes - -- Allow `__type(name: "Whatever")` to return null, as per the spec #233 -- Include a Relay mutation's description with a mutation field #225 - -## 0.18.6 (29 Aug 2016) - -### New features - -- ` GraphQL::Schema::Loader.load(schema_json)` turns an introspection result into a `GraphQL::Schema` #207 -- `.define` accepts plural definitions for: object fields, interface fields field arguments, enum values #222 - -## 0.18.5 (27 Aug 2016) - -### Deprecations - -- `Schema.new` is deprecated; use `Schema.define` instead. - - Before: - - ```ruby - schema = GraphQL::Schema.new( - query: QueryType, - mutation: MutationType, - max_complexity: 100, - types: [ExtraType, OtherType] - ) - schema.node_identification = MyGlobalID - schema.rescue_from(ActiveRecord::RecordNotFound) { |err| "..." } - ``` - - After: - - ```ruby - schema = GraphQL::Schema.define do - query QueryType - mutation MutationType - max_complexity 100 - node_identification MyGlobalID - rescue_from(ActiveRecord::RecordNotFound) { |err| "..." } - # Types was renamed to `orphan_types` to avoid conflict with the `types` helper - orphan_types [ExtraType, OtherType] - end - ``` - - This unifies the disparate methods of configuring a schema and provides new, more flexible design space. It also adds `#metadata` to schemas for user-defined storage. - -- `UnionType#resolve_type`, `InterfaceType#resolve_type`, and `GlobalNodeIdentification#type_from_object` are deprecated, unify them into `Schema#resolve_type` instead. - - Before: - - ```ruby - GraphQL::Relay::GlobalNodeIdentification.define do - type_from_object ->(obj) { ... } - end - - GraphQL::InterfaceType.define do - resolve_type ->(obj, ctx) { ... } - end - ``` - - After: - - ```ruby - GraphQL::Schema.define do - resolve_type ->(obj, ctx) { ... } - end - ``` - - This simplifies type inference and prevents unexpected behavior when different parts of the schema resolve types differently. - -### New features - -- Include expected type in Argument errors #221 -- Define schemas with `Schema.define` #208 -- Define a global object-to-type function with `Schema#resolve_type` #216 - -### Bug fixes - -## 0.18.4 (25 Aug 2016) - -### New features - -- `InvalidNullError`s expose a proper `#message` #217 - -### Bug fixes - -- Return an empty result for queries with no operations #219 - -## 0.18.3 (22 Aug 2016) - -### Bug fixes - -- `Connection.new(:field)` is optional, not required #215 -- 0.18.2 introduced a more restrictive approach to resolving interfaces & unions; revert that approach #212 - -## 0.18.2 (17 Aug 2016) - -### New features - -- Connection objects expose the `GraphQL::Field` that created them via `Connection#field` #206 - -## 0.18.1 (7 Aug 2016) - -### Deprecations - -- Unify `Relay` naming around `nodes` as the items of a connection: - - `Relay::BaseConnection.connection_for_nodes` replaces `Relay::BaseConnection.connection_for_items` - - `Relay::BaseConnection#nodes` replaces `Relay::BaseConnection#object` - -### New features - -- Connection fields' `.resolve_proc` is an instance of `Relay::ConnectionResolve` #204 -- Types, fields and arguments can store arbitrary values in their `metadata` hashes #203 - -## 0.18.0 (4 Aug 2016) - -### Breaking changes - -- `graphql-relay` has been merged with `graphql`, you should remove `graphql-relay` from your gemfile. #195 - -### Deprecations - -### New features - -- `GraphQL.parse` can turn schema definitions into a `GraphQL::Language::Nodes::Document`. The document can be stringified again with `Document#to_query_string` #191 -- Validation errors include a `path` to the part of the query where the error was found #198 -- `.define` also accepts keywords for each helper method, eg `GraphQL::ObjectType.define(name: "PostType", ...)` - -### Bug fixes - -- `global_id_field`s have default complexity of 1, not `nil` -- Relay `pageInfo` is correct for connections limited by `max_page_size` -- Rescue invalid variable errors & missing operation name errors during query analysis - -## 0.17.2 (26 Jul 2016) - -### Bug fixes - -- Correctly spread fragments when nested inside other fragments #194 - -## 0.17.1 (26 Jul 2016) - -### Bug fixes - -- Fix `InternalRepresentation::Node#inspect` - -## 0.17.0 (21 Jul 2016) - -### Breaking changes - -- `InternalRepresentation::Node` API changes: - - - `#definition_name` returns the field name on field nodes (while `#name` may have an alias) - - `#definitions` returns `{type => field}` pairs for possible fields on this node - - `#definition` is gone, it is equivalent to `node.definitions.values.first` - - `#on_types` is gone, it is equivalent to `node.definitions.keys` - -### New features - -- Accept `hash_key:` field option -- Call `.define { }` block lazily, so `-> { }` is not needed for circular references #182 - -### Bug fixes - -- Support `on` as an Enum value -- If the same field is requested on multiple types, choose the maximum complexity among them (not the first) - -## 0.16.1 (20 Jul 2016) - -### Bug fixes - -- Fix merging fragments on Union types (see #190, broken from #180) - -## 0.16.0 (14 Jul 2016) - -### Breaking changes & deprecations - -- I don't _know_ that this breaks anything, but `GraphQL::Query::SerialExecution` now iterates over a tree of `GraphQL::InternalRepresentation::Node`s instead of an AST (`GraphQL::Language::Nodes::Document`). - -### New features - -- Query context keys can be assigned with `Context#[]=` #178 -- Cancel further field resolution with `TimeoutMiddleware` #179 -- Add `GraphQL::InternalRepresentation` for normalizing queries from AST #180 -- Analyze the query before running it #180 -- Assign complexity cost to fields, enforce max complexity before running it #180 -- Log max complexity or max depth with `MaxComplexity` or `MaxDepth` analyzers #180 -- Query context exposes `#irep_node`, the internal representation of the current node #180 - -### Bug fixes - -- Non-null errors are propagated to the next nullable field, all the way up to `data` #174 - -## 0.15.3 (28 Jun 2016) - -### New features - -- `EnumValue`s can receive their properties after instantiation #171 - -## 0.15.2 (16 Jun 2016) - -### New features - -- Support lazy type arguments in Object's `interfaces` and Union's `possible_types` #169 - -### Bug fixes - -- Support single-member Unions, as per the spec #170 - -## 0.15.1 (15 Jun 2016) - -### Bug fixes - -- Whitelist operation types in `lexer.rb` - -## 0.15.0 (11 Jun 2016) - -### Breaking changes & deprecations - -- Remove `debug:` option, propagate all errors. #161 - -## 0.14.1 (11 Jun 2016) - -### Breaking changes & deprecations - -- `debug:` is deprecated (#165). Propagating errors (`debug: true`) will become the default behavior. You can get a similar implementation of error gobbling with `CatchallMiddleware`. Add it to your schema: - - ```ruby - MySchema.middleware << GraphQL::Schema::CatchallMiddleware - ``` - -### New features - -### Bug fixes - -- Restore previous introspection fields on DirectiveType as deprecated #164 -- Apply coercion to input default values #162 -- Proper Enum behavior when a value isn't found - -## 0.14.0 (31 May 2016) - -### Breaking changes & deprecations - -### New features - -- `GraphQL::Language::Nodes::Document#to_query_string` will re-serialize a query AST #151 -- Accept `root_value:` when running a query #157 -- Accept a `GraphQL::Language::Nodes::Document` to `Query.new` (this allows you to cache parsed queries on the server) #152 - -### Bug fixes - -- Improved parse error messages #149 -- Improved build-time validation #150 -- Raise a meaningful error when a Union or Interface can't be resolved during query execution #155 - -## 0.13.0 (29 Apr 2016) - -### Breaking changes & deprecations - -- "Dangling" object types are not loaded into the schema. The must be passed in `GraphQL::Schema.new(types: [...])`. (This was deprecated in 0.12.1) - -### New features - -- Update directive introspection to new spec #121 -- Improved schema validation errors #113 -- 20x faster parsing #119 -- Support inline fragments without type condition #123 -- Support multiple schemas composed of the same types #142 -- Accept argument `description` and `default_value` in the block #138 -- Middlewares can send _new_ arguments to subsequent middlewares #129 - -### Bug fixes - -- Don't leak details of internal errors #120 -- Default query `context` to `{}` #133 -- Fixed list nullability validation #131 -- Ensure field names are strings #128 -- Fix `@skip` and `@include` implementation #124 -- Interface membership is not shared between schemas #142 - -## 0.12.1 (26 Apr 2016) - -### Breaking changes & deprecations - -- __Connecting object types to the schema _only_ via interfaces is deprecated.__ It will be unsupported in the next version of `graphql`. - - Sometimes, object type is only connected to the Query (or Mutation) root by being a member of an interface. In these cases, bugs happen, especially with Rails development mode. (And sometimes, the bugs don't appear until you deploy to a production environment!) - - So, in a case like this: - - ```ruby - HatInterface = GraphQL::ObjectType.define do - # ... - end - - FezType = GraphQL::ObjectType.define do - # ... - interfaces [HatInterface] - end - - QueryType = GraphQL::ObjectType.define do - field :randomHat, HatInterface # ... - end - ``` - - `FezType` can only be discovered by `QueryType` _through_ `HatInterface`. If `fez_type.rb` hasn't been loaded by Rails, `HatInterface.possible_types` will be empty! - - Now, `FezType` must be passed to the schema explicitly: - - ```ruby - Schema.new( - # ... - types: [FezType] - ) - ``` - - Since the type is passed directly to the schema, it will be loaded right away! - -### New features - -### Bug fixes - -## 0.12.0 (20 Mar 2016) - -### Breaking changes & deprecations - -- `GraphQL::DefinitionConfig` was replaced by `GraphQL::Define` #116 -- Many scalar types are more picky about which inputs they allow (#115). To get the previous behavior, add this to your program: - - ```ruby - # Previous coerce behavior for scalars: - GraphQL::BOOLEAN_TYPE.coerce = ->(value) { !!value } - GraphQL::ID_TYPE.coerce = ->(value) { value.to_s } - GraphQL::STRING_TYPE.coerce = ->(value) { value.to_s } - # INT_TYPE and FLOAT_TYPE were unchanged - ``` - -- `GraphQL::Field`s can't be renamed because `#resolve` may depend on that name. (This was only a problem if you pass the _same_ `GraphQL::Field` instance to `field ... field:` definitions.) -- `GraphQL::Query::DEFAULT_RESOLVE` was removed. `GraphQL::Field#resolve` handles that behavior. - -### New features - -- Can override `max_depth:` from `Schema#execute` -- Base `GraphQL::Error` for all graphql-related errors - -### Bug fixes - -- Include `""` for String default values (so it's encoded as a GraphQL string literal) - -## 0.11.1 (6 Mar 2016) - -### New features - -- Schema `max_depth:` option #110 -- Improved validation errors for input objects #104 -- Interfaces provide field implementations to object types #108 - -## 0.11.0 (28 Feb 2016) - -### Breaking changes & deprecations - -- `GraphQL::Query::BaseExecution` was removed, you should probably extend `SerialExecution` instead #96 -- `GraphQL::Language::Nodes` members no longer raise if they don't get inputs during `initialize` #92 -- `GraphQL.parse` no longer accepts `as:` for parsing partial queries. #92 - -### New features - -- `Field#property` & `Field#property=` can be used to access & modify the method that will be sent to the underlying object when resolving a field #88 -- When defining a field, you can pass a string for as `type`. It will be looked up in the global namespace. -- `Query::Arguments#to_h` unwraps `Arguments` objects recursively -- If you raise `GraphQL::ExecutionError` during field resolution, it will be rescued and the message will be added to the response's `errors` key. #93 -- Raise an error when non-null fields are `nil` #94 - -### Bug fixes - -- Accept Rails params as input objects -- Don't get a runtime error when input contains unknown key #100 - -## 0.10.9 (15 Jan 2016) - -### Bug fixes - -- Handle re-assignment of `ObjectType#interfaces` #84 -- Fix merging queries on interface-typed fields #85 - -## 0.10.8 (14 Jan 2016) - -### Bug fixes - -- Fix transform of nested lists #79 -- Fix parse & transform of escaped characters #83 - -## 0.10.7 (22 Dec 2015) - -### New features - -- Support Rubinius - -### Bug fixes - -- Coerce values into one-item lists for ListTypes - -## 0.10.6 (20 Dec 2015) - -### Bug fixes - -- Remove leftover `puts`es - -## 0.10.5 (19 Dec 2015) - -### Bug fixes - -- Accept enum value description in definition #71 -- Correctly parse empty input objects #75 -- Correctly parse arguments preceded by newline -- Find undefined input object keys during static validation - -## 0.10.4 (24 Nov 2015) - -### New features - -- Add `Arguments#to_h` #66 - -### Bug fixes - -- Accept argument description in definition -- Correctly parse empty lists - -## 0.10.3 (11 Nov 2015) - -### New features - -- Support root-level `subscription` type - -### Bug fixes - -- Require Set for Schema::Printer - -## 0.10.2 (10 Nov 2015) - -### Bug fixes - -- Handle blank strings in queries -- Raise if a field is configured without a proper type #61 - -## 0.10.1 (22 Oct 2015) - -### Bug fixes - -- Properly merge fields on fragments within fragments -- Properly delegate enumerable-ish methods on `Arguments` #56 -- Fix & refactor literal coersion & validation #53 - -## 0.10.0 (17 Oct 2015) - -### New features - -- Scalars can have distinct `coerce_input` and `coerce_result` methods #48 -- Operations don't require a name #54 - -### Bug fixes - -- Big refactors and fixes to variables and arguments: - - Correctly apply argument default values - - Correctly apply variable default values - - Raise at execution-time if non-null variables are missing - - Incoming values are coerced to their proper types before execution - -## 0.9.5 (1 Oct 2015) - -### New features - -- Add `Schema#middleware` to wrap field access -- Add `RescueMiddleware` to handle errors during field execution -- Add `Schema::Printer` for printing the schema definition #45 - -### Bug fixes - -## 0.9.4 (22 Sept 2015) - -### New features - -- Fields can return `GraphQL::ExecutionError`s to add errors to the response - -### Bug fixes - -- Fix resolution of union types in some queries #41 - -## 0.9.3 (15 Sept 2015) - -### New features - -- Add `Schema#execute` shorthand for running queries -- Merge identical fields in fragments so they're only resolved once #34 -- An error during parsing raises `GraphQL::ParseError` #33 - -### Bug fixes - -- Find nested input types in `TypeReducer` #35 -- Find variable usages inside fragments during static validation - -## 0.9.2, 0.9.1 (10 Sept 2015) - -### Bug fixes - -- remove Celluloid dependency - -## 0.9.0 (10 Sept 2015) - -### Breaking changes & deprecations - -- remove `GraphQL::Query::ParallelExecution` (use [`graphql-parallel`](https://github.com/rmosolgo/graphql-parallel)) - -## 0.8.1 (10 Sept 2015) - -### Breaking changes & deprecations - -- `GraphQL::Query::ParallelExecution` has been extracted to [`graphql-parallel`](https://github.com/rmosolgo/graphql-parallel) - -## 0.8.0 (4 Sept 2015) - -### New features - -- Async field resolution with `context.async { ... }` -- Access AST node during resolve with `context.ast_node` - -### Bug fixes - -- Fix for validating arguments returning up too soon -- Raise if you try to define 2 types with the same name -- Raise if you try to get a type by name but it doesn't exist - -## 0.7.1 (27 Aug 2015) - -### Bug fixes - -- Merge nested results from different fragments instead of using the latest one only - -## 0.7.0 (26 Aug 2015) - -### Breaking changes & deprecations - -- Query keyword argument `params:` was removed, use `variables:` instead. - -### Bug fixes - -- `@skip` has precedence over `@include` -- Handle when `DEFAULT_RESOVE` returns nil - -## 0.6.2 (20 Aug 2015) - -### Bug fixes - -- Fix whitespace parsing in input objects - -## 0.6.1 (16 Aug 2015) - -### New features - -- Parse UTF-8 characters & escaped characters - -### Bug fixes - -- Properly parse empty strings -- Fix argument / variable compatibility validation - -## 0.6.0 (14 Aug 2015) - -### Breaking changes & deprecations - -- Deprecate `params` option to `Query#new` in favor of `variables` -- Deprecated `.new { |obj, types, fields, args| }` API was removed (use `.define`) - -### New features - -- `Query#new` accepts `operation_name` argument -- `InterfaceType` and `UnionType` accept `resolve_type` configs - -### Bug fixes - -- Gracefully handle blank-string & whitespace-only queries -- Handle lists in variable definitions and arguments -- Handle non-null input types - -## 0.5.0 (12 Aug 2015) - -### Breaking changes & deprecations - -- Deprecate definition API that yielded a bunch of helpers #18 - -### New features - -- Add new definition API #18 diff --git a/vendor/gems/graphql/CNAME b/vendor/gems/graphql/CNAME deleted file mode 100644 index fbf0dd845de..00000000000 --- a/vendor/gems/graphql/CNAME +++ /dev/null @@ -1 +0,0 @@ -graphql-ruby.org diff --git a/vendor/gems/graphql/Gemfile b/vendor/gems/graphql/Gemfile deleted file mode 100644 index 69aca9d35af..00000000000 --- a/vendor/gems/graphql/Gemfile +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -source "https://rubygems.org" - -gemspec - -gem 'bootsnap' # required by the Rails apps generated in tests -gem 'stackprof', platform: :ruby -gem 'pry' -gem 'pry-stack_explorer', platform: :ruby -gem 'pry-byebug' - -if RUBY_VERSION >= "3.0" - gem "libev_scheduler" - gem "evt" -end - -if RUBY_VERSION >= "3.1.1" - gem "async", "~>2.0" -end - -# Required for running `jekyll algolia ...` (via `rake site:update_search_index`) -group :jekyll_plugins do - gem 'jekyll-algolia', '~> 1.0' - gem 'jekyll-redirect-from' -end diff --git a/vendor/gems/graphql/Gemfile.lock b/vendor/gems/graphql/Gemfile.lock deleted file mode 100644 index 34fe50d3c73..00000000000 --- a/vendor/gems/graphql/Gemfile.lock +++ /dev/null @@ -1,324 +0,0 @@ -PATH - remote: . - specs: - graphql (2.4.11) - base64 - fiber-storage - logger - -GEM - remote: https://rubygems.org/ - specs: - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - algolia_html_extractor (2.6.4) - json (~> 2.0) - nokogiri (~> 1.10) - algoliasearch (1.27.5) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) - ansi (1.5.0) - ast (2.4.2) - async (2.23.0) - console (~> 1.29) - fiber-annotation - io-event (~> 1.9) - metrics (~> 0.12) - traces (~> 0.15) - base64 (0.2.0) - benchmark-ips (2.14.0) - bigdecimal (3.1.9) - binding_of_caller (1.0.1) - debug_inspector (>= 1.2.0) - bootsnap (1.18.4) - msgpack (~> 1.2) - builder (3.3.0) - byebug (11.1.3) - coderay (1.1.3) - colorator (1.1.0) - concurrent-ruby (1.3.5) - console (1.30.0) - fiber-annotation - fiber-local (~> 1.1) - json - csv (3.3.2) - debug_inspector (1.2.0) - docile (1.4.1) - em-websocket (0.5.3) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0) - eventmachine (1.2.7) - evt (0.4.0) - faraday (2.12.2) - faraday-net_http (>= 2.0, < 3.5) - json - logger - faraday-net_http (3.4.0) - net-http (>= 0.5.0) - ffi (1.17.1-aarch64-linux-gnu) - ffi (1.17.1-aarch64-linux-musl) - ffi (1.17.1-arm-linux-gnu) - ffi (1.17.1-arm-linux-musl) - ffi (1.17.1-arm64-darwin) - ffi (1.17.1-x86_64-darwin) - ffi (1.17.1-x86_64-linux-gnu) - ffi (1.17.1-x86_64-linux-musl) - fiber-annotation (0.2.0) - fiber-local (1.1.0) - fiber-storage - fiber-storage (1.0.0) - filesize (0.2.0) - forwardable-extended (2.6.0) - gitlab (4.20.1) - httparty (~> 0.20) - terminal-table (>= 1.5.1) - google-protobuf (4.30.0) - bigdecimal - rake (>= 13) - google-protobuf (4.30.0-aarch64-linux) - bigdecimal - rake (>= 13) - google-protobuf (4.30.0-arm64-darwin) - bigdecimal - rake (>= 13) - google-protobuf (4.30.0-x86_64-darwin) - bigdecimal - rake (>= 13) - google-protobuf (4.30.0-x86_64-linux) - bigdecimal - rake (>= 13) - graphql-batch (0.6.0) - graphql (>= 1.12.18, < 3) - promise.rb (~> 0.7.2) - http_parser.rb (0.8.0) - httparty (0.22.0) - csv - mini_mime (>= 1.0.0) - multi_xml (>= 0.5.2) - httpclient (2.9.0) - mutex_m - i18n (1.14.7) - concurrent-ruby (~> 1.0) - imagen (0.2.0) - parser (>= 2.5, != 2.5.1.1) - io-event (1.9.0) - jekyll (4.4.1) - addressable (~> 2.4) - base64 (~> 0.2) - colorator (~> 1.0) - csv (~> 3.0) - em-websocket (~> 0.5) - i18n (~> 1.0) - jekyll-sass-converter (>= 2.0, < 4.0) - jekyll-watch (~> 2.0) - json (~> 2.6) - kramdown (~> 2.3, >= 2.3.1) - kramdown-parser-gfm (~> 1.0) - liquid (~> 4.0) - mercenary (~> 0.3, >= 0.3.6) - pathutil (~> 0.9) - rouge (>= 3.0, < 5.0) - safe_yaml (~> 1.0) - terminal-table (>= 1.8, < 4.0) - webrick (~> 1.7) - jekyll-algolia (1.7.1) - algolia_html_extractor (~> 2.6) - algoliasearch (~> 1.26) - filesize (~> 0.1) - jekyll (>= 3.6, < 5.0) - json (~> 2.0) - nokogiri (~> 1.6) - progressbar (~> 1.9) - verbal_expressions (~> 0.1.5) - jekyll-redirect-from (0.16.0) - jekyll (>= 3.3, < 5.0) - jekyll-sass-converter (2.2.0) - sassc (> 2.0.1, < 3.0) - jekyll-watch (2.2.1) - listen (~> 3.0) - json (2.10.1) - kramdown (2.5.1) - rexml (>= 3.3.9) - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - language_server-protocol (3.17.0.4) - libev_scheduler (0.2) - lint_roller (1.1.0) - liquid (4.0.4) - listen (3.9.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.6) - m (1.5.1) - method_source (>= 0.6.7) - rake (>= 0.9.2.2) - memory_profiler (1.1.0) - mercenary (0.4.0) - method_source (1.1.0) - metrics (0.12.1) - mini_mime (1.1.5) - minitest (5.25.4) - minitest-focus (1.4.0) - minitest (>= 4, < 6) - minitest-reporters (1.7.1) - ansi - builder - minitest (>= 5.0) - ruby-progressbar - msgpack (1.8.0) - multi_xml (0.7.1) - bigdecimal (~> 3.1) - mutex_m (0.3.0) - net-http (0.6.0) - uri - nokogiri (1.18.3-aarch64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.3-aarch64-linux-musl) - racc (~> 1.4) - nokogiri (1.18.3-arm-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.3-arm-linux-musl) - racc (~> 1.4) - nokogiri (1.18.3-arm64-darwin) - racc (~> 1.4) - nokogiri (1.18.3-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.18.3-x86_64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.3-x86_64-linux-musl) - racc (~> 1.4) - octokit (9.2.0) - faraday (>= 1, < 3) - sawyer (~> 0.9) - parallel (1.26.3) - parser (3.3.7.1) - ast (~> 2.4.1) - racc - pathutil (0.16.2) - forwardable-extended (~> 2.6) - progressbar (1.13.0) - promise.rb (0.7.4) - pronto (0.11.3) - gitlab (>= 4.4.0, < 5.0) - httparty (>= 0.13.7, < 1.0) - octokit (>= 4.7.0, < 10.0) - rainbow (>= 2.2, < 4.0) - rexml (>= 3.2.5, < 4.0) - rugged (>= 0.23.0, < 2.0) - thor (>= 0.20.3, < 2.0) - pronto-undercover (0.2.0) - pronto (>= 0.9, < 0.12) - undercover (~> 0.4.3) - pry (0.14.2) - coderay (~> 1.1) - method_source (~> 1.0) - pry-byebug (3.10.1) - byebug (~> 11.0) - pry (>= 0.13, < 0.15) - pry-stack_explorer (0.6.1) - binding_of_caller (~> 1.0) - pry (~> 0.13) - public_suffix (6.0.1) - racc (1.8.1) - rainbow (3.1.1) - rake (13.2.1) - rake-compiler (1.2.9) - rake - rb-fsevent (0.11.2) - rb-inotify (0.11.1) - ffi (~> 1.0) - regexp_parser (2.10.0) - rexml (3.4.1) - rouge (4.5.1) - rubocop (1.73.2) - json (~> 2.3) - language_server-protocol (~> 3.17.0.2) - lint_roller (~> 1.1.0) - parallel (~> 1.10) - parser (>= 3.3.0.2) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.38.0, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.38.1) - parser (>= 3.3.1.0) - ruby-progressbar (1.13.0) - rugged (1.6.5) - safe_yaml (1.0.5) - sassc (2.4.0) - ffi (~> 1.9) - sawyer (0.9.2) - addressable (>= 2.3.5) - faraday (>= 0.17.3, < 3) - simplecov (0.22.0) - docile (~> 1.1) - simplecov-html (~> 0.11) - simplecov_json_formatter (~> 0.1) - simplecov-html (0.13.1) - simplecov-lcov (0.8.0) - simplecov_json_formatter (0.1.4) - stackprof (0.2.27) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - thor (1.3.2) - traces (0.15.2) - undercover (0.4.7) - imagen (>= 0.1.8) - rainbow (>= 2.1, < 4.0) - rugged (>= 0.27, < 1.7) - unicode-display_width (2.6.0) - uri (1.0.3) - verbal_expressions (0.1.5) - webrick (1.9.1) - yard (0.9.37) - -PLATFORMS - aarch64-linux - aarch64-linux-gnu - aarch64-linux-musl - arm-linux-gnu - arm-linux-musl - arm64-darwin - x86_64-darwin - x86_64-linux - x86_64-linux-gnu - x86_64-linux-musl - -DEPENDENCIES - async (~> 2.0) - benchmark-ips - bootsnap - concurrent-ruby (~> 1.0) - evt - google-protobuf - graphql! - graphql-batch - jekyll - jekyll-algolia (~> 1.0) - jekyll-redirect-from - jekyll-sass-converter (~> 2.2) - libev_scheduler - m (~> 1.5.0) - memory_profiler - minitest - minitest-focus - minitest-reporters - mutex_m - pronto - pronto-undercover - pry - pry-byebug - pry-stack_explorer - rake - rake-compiler - rubocop - simplecov - simplecov-lcov - stackprof - undercover - webrick - yard - -BUNDLED WITH - 2.6.5 diff --git a/vendor/gems/graphql/MIT-LICENSE b/vendor/gems/graphql/MIT-LICENSE deleted file mode 100644 index fd143840928..00000000000 --- a/vendor/gems/graphql/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright 2015 Robert Mosolgo - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/gems/graphql/Rakefile b/vendor/gems/graphql/Rakefile deleted file mode 100644 index 4a48d826c40..00000000000 --- a/vendor/gems/graphql/Rakefile +++ /dev/null @@ -1,240 +0,0 @@ -# frozen_string_literal: true -require "bundler/gem_helper" -Bundler::GemHelper.install_tasks - -require "rake/testtask" -require_relative "guides/_tasks/site" -require_relative "lib/graphql/rake_task/validate" -require 'rake/extensiontask' - -Rake::TestTask.new do |t| - t.libs << "spec" << "lib" << "graphql-c_parser/lib" - - exclude_integrations = [] - ['mongoid', 'rails'].each do |integration| - begin - require integration - rescue LoadError - exclude_integrations << integration - end - end - - t.test_files = FileList.new("spec/**/*_spec.rb") do |fl| - fl.exclude(*exclude_integrations.map { |int| "spec/integration/#{int}/**/*" }) - end - - # After 2.7, there were not warnings for uninitialized ivars anymore - if RUBY_VERSION < "3" - t.warning = false - end -end - -require 'rubocop/rake_task' -RuboCop::RakeTask.new - -default_tasks = [:test, :rubocop] -if ENV["SYSTEM_TESTS"] - task(default: ["test:system"] + default_tasks) -else - task(default: default_tasks) -end - -def assert_dependency_version(dep_name, required_version, check_script) - version = `#{check_script}` - if !version.include?(required_version) - raise <<-ERR -build_parser requires #{dep_name} version "#{required_version}", but found: - - $ #{check_script} - > #{version} - -To fix this issue: - -- Update #{dep_name} to the required version -- Update the assertion in `Rakefile` to match the current version -ERR - end -end - -namespace :bench do - def prepare_benchmark - $LOAD_PATH << "./lib" << "./spec/support" - require_relative("./benchmark/run.rb") - end - - desc "Benchmark parsing" - task :parse do - prepare_benchmark - GraphQLBenchmark.run("parse") - end - - desc "Benchmark lexical analysis" - task :scan do - prepare_benchmark - GraphQLBenchmark.run("scan") - end - - desc "Benchmark the introspection query" - task :query do - prepare_benchmark - GraphQLBenchmark.run("query") - end - - desc "Benchmark validation of several queries" - task :validate do - prepare_benchmark - GraphQLBenchmark.run("validate") - end - - desc "Profile a validation" - task :validate_memory do - prepare_benchmark - GraphQLBenchmark.validate_memory - end - - desc "Generate a profile of the introspection query" - task :profile do - prepare_benchmark - GraphQLBenchmark.profile - end - - desc "Run benchmarks on a very large result" - task :profile_large_result do - prepare_benchmark - GraphQLBenchmark.profile_large_result - end - - desc "Run benchmarks on a small result" - task :profile_small_result do - prepare_benchmark - GraphQLBenchmark.profile_small_result - end - - desc "Run introspection on a small schema" - task :profile_small_introspection do - prepare_benchmark - GraphQLBenchmark.profile_small_introspection - end - - desc "Dump schema to SDL" - task :profile_to_definition do - prepare_benchmark - GraphQLBenchmark.profile_to_definition - end - - desc "Load schema from SDL" - task :profile_from_definition do - prepare_benchmark - GraphQLBenchmark.profile_from_definition - end - - desc "Compare GraphQL-Batch and GraphQL-Dataloader" - task :profile_batch_loaders do - prepare_benchmark - GraphQLBenchmark.profile_batch_loaders - end - - desc "Run benchmarks on schema creation" - task :profile_boot do - prepare_benchmark - GraphQLBenchmark.profile_boot - end - - desc "Check the memory footprint of a large schema" - task :profile_schema_memory_footprint do - prepare_benchmark - GraphQLBenchmark.profile_schema_memory_footprint - end - - desc "Check the depth of the stacktrace during execution" - task :profile_stack_depth do - prepare_benchmark - GraphQLBenchmark.profile_stack_depth - end - - desc "Run a very big introspection query" - task :profile_large_introspection do - prepare_benchmark - GraphQLBenchmark.profile_large_introspection - end - - task :profile_small_query_on_large_schema do - prepare_benchmark - GraphQLBenchmark.profile_small_query_on_large_schema - end - - desc "Run analysis on a big query" - task :profile_large_analysis do - prepare_benchmark - GraphQLBenchmark.profile_large_analysis - end - - desc "Run analysis on parsing" - task :profile_parse do - prepare_benchmark - GraphQLBenchmark.profile_parse - end -end - -namespace :test do - desc "Run system tests for ActionCable subscriptions" - task :system do - success = Dir.chdir("spec/dummy") do - system("bundle install") - system("bundle exec bin/rails test:system") - end - success || abort - end - - task js: "js:test" -end - -namespace :js do - client_dir = "./javascript_client" - - desc "Run the tests for javascript_client" - task :test do - success = Dir.chdir(client_dir) do - system("yarn run test") - end - success || abort - end - - desc "Install JS dependencies" - task :install do - Dir.chdir(client_dir) do - system("yarn install") - end - end - - desc "Compile TypeScript to JavaScript" - task :build do - Dir.chdir(client_dir) do - system("yarn tsc") - end - end - task all: [:install, :build, :test] -end - -task :build_c_lexer do - assert_dependency_version("Ragel", "7.0.4", "ragel -v") - `ragel -F1 graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl` -end - -Rake::ExtensionTask.new("graphql_c_parser_ext") do |t| - t.ext_dir = 'graphql-c_parser/ext/graphql_c_parser_ext' - t.lib_dir = "graphql-c_parser/lib/graphql" -end - -task :build_yacc_parser do - assert_dependency_version("Bison", "3.8", "yacc --version") - `yacc graphql-c_parser/ext/graphql_c_parser_ext/parser.y -o graphql-c_parser/ext/graphql_c_parser_ext/parser.c -Wyacc` -end - -task :move_binary do - # For some reason my local env doesn't respect the `lib_dir` configured above - `mv graphql-c_parser/lib/*.bundle graphql-c_parser/lib/graphql` -end - -desc "Build the C Extension" -task build_ext: [:build_c_lexer, :build_yacc_parser, "compile:graphql_c_parser_ext", :move_binary] diff --git a/vendor/gems/graphql/benchmark/abstract_fragments.graphql b/vendor/gems/graphql/benchmark/abstract_fragments.graphql deleted file mode 100644 index cf356efcc1a..00000000000 --- a/vendor/gems/graphql/benchmark/abstract_fragments.graphql +++ /dev/null @@ -1,41 +0,0 @@ -query AbstractFragments { - node(id: "1") { - ...Frag1 - } -} - -fragment Frag1 on Commentable { - id - __typename - ...Frag2 -} - -fragment Frag2 on Commentable { - id - __typename - ...Frag3 -} - -fragment Frag3 on Commentable { - id - __typename - ...Frag4 -} - -fragment Frag4 on Commentable { - id - __typename - ...Frag5 -} - -fragment Frag5 on Commentable { - id - __typename - ...Frag6 -} - -fragment Frag6 on Commentable { - comments { - body - } -} diff --git a/vendor/gems/graphql/benchmark/abstract_fragments_2.graphql b/vendor/gems/graphql/benchmark/abstract_fragments_2.graphql deleted file mode 100644 index 6ab5517f744..00000000000 --- a/vendor/gems/graphql/benchmark/abstract_fragments_2.graphql +++ /dev/null @@ -1,64 +0,0 @@ -query AbstractFragments { - node(id: "1") { - ...Frag1 - } -} - -fragment Frag1 on Commentable { - id - __typename - ...Frag9 - ...Frag2 -} - -fragment Frag2 on Commentable { - id - __typename - ...Frag9 - ...Frag3 -} - -fragment Frag3 on Commentable { - id - __typename - ...Frag9 - ...Frag4 -} - -fragment Frag4 on Commentable { - id - __typename - ...Frag9 - ...Frag5 -} - -fragment Frag5 on Commentable { - id - __typename - ...Frag9 - ...Frag6 -} - -fragment Frag6 on Commentable { - ...Frag7 - ...Frag9 - name - id - comments { - ...Frag8 - ...Frag7 - id - } -} - -fragment Frag7 on Node { - id -} - -fragment Frag8 on Comment { - body -} - -fragment Frag9 on Named { - name -} diff --git a/vendor/gems/graphql/benchmark/batch_loading.rb b/vendor/gems/graphql/benchmark/batch_loading.rb deleted file mode 100644 index b2d3f2d22c8..00000000000 --- a/vendor/gems/graphql/benchmark/batch_loading.rb +++ /dev/null @@ -1,138 +0,0 @@ -# frozen_string_literal: true -module BatchLoading - class GraphQLBatchSchema < GraphQL::Schema - DATA = [ - { id: "1", name: "Bulls", player_ids: ["2", "3"] }, - { id: "2", name: "Michael Jordan", team_id: "1" }, - { id: "3", name: "Scottie Pippin", team_id: "1" }, - { id: "4", name: "Braves", player_ids: ["5", "6"] }, - { id: "5", name: "Chipper Jones", team_id: "4" }, - { id: "6", name: "Tom Glavine", team_id: "4" }, - ] - - class DataLoader < GraphQL::Batch::Loader - def initialize(column: :id) - @column = column - end - - def perform(keys) - keys.each do |key| - record = DATA.find { |d| d[@column] == key } - fulfill(key, record) - end - end - end - - class Team < GraphQL::Schema::Object - field :name, String, null: false - field :players, "[BatchLoading::GraphQLBatchSchema::Player]", null: false - - def players - DataLoader.load_many(object[:player_ids]) - end - end - - class Player < GraphQL::Schema::Object - field :name, String, null: false - field :team, Team, null: false - - def team - DataLoader.load(object[:team_id]) - end - end - - class Query < GraphQL::Schema::Object - field :team, Team do - argument :name, String - end - - def team(name:) - DataLoader.for(column: :name).load(name) - end - end - - query(Query) - use GraphQL::Batch - end - - class GraphQLDataloaderSchema < GraphQL::Schema - class DataSource < GraphQL::Dataloader::Source - def initialize(options = {column: :id}) - @column = options[:column] - end - - def fetch(keys) - keys.map { |key| - d = GraphQLBatchSchema::DATA.find { |d| d[@column] == key } - # p [key, @column, d] - d - } - end - end - - class Team < GraphQL::Schema::Object - field :name, String, null: false - field :players, "[BatchLoading::GraphQLDataloaderSchema::Player]", null: false - - def players - dataloader.with(DataSource).load_all(object[:player_ids]) - end - end - - class Player < GraphQL::Schema::Object - field :name, String, null: false - field :team, Team, null: false - - def team - dataloader.with(DataSource).load(object[:team_id]) - end - end - - class Query < GraphQL::Schema::Object - field :team, Team do - argument :name, String - end - - def team(name:) - dataloader.with(DataSource, column: :name).load(name) - end - end - - query(Query) - use GraphQL::Dataloader - end - - class GraphQLNoBatchingSchema < GraphQL::Schema - DATA = GraphQLBatchSchema::DATA - - class Team < GraphQL::Schema::Object - field :name, String, null: false - field :players, "[BatchLoading::GraphQLNoBatchingSchema::Player]", null: false - - def players - object[:player_ids].map { |id| DATA.find { |d| d[:id] == id } } - end - end - - class Player < GraphQL::Schema::Object - field :name, String, null: false - field :team, Team, null: false - - def team - DATA.find { |d| d[:id] == object[:team_id] } - end - end - - class Query < GraphQL::Schema::Object - field :team, Team do - argument :name, String - end - - def team(name:) - DATA.find { |d| d[:name] == name } - end - end - - query(Query) - end -end diff --git a/vendor/gems/graphql/benchmark/big_query.graphql b/vendor/gems/graphql/benchmark/big_query.graphql deleted file mode 100644 index 2f986970c14..00000000000 --- a/vendor/gems/graphql/benchmark/big_query.graphql +++ /dev/null @@ -1,476 +0,0 @@ -query Anc_inbox_layout($first_0:Int!,$first_5:Int!,$first_6:Int!,$first_a:Int!,$first_c:Int!,$order_by_1:ReportOrderInput!,$substate_2:ReportStateEnum!,$pre_submission_review_states_3:[ReportPreSubmissionReviewStateEnum]!,$size_4:ProfilePictureSizes!,$size_9:ProfilePictureSizes!,$not_types_7:[ActivityTypes]!,$order_by_8:ActivityOrderInput!,$order_by_b:TeamOrderInput!) { - query { - id, - ...FE - } -} -fragment F0 on User { - username, - _profile_picturePkPpF:profile_picture(size:$size_4), - impact, - reputation, - signal, - id -} -fragment F1 on Report { - reporter { - username, - _profile_picturePkPpF:profile_picture(size:$size_4), - impact, - reputation, - signal, - _duplicate_users2Nhzxe:duplicate_users(first:$first_5) { - pageInfo { - hasNextPage, - hasPreviousPage - }, - total_count, - edges { - node { - id, - ...F0 - }, - cursor - } - }, - id - }, - id -} -fragment F2 on Report { - id -} -fragment F3 on Node { - id, - __typename -} -fragment F4 on ReportActivityInterface { - automated_response, - genius_execution_id, - report { - team { - handle, - id - }, - id - }, - __typename, - ...F3 -} -fragment F5 on Team { - url, - internet_bug_bounty, - _profile_pictureihzmG:profile_picture(size:$size_9), - name, - id -} -fragment F6 on User { - username, - url, - _profile_pictureihzmG:profile_picture(size:$size_9), - id -} -fragment F7 on ActivitiesBugDuplicate { - original_report_id, - id -} -fragment F8 on ActivitiesReferenceIdAdded { - reference, - reference_url, - id -} -fragment F9 on ActivitiesCveIdAdded { - cve_ids, - id -} -fragment Fa on ActivitiesAgreedOnGoingPublic { - first_to_agree, - id -} -fragment Fb on ActivitiesBugCloned { - original_report_id, - id -} -fragment Fc on ActivitiesUserAssignedToBug { - assigned_user { - url, - username, - id - }, - id -} -fragment Fd on ActivitiesGroupAssignedToBug { - assigned_group { - name, - id - }, - id -} -fragment Fe on ActivitiesExternalUserInvited { - email, - id -} -fragment Ff on ActivitiesExternalUserInvitationCancelled { - email, - id -} -fragment Fg on ActivitiesExternalUserRemoved { - removed_user { - id - }, - id -} -fragment Fh on ActivitiesUserBannedFromProgram { - removed_user { - id - }, - id -} -fragment Fi on ActivitiesBountyAwarded { - bounty_amount, - bounty_currency, - bonus_amount, - report { - reporter { - username, - url, - id - }, - id - }, - id -} -fragment Fj on ActivitiesBountySuggested { - bounty_amount, - bounty_currency, - bonus_amount, - id -} -fragment Fk on ActivitiesBugResolved { - report { - reporter { - username, - url, - id - }, - id - }, - id -} -fragment Fl on ActivitiesSwagAwarded { - report { - reporter { - username, - url, - id - }, - id - }, - id -} -fragment Fm on ActivitiesChangedScope { - old_scope { - asset_identifier, - id - }, - new_scope { - asset_identifier, - id - }, - id -} -fragment Fn on ActivityInterface { - _id, - internal, - i_can_edit, - __typename, - message, - markdown_message, - created_at, - updated_at, - actor { - __typename, - ...F5, - ...F6, - ...F3 - }, - attachments { - _id, - file_name, - content_type, - expiring_url, - id - }, - ...F7, - ...F8, - ...F9, - ...Fa, - ...Fb, - ...Fc, - ...Fd, - ...Fe, - ...Ff, - ...Fg, - ...Fh, - ...Fi, - ...Fj, - ...Fk, - ...Fl, - ...Fm, - ...F3 -} -fragment Fo on User { - username, - url, - __typename, - id -} -fragment Fp on TeamMemberGroup { - name, - __typename, - id -} -fragment Fq on Report { - _id, - url, - title, - state, - substate, - created_at, - assignee { - __typename, - ...Fo, - ...Fp, - ...F3 - }, - cloned_from { - _id, - id - }, - reporter { - username, - url, - id - }, - team { - _id, - url, - handle, - name, - twitter_handle, - website, - about, - offers_bounties, - id - }, - id -} -fragment Fr on Report { - state, - stage, - disclosed_at, - cve_ids, - singular_disclosure_disabled, - disclosed_at, - bug_reporter_agreed_on_going_public_at, - team_member_agreed_on_going_public_at, - comments_closed, - mediation_requested_at, - vulnerability_information, - vulnerability_information_html, - reporter { - disabled, - username, - url, - _profile_picture2g6hJa:profile_picture(size:$size_4), - id - }, - weakness { - id, - name - }, - original_report { - id, - url - }, - attachments { - _id, - file_name, - expiring_url, - content_type, - id - }, - allow_singular_disclosure_at, - allow_singular_disclosure_after, - singular_disclosure_allowed, - severity { - rating, - score, - author_type, - id - }, - structured_scope { - _id, - asset_type, - asset_identifier, - max_severity, - id - }, - _activities4z6spP:activities(first:$first_6,not_types:$not_types_7,order_by:$order_by_8) { - edges { - node { - __typename, - ...F4, - ...Fn, - ...F3 - }, - cursor - }, - pageInfo { - hasNextPage, - hasPreviousPage - } - }, - id, - ...Fq -} -fragment Fs on Report { - id, - ...Fr -} -fragment Ft on Report { - title, - id -} -fragment Fu on Report { - _id, - pre_submission_review_state, - i_can_anc_review, - reporter { - username, - id - }, - team { - handle, - id - }, - id, - ...F2 -} -fragment Fv on Report { - team { - policy_html, - id - }, - structured_scope { - asset_identifier, - asset_type, - instruction, - id - }, - id -} -fragment Fw on Report { - weakness { - name, - id - }, - id -} -fragment Fx on Report { - severity { - rating, - score, - id - }, - id -} -fragment Fy on Report { - latest_activity_at, - created_at, - id, - ...Fq -} -fragment Fz on Query { - me { - username, - _teamsWbVmT:teams(order_by:$order_by_b,first:$first_c) { - edges { - node { - name, - handle, - id - }, - cursor - }, - pageInfo { - hasNextPage, - hasPreviousPage - } - }, - id - }, - id -} -fragment FA on Query { - _reports1t04lE:reports(page:$first_0,first:$first_a,limit:$first_a,order_by:$order_by_1,substate:$substate_2,pre_submission_review_states:$pre_submission_review_states_3) { - total_count, - edges { - node { - _id, - id, - ...Fy - }, - cursor - }, - pageInfo { - hasNextPage, - hasPreviousPage - } - }, - id, - ...Fz -} -fragment FB on Query { - id, - ...Fz -} -fragment FC on Query { - id -} -fragment FD on Query { - me { - username, - _profile_pictureihzmG:profile_picture(size:$size_9), - id - }, - id, - ...FC -} -fragment FE on Query { - _reports3QQXft:reports(first:$first_0,order_by:$order_by_1,substate:$substate_2,pre_submission_review_states:$pre_submission_review_states_3) { - edges { - node { - id, - ...F1, - ...F2, - ...Fs, - ...Ft, - ...Fu, - ...Fv, - ...Fw, - ...Fx - }, - cursor - }, - pageInfo { - hasNextPage, - hasPreviousPage - } - }, - id, - ...FA, - ...FB, - ...FD -} - diff --git a/vendor/gems/graphql/benchmark/big_schema.graphql b/vendor/gems/graphql/benchmark/big_schema.graphql deleted file mode 100644 index 7c870316343..00000000000 --- a/vendor/gems/graphql/benchmark/big_schema.graphql +++ /dev/null @@ -1,4202 +0,0 @@ -# Autogenerated input type of AcceptInvitation -input AcceptInvitationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - handle: String! -} - -# Autogenerated return type of AcceptInvitation -type AcceptInvitationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - me: User -} - -# Autogenerated input type of AcknowledgeProgramHealthAcknowledgementMutation -input AcknowledgeProgramHealthAcknowledgementMutationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - program_health_acknowledgement_id: ID! -} - -# Autogenerated return type of AcknowledgeProgramHealthAcknowledgementMutation -type AcknowledgeProgramHealthAcknowledgementMutationPayload { - acknowledged_program_health_acknowledgement_id: String! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - me: User - was_successful: Boolean! -} - -# A Activities::AgreedOnGoingPublic activity for a report -type ActivitiesAgreedOnGoingPublic implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - first_to_agree: Boolean! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BountyAwarded activity for a report -type ActivitiesBountyAwarded implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - bonus_amount: String - bounty_amount: String - bounty_currency: String - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BountySuggested activity for a report -type ActivitiesBountySuggested implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - bonus_amount: String - bounty_amount: String - bounty_currency: String - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugCloned activity for a report -type ActivitiesBugCloned implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - original_report: Report - original_report_id: Int @deprecated(reason: "Deprecated in favor of .original_report") - report: Report - updated_at: String! -} - -# A Activities::BugDuplicate activity for a report -type ActivitiesBugDuplicate implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - original_report: Report - original_report_id: Int @deprecated(reason: "Deprecated in favor of .original_report") - report: Report - updated_at: String! -} - -# A Activities::BugFiled activity for a report -type ActivitiesBugFiled implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugInformative activity for a report -type ActivitiesBugInformative implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugNeedsMoreInfo activity for a report -type ActivitiesBugNeedsMoreInfo implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugNew activity for a report -type ActivitiesBugNew implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugNotApplicable activity for a report -type ActivitiesBugNotApplicable implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugReopened activity for a report -type ActivitiesBugReopened implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugResolved activity for a report -type ActivitiesBugResolved implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugSpam activity for a report -type ActivitiesBugSpam implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::BugTriaged activity for a report -type ActivitiesBugTriaged implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ChangedScope activity for a report -type ActivitiesChangedScope implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - new_scope: StructuredScope - old_scope: StructuredScope - report: Report - updated_at: String! -} - -# A Activities::Comment activity for a report -type ActivitiesComment implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::CommentsClosed activity for a report -type ActivitiesCommentsClosed implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::CveIdAdded activity for a report -type ActivitiesCveIdAdded implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - cve_ids: [String] - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ExternalAdvisoryAdded activity for a report -type ActivitiesExternalAdvisoryAdded implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ExternalUserInvitationCancelled activity for a report -type ActivitiesExternalUserInvitationCancelled implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - email: String - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ExternalUserInvited activity for a report -type ActivitiesExternalUserInvited implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - email: String - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ExternalUserJoined activity for a report -type ActivitiesExternalUserJoined implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - duplicate_report: Report - duplicate_report_id: Int @deprecated(reason: "Deprecated in favor of .duplicate_report") - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ExternalUserRemoved activity for a report -type ActivitiesExternalUserRemoved implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - removed_user: User! - report: Report - updated_at: String! -} - -# A Activities::GroupAssignedToBug activity for a report -type ActivitiesGroupAssignedToBug implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - assigned_group: TeamMemberGroup - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - group: TeamMemberGroup @deprecated(reason: "deprecated in favor of assigned group") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::HackerRequestedMediation activity for a report -type ActivitiesHackerRequestedMediation implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ManuallyDisclosed activity for a report -type ActivitiesManuallyDisclosed implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::MediationRequested activity for a report -type ActivitiesMediationRequested implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# An Activities::NobodyAssignedToBug activity for a report -type ActivitiesNobodyAssignedToBug implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::NotEligibleForBounty activity for a report -type ActivitiesNotEligibleForBounty implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ReassignedToTeam activity for a report -type ActivitiesReassignedToTeam implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ReferenceIdAdded activity for a report -type ActivitiesReferenceIdAdded implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - reference: String - reference_url: String - report: Report - updated_at: String! -} - -# A Activities::ReportBecamePublic activity for a report -type ActivitiesReportBecamePublic implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ReportCollaboratorInvited activity for a report -type ActivitiesReportCollaboratorInvited implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ReportCollaboratorJoined activity for a report -type ActivitiesReportCollaboratorJoined implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ReportSeverityUpdated activity for a report -type ActivitiesReportSeverityUpdated implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::ReportTitleUpdated activity for a report -type ActivitiesReportTitleUpdated implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - new_title: String! - old_title: String! - report: Report - updated_at: String! -} - -# A Activities::ReportVulnerabilityTypesUpdated activity for a report -type ActivitiesReportVulnerabilityTypesUpdated implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - new_weakness: Weakness - old_weakness: Weakness - report: Report - updated_at: String! -} - -# A Activities::SwagAwarded activity for a report -type ActivitiesSwagAwarded implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - swag: Swag! - updated_at: String! -} - -# A Activities::TeamPublished activity for a team -type ActivitiesTeamPublished implements ActivityInterface, Node { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - created_at: String! - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - updated_at: String! -} - -# A Activities::UserAssignedToBug activity for a report -type ActivitiesUserAssignedToBug implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - assigned_user: User! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - report: Report - updated_at: String! -} - -# A Activities::UserBannedFromProgram activity for a report -type ActivitiesUserBannedFromProgram implements ActivityInterface, Node, ReportActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - automated_response: Boolean - created_at: String! - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - removed_user: User! - report: Report - updated_at: String! -} - -# A Activities::UserJoined activity for a user -type ActivitiesUserJoined implements ActivityInterface, Node { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - created_at: String! - i_can_edit: Boolean! - id: ID! - internal: Boolean - markdown_message: String - message: String - updated_at: String! -} - -# The connection type for ActivityUnion. -type ActivityConnection { - # A list of edges. - edges: [ActivityUnionEdge] - max_updated_at: String - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# A interface for the common fields on an HackerOne Activity -interface ActivityInterface { - _id: ID! - actor: ActorUnion! - attachments: [Attachment] - created_at: String! - i_can_edit: Boolean! - internal: Boolean - markdown_message: String - message: String - updated_at: String! -} - -# Fields on which a collection of activities can be ordered -enum ActivityOrderField { - created_at - updated_at -} - -input ActivityOrderInput { - direction: OrderDirection! - field: ActivityOrderField! -} - -# Possible types for an activity -enum ActivityTypes { - AgreedOnGoingPublic - BountyAwarded - BountySuggested - BugCloned - BugDuplicate - BugFiled - BugInformative - BugNeedsMoreInfo - BugNew - BugNotApplicable - BugReopened - BugResolved - BugSpam - BugTriaged - ChangedScope - Comment - CommentsClosed - CveIdAdded - ExternalAdvisoryAdded - ExternalUserInvitationCancelled - ExternalUserInvited - ExternalUserJoined - ExternalUserRemoved - GroupAssignedToBug - HackerRequestedMediation - ManuallyDisclosed - MediationRequested - NobodyAssignedToBug - NotEligibleForBounty - ReassignedToTeam - ReferenceIdAdded - ReportBecamePublic - ReportCollaboratorInvited - ReportCollaboratorJoined - ReportSeverityUpdated - ReportTitleUpdated - ReportVulnerabilityTypesUpdated - SwagAwarded - TeamPublished - UserAssignedToBug - UserBannedFromProgram - UserJoined -} - -# Activities can be of multiple types -union ActivityUnion = ActivitiesAgreedOnGoingPublic | ActivitiesBountyAwarded | ActivitiesBountySuggested | ActivitiesBugCloned | ActivitiesBugDuplicate | ActivitiesBugFiled | ActivitiesBugInformative | ActivitiesBugNeedsMoreInfo | ActivitiesBugNew | ActivitiesBugNotApplicable | ActivitiesBugReopened | ActivitiesBugResolved | ActivitiesBugSpam | ActivitiesBugTriaged | ActivitiesChangedScope | ActivitiesComment | ActivitiesCommentsClosed | ActivitiesCveIdAdded | ActivitiesExternalAdvisoryAdded | ActivitiesExternalUserInvitationCancelled | ActivitiesExternalUserInvited | ActivitiesExternalUserJoined | ActivitiesExternalUserRemoved | ActivitiesGroupAssignedToBug | ActivitiesHackerRequestedMediation | ActivitiesManuallyDisclosed | ActivitiesMediationRequested | ActivitiesNobodyAssignedToBug | ActivitiesNotEligibleForBounty | ActivitiesReassignedToTeam | ActivitiesReferenceIdAdded | ActivitiesReportBecamePublic | ActivitiesReportCollaboratorInvited | ActivitiesReportCollaboratorJoined | ActivitiesReportSeverityUpdated | ActivitiesReportTitleUpdated | ActivitiesReportVulnerabilityTypesUpdated | ActivitiesSwagAwarded | ActivitiesTeamPublished | ActivitiesUserAssignedToBug | ActivitiesUserBannedFromProgram | ActivitiesUserJoined - -# The connection type for ActivityUnion. -type ActivityUnionConnection { - # A list of edges. - edges: [ActivityUnionEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type ActivityUnionEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: ActivityUnion -} - -# The actor of an activity can be multiple types -union ActorUnion = Team | User - -# A HackerOne user's address used for submitting swag -type Address implements Node { - _id: ID! - city: String! - country: String! - created_at: String! - id: ID! - name: String! - phone_number: String - postal_code: String! - state: String! - street: String! - tshirt_size: TshirtSizeEnum @deprecated(reason: "Query tshirt size on User instead") -} - -# Autogenerated input type of ArchiveStructuredScope -input ArchiveStructuredScopeInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - structured_scope_id: ID! -} - -# Autogenerated return type of ArchiveStructuredScope -type ArchiveStructuredScopePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - query: Query - structured_scope: StructuredScope -} - -# Report can be assigned to either a user or a team member group -union AssigneeUnion = TeamMemberGroup | User - -# A HackerOne attachment for a report -type Attachment implements Node { - _id: ID! - content_type: String! - created_at: String! - expiring_url: String! - file_name: String! - file_size: Int! - id: ID! -} - -# Types of authentication methods for users -enum AuthenticationServiceEnum { - database - saml - token -} - -# A HackerOne badge -type Badge implements Node { - _id: ID! - description: String! - id: ID! - image_path: String! - name: String! -} - -# Represents a badge earned by a user -type BadgesUsers implements Node { - _id: ID! - badge: Badge! - created_at: String! - id: ID! - user: User! -} - -# The connection type for BadgesUsers. -type BadgesUsersConnection { - # A list of edges. - edges: [BadgesUsersEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type BadgesUsersEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: BadgesUsers -} - -# Resources for setting up the Bank Transfer payment method -type BankTransferReference implements Node { - beneficiary_required_details(currency: String!, bank_account_country: String!, beneficiary_country: String!): BeneficiaryRequiredDetail - countries: [Country] - currencies: [Currency] - id: ID! -} - -# A specification of information needed to create a bank transfer payment preference -type BeneficiaryRequiredDetail implements Node { - bank_account_country: String! - beneficiary_country: String! - beneficiary_required_details( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): BeneficiaryRequiredDetailsConnection - currency: String! - id: ID! -} - -input BeneficiaryRequiredDetailInput { - field: String! - value: String! -} - -# A specification of the possibilities for creating a bank transfer payout preference -type BeneficiaryRequiredDetails implements Node { - beneficiary_entity_type: String! - beneficiary_required_fields( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): BeneficiaryRequiredFieldConnection - description: String! - fee: String! - id: ID! - payment_type: String! -} - -# The connection type for BeneficiaryRequiredDetails. -type BeneficiaryRequiredDetailsConnection { - # A list of edges. - edges: [BeneficiaryRequiredDetailsEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type BeneficiaryRequiredDetailsEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: BeneficiaryRequiredDetails -} - -# A specification of the format of a field used to create a bank transfer payout preference -type BeneficiaryRequiredField implements Node { - description: String! - field: String! - id: ID! - regex: String! -} - -# The connection type for BeneficiaryRequiredField. -type BeneficiaryRequiredFieldConnection { - # A list of edges. - edges: [BeneficiaryRequiredFieldEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type BeneficiaryRequiredFieldEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: BeneficiaryRequiredField -} - -# A HackerOne bounty for a report -type Bounty implements Node { - _id: ID! - amount: String! - awarded_amount: String! - awarded_bonus_amount: String! - awarded_currency: String! - bonus_amount: String! - created_at: String! - id: ID! - report: Report! - status: BountyStatusEnum! -} - -# The connection type for Bounty. -type BountyConnection { - # A list of edges. - edges: [BountyEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_amount: Float! - total_count: Int! -} - -# An edge in a connection. -type BountyEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Bounty -} - -# Status which reflect the state of a bounty -enum BountyStatusEnum { - cancelled - confirmed - failed - failed_ofac_check - needs_payout_method - needs_tax_form - no_mileage_account - no_status - no_tax_form - ofac_reject - pending - pending_ofac_check - rejected - sent -} - -# A subset of weaknesses that share a common characteristic -type Cluster implements Node { - created_at: String! - description: String - id: ID! - name: String! - updated_at: String! - weaknesses( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: WeaknessOrder = {direction: ASC, field: name} - search: String = null - team_handle: String = null - team_weakness_state: [TeamWeaknessStates] = [enabled, disabled, hidden] - ): ClusterWeaknessConnection -} - -# The connection type for Cluster. -type ClusterConnection { - # A list of edges. - edges: [ClusterEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type ClusterEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Cluster -} - -input ClusterOrder { - direction: OrderDirection! - field: ClusterOrderField! -} - -# Fields on which a collection of Cluster can be ordered -enum ClusterOrderField { - BROWSING_FRIENDLY -} - -# The connection type for Weakness. -type ClusterWeaknessConnection { - # A list of edges. - edges: [ClusterWeaknessEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type ClusterWeaknessEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Weakness - team_weakness(team_handle: String!): TeamWeakness -} - -# A Coinbase Payout Preference -type CoinbasePayoutPreferenceType implements Node, PayoutPreferenceInterface { - _id: ID! - default: Boolean - email: String - id: ID! -} - -# A common response -type CommonResponse implements Node { - # The primary key from the database - _id: ID! - created_at: String! - id: ID! - message: String! - team: Team! - title: String! - updated_at: String! -} - -# The connection type for CommonResponse. -type CommonResponseConnection { - # A list of edges. - edges: [CommonResponseEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type CommonResponseEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: CommonResponse -} - -input CommonResponseOrder { - direction: OrderDirection! - field: CommonResponseOrderField! -} - -# Fields on which a collection of common responses can be ordered -enum CommonResponseOrderField { - title -} - -# Will only return values for valid SeverityRatingEnum and null. -scalar CountBySeverity - -# A country as specified in ISO 3166 -type Country implements Node { - alpha2: String! - currency_code: String - id: ID! - name: String! -} - -# Autogenerated input type of CreateActivityComment -input CreateActivityCommentInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - internal: Boolean! - message: String! - report_id: ID! -} - -# Autogenerated return type of CreateActivityComment -type CreateActivityCommentPayload { - activities: ActivityUnionConnection - activity: ActivitiesComment - - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - new_activity_edge: ActivityUnionEdge - report: Report -} - -# Autogenerated input type of CreateBounty -input CreateBountyInput { - amount: Float - bonus_amount: Float - - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String - report_id: ID! -} - -# Autogenerated return type of CreateBounty -type CreateBountyPayload { - bounty: Bounty! - - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated input type of CreateBountySuggestion -input CreateBountySuggestionInput { - amount: Float - bonus_amount: Float - - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String - report_id: ID! -} - -# Autogenerated return type of CreateBountySuggestion -type CreateBountySuggestionPayload { - activity: ActivitiesBountySuggested! - - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated input type of CreateCoinbasePayoutPreference -input CreateCoinbasePayoutPreferenceInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - coinbase_email: String! - default_method: Boolean! -} - -# Autogenerated return type of CreateCoinbasePayoutPreference -type CreateCoinbasePayoutPreferencePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - me: User -} - -# Autogenerated input type of CreateCurrencycloudBankTransferPayoutPreference -input CreateCurrencycloudBankTransferPayoutPreferenceInput { - bank_account_country: String! - bank_account_holder_name: String! - beneficiary_entity_type: CurrencycloudBankTransferEntityType! - beneficiary_required_details: [BeneficiaryRequiredDetailInput]! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - currency: String! - default_method: Boolean! - payment_type: CurrencycloudBankTransferPaymentType! -} - -# Autogenerated return type of CreateCurrencycloudBankTransferPayoutPreference -type CreateCurrencycloudBankTransferPayoutPreferencePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of CreateJiraOauthUrl -input CreateJiraOauthUrlInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - site: String! - team_id: ID! -} - -# Autogenerated return type of CreateJiraOauthUrl -type CreateJiraOauthUrlPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team: Team - url: String -} - -# Autogenerated input type of CreateJiraWebhookToken -input CreateJiraWebhookTokenInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_id: ID! -} - -# Autogenerated return type of CreateJiraWebhookToken -type CreateJiraWebhookTokenPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team! - webhook_url: String! -} - -# Autogenerated input type of CreateMailingAddress -input CreateMailingAddressInput { - city: String! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - country: String! - name: String! - phone_number: String! - postal_code: String! - state: String! - street: String! -} - -# Autogenerated return type of CreateMailingAddress -type CreateMailingAddressPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - me: User - was_successful: Boolean! -} - -# Autogenerated input type of CreateOrUpdateHackeroneToJiraEventsConfiguration -input CreateOrUpdateHackeroneToJiraEventsConfigurationInput { - assignee_changes: Boolean - - # A unique identifier for the client performing the mutation. - clientMutationId: String - comments: Boolean - public_disclosures: Boolean - rewards: Boolean - state_changes: Boolean - team_id: ID! -} - -# Autogenerated return type of CreateOrUpdateHackeroneToJiraEventsConfiguration -type CreateOrUpdateHackeroneToJiraEventsConfigurationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team! -} - -# Autogenerated input type of CreateOrUpdateJiraIntegration -input CreateOrUpdateJiraIntegrationInput { - assignee: String - base_url: String! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - custom: String - description: String! - generate_webhook_in_jira_if_does_not_exist: Boolean - issue_type: Int! - labels: String - pid: Int! - summary: String! - team_id: ID! -} - -# Autogenerated return type of CreateOrUpdateJiraIntegration -type CreateOrUpdateJiraIntegrationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team: Team -} - -# Autogenerated input type of CreatePaypalPreference -input CreatePaypalPreferenceInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - default_method: Boolean! - paypal_email: String! -} - -# Autogenerated return type of CreatePaypalPreference -type CreatePaypalPreferencePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - me: User - was_successful: Boolean! -} - -# Autogenerated input type of CreateSlackPipeline -input CreateSlackPipelineInput { - channel: String! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - descriptive_label: String - notification_report_agreed_on_going_public: Boolean! - notification_report_assignee_changed: Boolean! - notification_report_became_public: Boolean! - notification_report_bounty_paid: Boolean! - notification_report_bounty_suggested: Boolean! - notification_report_bug_closed_as_spam: Boolean! - notification_report_bug_duplicate: Boolean! - notification_report_bug_informative: Boolean! - notification_report_bug_needs_more_info: Boolean! - notification_report_bug_new: Boolean! - notification_report_bug_not_applicable: Boolean! - notification_report_closed_as_resolved: Boolean! - notification_report_comments_closed: Boolean! - notification_report_created: Boolean! - notification_report_internal_comment_added: Boolean! - notification_report_manually_disclosed: Boolean! - notification_report_not_eligible_for_bounty: Boolean! - notification_report_public_comment_added: Boolean! - notification_report_reopened: Boolean! - notification_report_swag_awarded: Boolean! - notification_report_triaged: Boolean! - team_id: ID! -} - -# Autogenerated return type of CreateSlackPipeline -type CreateSlackPipelinePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - new_slack_pipeline_edge: SlackPipelineEdge - slack_pipelines_connection: SlackPipelineConnection - team: Team! -} - -# Autogenerated input type of CreateStructuredScope -input CreateStructuredScopeInput { - asset_identifier: String! - asset_type: StructuredScopedAssetTypeEnum - availability_requirement: String - - # A unique identifier for the client performing the mutation. - clientMutationId: String - confidentiality_requirement: String - eligible_for_bounty: Boolean - eligible_for_submission: Boolean - instruction: String - integrity_requirement: String - reference: String - team_id: ID! -} - -# Autogenerated return type of CreateStructuredScope -type CreateStructuredScopePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team: Team -} - -# Autogenerated input type of CreateSurveyAnswer -input CreateSurveyAnswerInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - feedback: String - invitation_token: String - structured_response_ids: [ID]! -} - -# Autogenerated return type of CreateSurveyAnswer -type CreateSurveyAnswerPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - me: User - was_successful: Boolean! -} - -# Autogenerated input type of CreateSwag -input CreateSwagInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String - report_id: ID! -} - -# Autogenerated return type of CreateSwag -type CreateSwagPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - swag: Swag! -} - -# Autogenerated input type of CreateTaxForm -input CreateTaxFormInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - type: TaxFormTypeEnum! -} - -# Autogenerated return type of CreateTaxForm -type CreateTaxFormPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [String] - me: User -} - -# Autogenerated input type of CreateUserBountiesReport -input CreateUserBountiesReportInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated return type of CreateUserBountiesReport -type CreateUserBountiesReportPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of CreateUserLufthansaAccount -input CreateUserLufthansaAccountInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - first_name: String! - last_name: String! - number: String! -} - -# Autogenerated return type of CreateUserLufthansaAccount -type CreateUserLufthansaAccountPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# A currency as defined by ISO 4217 -type Currency implements Node { - code: String! - id: ID! - name: String! -} - -# Possible currencies codes for bounties -enum CurrencyCode { - USD - XLA -} - -# Different entity types for currencycloud payout preferences -enum CurrencycloudBankTransferEntityType { - company - individual -} - -# Different payment types for currencycloud payout preferences -enum CurrencycloudBankTransferPaymentType { - priority - regular -} - -# A CurrencyCloud Bank Transfer Payout Preference -type CurrencycloudBankTransferPayoutPreferenceType implements Node, PayoutPreferenceInterface { - _id: ID! - default: Boolean - id: ID! - name: String -} - -# Autogenerated input type of DeleteBiDirectionalJiraIntegration -input DeleteBiDirectionalJiraIntegrationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_id: ID! -} - -# Autogenerated return type of DeleteBiDirectionalJiraIntegration -type DeleteBiDirectionalJiraIntegrationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team -} - -# Autogenerated input type of DeleteJiraWebhook -input DeleteJiraWebhookInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - jira_webhook_id: ID! -} - -# Autogenerated return type of DeleteJiraWebhook -type DeleteJiraWebhookPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team -} - -# Autogenerated input type of DeletePhabricatorIntegration -input DeletePhabricatorIntegrationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_id: ID! -} - -# Autogenerated return type of DeletePhabricatorIntegration -type DeletePhabricatorIntegrationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team -} - -# Autogenerated input type of DeleteSlackPipeline -input DeleteSlackPipelineInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - slack_pipeline_id: String! -} - -# Autogenerated return type of DeleteSlackPipeline -type DeleteSlackPipelinePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - deleted_slack_pipeline_id: String! - team: Team -} - -# Autogenerated input type of DeleteTeamMember -input DeleteTeamMemberInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_member_id: ID! -} - -# Autogenerated return type of DeleteTeamMember -type DeleteTeamMemberPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - deletedMembershipId: ID - errors: Hash - me: User -} - -# Autogenerated input type of DeleteTeamSlackIntegration -input DeleteTeamSlackIntegrationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - slack_integration_id: ID! -} - -# Autogenerated return type of DeleteTeamSlackIntegration -type DeleteTeamSlackIntegrationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team -} - -# Autogenerated input type of DeleteUserLufthansaAccount -input DeleteUserLufthansaAccountInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated return type of DeleteUserLufthansaAccount -type DeleteUserLufthansaAccountPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of DisableTwoFactorAuthentication -input DisableTwoFactorAuthenticationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - password: String! - totp_code: String! -} - -# Autogenerated return type of DisableTwoFactorAuthentication -type DisableTwoFactorAuthenticationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [String] - me: User -} - -# Autogenerated input type of DismissProgramHealthAcknowledgementMutation -input DismissProgramHealthAcknowledgementMutationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - program_health_acknowledgement_id: ID! -} - -# Autogenerated return type of DismissProgramHealthAcknowledgementMutation -type DismissProgramHealthAcknowledgementMutationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - dismissed_program_health_acknowledgement_id: String! - errors: [Error] - me: User - was_successful: Boolean! -} - -# Autogenerated input type of EnableTwoFactorAuthentication -input EnableTwoFactorAuthenticationInput { - backup_codes: [String]! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - password: String! - signature: String! - totp_code: String! - totp_secret: String! -} - -# Autogenerated return type of EnableTwoFactorAuthentication -type EnableTwoFactorAuthenticationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [String] - me: User -} - -# Autogenerated input type of EnableUser -input EnableUserInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated return type of EnableUser -type EnableUserPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of EnrollForProgram -input EnrollForProgramInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_id: ID! -} - -# Autogenerated return type of EnrollForProgram -type EnrollForProgramPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - deleted_recommended_program_edge: TeamEdge - query: Query! - teams_connection: TeamConnection -} - -# An error -type Error implements Node { - field: String - id: ID! - message: String! - type: ErrorTypeEnum! -} - -# Types of errors that can occur -enum ErrorTypeEnum { - ARGUMENT - AUTHORIZATION -} - -# An External Program -type ExternalProgram implements Node { - # The primary key from the database - _id: ID! - about: String - handle: String! - - # ID of the object. - id: ID! - name: String! - profile_picture(size: ProfilePictureSizes!): String! -} - -# Autogenerated input type of GenerateMfaOtp -input GenerateMfaOtpInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated return type of GenerateMfaOtp -type GenerateMfaOtpPayload { - backup_codes: [String] - - # A unique identifier for the client performing the mutation. - clientMutationId: String - me: User - qrcode: Hash - signature: String - totp_secret: String -} - -# Autogenerated input type of GenerateTaxFormUrl -input GenerateTaxFormUrlInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String -} - -# Autogenerated return type of GenerateTaxFormUrl -type GenerateTaxFormUrlPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# A HackeronePayroll Payout Preference -type HackeronePayrollPayoutPreferenceType implements Node, PayoutPreferenceInterface { - _id: ID! - default: Boolean - email: String - id: ID! -} - -# Configuration for the events sent from HackerOne to JIRA -type HackeroneToJiraEventsConfiguration implements Node { - assignee_changes: Boolean! - comments: Boolean! - id: ID! - public_disclosures: Boolean! - rewards: Boolean! - state_changes: Boolean! - team: Team! -} - -scalar Hash - -# An interface for the common fields on a HackerOne Invitation -interface InvitationInterface { - _id: ID! -} - -# User invitation preference type -enum InvitationPreferenceTypeEnum { - always - bounty_only - never -} - -# Invitations can be of multiple types -union InvitationUnion = InvitationsSoftLaunch - -# The connection type for InvitationUnion. -type InvitationUnionConnection { - # A list of edges. - edges: [InvitationUnionEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type InvitationUnionEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: InvitationUnion -} - -# An invitation to join a private team as a hacker -type InvitationsSoftLaunch implements InvitationInterface, Node { - _id: ID! - expires_at: String - id: ID! - markdown_message: String - message: String - team: Team! - token: String -} - -# A JIRA integration for a team -type JiraIntegration implements Node { - assignee: String - base_url: String! - created_at: String! - custom: String - description: String! - id: ID! - issue_type: Int! - labels: String - pid: Int! - summary: String! - team: Team! - updated_at: String! -} - -# A JIRA Oauth for a team -type JiraOauth implements Node { - # Assignables for a project - assignables(project_id: Int!): [Hash] - configured: Boolean! - created_at: String! - id: ID! - issue_types: [Hash] - projects: [Hash] - site: String - team: Team! - updated_at: String! -} - -# A JIRA webhook for a team -type JiraWebhook implements Node { - created_at: String! - id: ID! - last_event_received_at: String - last_token_issued_at: String - process_assignee_change: Boolean! - process_comment_add: Boolean! - process_priority_change: Boolean! - process_resolution_change: Boolean! - process_status_change: Boolean! - team: Team! - updated_at: String! -} - -# Autogenerated input type of LeavePrivateProgram -input LeavePrivateProgramInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - handle: String! -} - -# Autogenerated return type of LeavePrivateProgram -type LeavePrivateProgramPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - me: User - was_successful: Boolean! -} - -# Autogenerated input type of LockReport -input LockReportInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - report_id: ID! -} - -# Autogenerated return type of LockReport -type LockReportPayload { - activities: ActivityUnionConnection - activity: ActivitiesComment - - # A unique identifier for the client performing the mutation. - clientMutationId: String - new_activity_edge: ActivityUnionEdge - report: Report -} - -# Settings for a user's Lufthansa Account -type LufthansaAccount implements Node { - created_at: String! - first_name: String! - id: ID! - last_name: String! - number: String! - updated_at: String! -} - -# Autogenerated input type of MarkReportAsNeedsMoreInfo -input MarkReportAsNeedsMoreInfoInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String! - report_id: ID! -} - -# Autogenerated return type of MarkReportAsNeedsMoreInfo -type MarkReportAsNeedsMoreInfoPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - query: Query - report: Report - report_id: ID! - was_successful: Boolean! -} - -# Autogenerated input type of MarkReportAsNoise -input MarkReportAsNoiseInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String! - report_id: ID! -} - -# Autogenerated return type of MarkReportAsNoise -type MarkReportAsNoisePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - query: Query - report: Report - was_successful: Boolean! -} - -# Autogenerated input type of MarkReportAsSignal -input MarkReportAsSignalInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String - report_id: ID! -} - -# Autogenerated return type of MarkReportAsSignal -type MarkReportAsSignalPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - query: Query - report: Report - was_successful: Boolean! -} - -type Mutation { - acceptInvitation(input: AcceptInvitationInput!): AcceptInvitationPayload - acknowledgeProgramHealthAcknowledgement(input: AcknowledgeProgramHealthAcknowledgementMutationInput!): AcknowledgeProgramHealthAcknowledgementMutationPayload - archiveStructuredScope(input: ArchiveStructuredScopeInput!): ArchiveStructuredScopePayload - createActivityComment(input: CreateActivityCommentInput!): CreateActivityCommentPayload - createBounty(input: CreateBountyInput!): CreateBountyPayload - createBountySuggestion(input: CreateBountySuggestionInput!): CreateBountySuggestionPayload - createCoinbasePayoutPreference(input: CreateCoinbasePayoutPreferenceInput!): CreateCoinbasePayoutPreferencePayload - createCurrencycloudBankTransferPayoutPreference(input: CreateCurrencycloudBankTransferPayoutPreferenceInput!): CreateCurrencycloudBankTransferPayoutPreferencePayload - createJiraOauthUrl(input: CreateJiraOauthUrlInput!): CreateJiraOauthUrlPayload - createJiraWebhookToken(input: CreateJiraWebhookTokenInput!): CreateJiraWebhookTokenPayload - createMailingAddress(input: CreateMailingAddressInput!): CreateMailingAddressPayload - createOrUpdateHackeroneToJiraEventsConfiguration(input: CreateOrUpdateHackeroneToJiraEventsConfigurationInput!): CreateOrUpdateHackeroneToJiraEventsConfigurationPayload - createOrUpdateJiraIntegration(input: CreateOrUpdateJiraIntegrationInput!): CreateOrUpdateJiraIntegrationPayload - createPaypalPreference(input: CreatePaypalPreferenceInput!): CreatePaypalPreferencePayload - createSlackPipeline(input: CreateSlackPipelineInput!): CreateSlackPipelinePayload - createStructuredScope(input: CreateStructuredScopeInput!): CreateStructuredScopePayload - createSurveyAnswer(input: CreateSurveyAnswerInput!): CreateSurveyAnswerPayload - createSwag(input: CreateSwagInput!): CreateSwagPayload - createTaxForm(input: CreateTaxFormInput!): CreateTaxFormPayload - createUserBountiesReport(input: CreateUserBountiesReportInput!): CreateUserBountiesReportPayload - createUserLufthansaAccount(input: CreateUserLufthansaAccountInput!): CreateUserLufthansaAccountPayload - deleteBiDirectionalJiraIntegration(input: DeleteBiDirectionalJiraIntegrationInput!): DeleteBiDirectionalJiraIntegrationPayload - deleteJiraWebhook(input: DeleteJiraWebhookInput!): DeleteJiraWebhookPayload - deletePhabricatorIntegration(input: DeletePhabricatorIntegrationInput!): DeletePhabricatorIntegrationPayload - deleteSlackPipeline(input: DeleteSlackPipelineInput!): DeleteSlackPipelinePayload - deleteTeamMember(input: DeleteTeamMemberInput!): DeleteTeamMemberPayload - deleteTeamSlackIntegration(input: DeleteTeamSlackIntegrationInput!): DeleteTeamSlackIntegrationPayload - deleteUserLufthansaAccount(input: DeleteUserLufthansaAccountInput!): DeleteUserLufthansaAccountPayload - disableTwoFactorAuthentication(input: DisableTwoFactorAuthenticationInput!): DisableTwoFactorAuthenticationPayload - dismissProgramHealthAcknowledgement(input: DismissProgramHealthAcknowledgementMutationInput!): DismissProgramHealthAcknowledgementMutationPayload - enableTwoFactorAuthentication(input: EnableTwoFactorAuthenticationInput!): EnableTwoFactorAuthenticationPayload - enableUser(input: EnableUserInput!): EnableUserPayload - enrollForProgram(input: EnrollForProgramInput!): EnrollForProgramPayload - generateMfaOtp(input: GenerateMfaOtpInput!): GenerateMfaOtpPayload - generateTaxFormUrl(input: GenerateTaxFormUrlInput!): GenerateTaxFormUrlPayload - leavePrivateProgram(input: LeavePrivateProgramInput!): LeavePrivateProgramPayload - lockReport(input: LockReportInput!): LockReportPayload - markReportAsNeedsMoreInfo(input: MarkReportAsNeedsMoreInfoInput!): MarkReportAsNeedsMoreInfoPayload - markReportAsNoise(input: MarkReportAsNoiseInput!): MarkReportAsNoisePayload - markReportAsSignal(input: MarkReportAsSignalInput!): MarkReportAsSignalPayload - rejectInvitation(input: RejectInvitationInput!): RejectInvitationPayload - updateAutomaticInvites(input: UpdateAutomaticInvitesInput!): UpdateAutomaticInvitesPayload - updateBackupCodes(input: UpdateBackupCodesInput!): UpdateBackupCodesPayload - updateInvitationPreferences(input: UpdateInvitationPreferencesInput!): UpdateInvitationPreferencesPayload - updateJiraWebhook(input: UpdateJiraWebhookInput!): UpdateJiraWebhookPayload - updateMe(input: UpdateMeInput!): UpdateMePayload - updatePhabricatorIntegration(input: UpdatePhabricatorIntegrationInput!): UpdatePhabricatorIntegrationPayload - updateReportStateToNeedsMoreInfo(input: UpdateReportStateToNeedsMoreInfoInput!): UpdateReportStateToNeedsMoreInfoPayload - updateReportStructuredScope(input: UpdateReportStructuredScopeInput!): UpdateReportStructuredScopePayload - updateSlackPipeline(input: UpdateSlackPipelineInput!): UpdateSlackPipelinePayload - updateSlackUser(input: UpdateSlackUserInput!): UpdateSlackUserPayload - updateStructuredScope(input: UpdateStructuredScopeInput!): UpdateStructuredScopePayload - updateTeamFancySlackIntegration(input: UpdateTeamFancySlackIntegrationInput!): UpdateTeamFancySlackIntegrationPayload - updateTeamMemberVisibility(input: UpdateTeamMemberVisibilityInput!): UpdateTeamMemberVisibilityPayload - updateTeamSubmissionState(input: UpdateTeamSubmissionStateInput!): UpdateTeamSubmissionStatePayload - updateTeamSubscription(input: UpdateTeamSubscriptionInput!): UpdateTeamSubscriptionPayload - updateTeamWeakness(input: UpdateTeamWeaknessInput!): UpdateTeamWeaknessPayload - updateUserEmail(input: UpdateUserEmailInput!): UpdateUserEmailPayload - updateUserLufthansaAccount(input: UpdateUserLufthansaAccountInput!): UpdateUserLufthansaAccountPayload - updateUserPassword(input: UpdateUserPasswordInput!): UpdateUserPasswordPayload -} - -# An object with an ID. -interface Node { - # ID of the object. - id: ID! -} - -# Possible directions for sorting a collection -enum OrderDirection { - ASC - DESC -} - -# Information about pagination in a connection. -type PageInfo { - # When paginating forwards, the cursor to continue. - endCursor: String - - # When paginating forwards, are there more items? - hasNextPage: Boolean! - - # When paginating backwards, are there more items? - hasPreviousPage: Boolean! - - # When paginating backwards, the cursor to continue. - startCursor: String -} - -# The connection type for User. -type ParticipantConnection { - # A list of edges. - edges: [ParticipantWithReputationEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! - year_range: [Int] -} - -# An edge in a connection. -type ParticipantWithReputationEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: User - - # The participant's rank within the team - rank: Int - - # The participant's reputation within the team - reputation: Int -} - -# A interface for the common fields on an Payout Preference -interface PayoutPreferenceInterface { - _id: ID! - default: Boolean - id: ID! -} - -# A user can have payout preferences for different payment services -union PayoutPreferenceUnion = CoinbasePayoutPreferenceType | CurrencycloudBankTransferPayoutPreferenceType | HackeronePayrollPayoutPreferenceType | PaypalPayoutPreferenceType - -# A Paypal Payout Preference -type PaypalPayoutPreferenceType implements Node, PayoutPreferenceInterface { - _id: ID! - default: Boolean - email: String - id: ID! -} - -# All available permissions -enum PermissionEnum { - program_management -} - -# A Phabricator integration for a team -type PhabricatorIntegration implements Node { - base_url: String - created_at: String! - description: String - id: ID! - process_h1_comment_added: Boolean! - process_h1_status_change: Boolean! - process_phabricator_comment_added: Boolean! - process_phabricator_status_change: Boolean! - project_tags: String - team: Team! - title: String - updated_at: String! -} - -# Different possible profile picture sizes -enum ProfilePictureSizes { - # 110x110 - large - - # 82x82 - medium - - # 62x62 - small - - # 260x260 - xtralarge -} - -# A program_health_acknowledgement for a team_member -type ProgramHealthAcknowledgement implements Node { - acknowledged: Boolean - created_at: String - dismissed: Boolean - id: ID! - reason: ProgramHealthAcknowledgementReasonEnum - team_member: TeamMember! -} - -# The connection type for ProgramHealthAcknowledgement. -type ProgramHealthAcknowledgementConnection { - # A list of edges. - edges: [ProgramHealthAcknowledgementEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type ProgramHealthAcknowledgementEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: ProgramHealthAcknowledgement -} - -# reason which reflect the state of a program health acknowledgement -enum ProgramHealthAcknowledgementReasonEnum { - failed - ok - paused - review -} - -# Root entity of the Hackerone Schema -type Query implements Node { - bank_transfer_reference: BankTransferReference - clusters( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: ClusterOrder = {direction: ASC, field: BROWSING_FRIENDLY} - ): ClusterConnection - external_program(handle: String!): ExternalProgram - id: ID! - me: User - - # Fetches an object given its ID. - node( - # ID of the object. - id: ID! - ): Node - query: Query! - report(id: Int!): Report @deprecated(reason: "Query for a Report node at the root level is not recommended.") - reports( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - database_id: Int = null - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - - # Relay classic does not have support for starting paginationsomewhere in the - # middle, see https://github.com/facebook/relay/issues/1312 Workaround is to - # add a page argument till Relay supports this - limit: Int = null - order_by: ReportOrderInput = {direction: DESC, field: id} - - # Relay classic does not have support for starting paginationsomewhere in the - # middle, see https://github.com/facebook/relay/issues/1312 Workaround is to - # add a page argument till Relay supports this - page: Int = null - pre_submission_review_states: [ReportPreSubmissionReviewStateEnum] = null - substate: ReportStateEnum - without_scope: Boolean = null - ): ReportConnection - resource(url: URI): ResourceInterface - session: Session - severity_calculator: SeverityCalculator - surveys( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - category: SurveyCategoryEnum - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): SurveyConnectionType - team(handle: String!): Team @deprecated(reason: "Query for a Team node at the root level is not recommended. Ref T12456") - teams( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - enrollable: Boolean - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: TeamOrderInput - permissions: [PermissionEnum] = [] - ): TeamConnection - user(username: String!): User @deprecated(reason: "Query for a User node at the root level is not recommended. Ref T12456") - users( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): UserConnection -} - -# Autogenerated input type of RejectInvitation -input RejectInvitationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - handle: String! -} - -# Autogenerated return type of RejectInvitation -type RejectInvitationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - me: User -} - -# A HackerOne report -type Report implements Node, ResourceInterface { - _id: ID! - activities( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - not_types: [ActivityTypes] - order_by: ActivityOrderInput - types: [ActivityTypes] - - # A timestamp encoded as a string that. When provided, only activities with a - # updated_at greater than this value will be resolved. Example: - # activities(updated_at_after: "2017-11-30 13:37:12 UTC") - updated_at_after: String - ): ActivityConnection - allow_singular_disclosure_after: String - allow_singular_disclosure_at: String - assignee: AssigneeUnion - attachments: [Attachment] - bounties: [Bounty] - bounty_awarded_at: String - bug_reporter_agreed_on_going_public_at: String - cloned_from: Report - closed_at: String - comments_closed: Boolean - created_at: String! - cve_ids: [String] - disclosed_at: String - first_program_activity_at: String - i_can_anc_review: Boolean - id: ID! - latest_activity_at: String - latest_public_activity_at: String - latest_public_activity_of_reporter_at: String - latest_public_activity_of_team_at: String - mediation_requested_at: String - original_report: Report - pre_submission_review_state: String - reference: ID - reference_link: String - reporter: User - severity: Severity - singular_disclosure_allowed: Boolean - singular_disclosure_disabled: Boolean - stage: String - stale: Boolean - state: String - structured_scope: StructuredScope - substate: String! - suggested_bounty: String - summaries: [Summary] - swag: [Swag] - swag_awarded_at: String - team: Team - team_member_agreed_on_going_public_at: String - title: String - triaged_at: String - - # A pre-submission trigger that notified the hacker before submission. This - # field is only present for reports filed after February 14, 2016. - triggered_pre_submission_trigger: Trigger - url: URI! - vulnerability_information: String - vulnerability_information_html: String - weakness: Weakness -} - -# A interface for the common fields on an HackerOne Report Activity -interface ReportActivityInterface { - attachments: [Attachment] - automated_response: Boolean - genius_execution_id: ID @deprecated(reason: "This is about to be replaced by .genius_execution") - report: Report -} - -# The connection type for Report. -type ReportConnection { - # Groups and counts reports by the severity rating - count_by_severity: [CountBySeverity]! - - # A list of edges. - edges: [ReportEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type ReportEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Report -} - -# Filters which can be used to query reports -enum ReportFilterEnum { - assigned - assigned_to_me - bounty_awarded - going_public_team - going_public_user - hacker_requested_mediation - ineligible_for_bounty - needs_first_program_response - no_bounty_awarded - no_swag_awarded - not_public - public - reporter_is_active - stale - swag_awarded - unassigned -} - -# Fields on which a collection of reports can be ordered -enum ReportOrderField { - created_at - id - latest_activity_at -} - -input ReportOrderInput { - direction: OrderDirection! - field: ReportOrderField! -} - -# Pre submission review states a report can be in -enum ReportPreSubmissionReviewStateEnum { - pre_submission_accepted - pre_submission_needs_more_info - pre_submission_pending - pre_submission_rejected -} - -# States a report can be in -enum ReportStateEnum { - closed - duplicate - informative - needs_more_info - new - not_applicable - open - pre_submission - resolved - spam - triaged -} - -# The connection type for User. -type ReporterConnection { - # A list of edges. - edges: [UserEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# Represents a type that can be retrieved by a URL. -interface ResourceInterface { - url: URI! -} - -# A HackerOne session -type Session implements Node { - csrf_token: String! @deprecated(reason: "This token is used to support legacy operations in HackerOne's frontend") - id: ID! -} - -# A HackerOne severity for a report -type Severity implements Node { - _id: ID! - attack_complexity: SeverityAttackComplexityEnum - attack_vector: SeverityAttackVectorEnum - author_type: SeverityAuthorEnum - availability: SeverityAvailabilityEnum - confidentiality: SeverityConfidentialityEnum - created_at: String - id: ID! - integrity: SeverityIntegrityEnum - privileges_required: SeverityPrivilegesRequiredEnum - rating: SeverityRatingEnum - scope: SeverityScopeEnum - score: Float - user_id: Int - user_interaction: SeverityUserInteractionEnum -} - -# Severity attack complexity -enum SeverityAttackComplexityEnum { - high - low -} - -# Severity attack vector -enum SeverityAttackVectorEnum { - adjacent - local - network - physical -} - -# Severity author -enum SeverityAuthorEnum { - Team - User -} - -# Severity availability -enum SeverityAvailabilityEnum { - high - low - none -} - -# Calculate CVSS Severity score and rating -type SeverityCalculator implements Node { - calculated_max_severity(availability_requirement: SeveritySecurityRequirementEnum, confidentiality_requirement: SeveritySecurityRequirementEnum, integrity_requirement: SeveritySecurityRequirementEnum): SeverityRatingEnum - id: ID! -} - -# Severity confidentiality -enum SeverityConfidentialityEnum { - high - low - none -} - -# Severity integrity -enum SeverityIntegrityEnum { - high - low - none -} - -# Severity privileges required -enum SeverityPrivilegesRequiredEnum { - high - low - none -} - -# Severity rating -enum SeverityRatingEnum { - critical - high - low - medium - none -} - -# Severity scope -enum SeverityScopeEnum { - changed - unchanged -} - -# Severity security requirement rating -enum SeveritySecurityRequirementEnum { - high - low - medium - none -} - -# Severity user interaction -enum SeverityUserInteractionEnum { - none - required -} - -# SLA types -enum SlaTypeEnum { - first_program_response - report_triage -} - -# Slack channel -type SlackChannel implements Node { - # ID of the object. - id: ID! - name: String! -} - -# A Slack integration for a team -type SlackIntegration implements Node { - channel: String @deprecated(reason: "this field is not used in our new Slack integration") - channels: [SlackChannel] - id: ID! - should_fetch_slack_channels: Boolean! - should_fetch_slack_users: Boolean! - team: Team! - team_url: String! - users: [SlackUser] -} - -# A Slack Pipeline Configuration for notifications -type SlackPipeline implements Node, ResourceInterface { - # The primary key from the database - _id: ID! - channel: String! - descriptive_label: String - id: ID! - notification_report_agreed_on_going_public: Boolean! - notification_report_assignee_changed: Boolean! - notification_report_became_public: Boolean! - notification_report_bounty_paid: Boolean! - notification_report_bounty_suggested: Boolean! - notification_report_bug_closed_as_spam: Boolean! - notification_report_bug_duplicate: Boolean! - notification_report_bug_informative: Boolean! - notification_report_bug_needs_more_info: Boolean! - notification_report_bug_new: Boolean! - notification_report_bug_not_applicable: Boolean! - notification_report_closed_as_resolved: Boolean! - notification_report_comments_closed: Boolean! - notification_report_created: Boolean! - notification_report_internal_comment_added: Boolean! - notification_report_manually_disclosed: Boolean! - notification_report_not_eligible_for_bounty: Boolean! - notification_report_public_comment_added: Boolean! - notification_report_reopened: Boolean! - notification_report_swag_awarded: Boolean! - notification_report_triaged: Boolean! - team: Team! - updated_at: String! - url: URI! -} - -# The connection type for SlackPipeline. -type SlackPipelineConnection { - # A list of edges. - edges: [SlackPipelineEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type SlackPipelineEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: SlackPipeline -} - -# Slack user -type SlackUser implements Node { - # The id provided by Slack - _id: String! - avatar_small: String! - deleted: Boolean! - email: String! - - # ID of the object. - id: ID! - name: String! - real_name: String -} - -# A static participant for a team -type StaticParticipant implements Node { - _id: ID! - avatar(size: ProfilePictureSizes!): String! - bio: String - external_url: String - id: ID! - name: String! - year: String -} - -# The connection type for StaticParticipant. -type StaticParticipantConnection { - # A list of edges. - edges: [StaticParticipantEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type StaticParticipantEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: StaticParticipant -} - -# A defined scope of a HackerOne program -type StructuredScope implements Node, ResourceInterface { - _id: ID! - archived_at: String - asset_identifier: String! - asset_type: StructuredScopedAssetTypeEnum - availability_requirement: SeveritySecurityRequirementEnum - confidentiality_requirement: SeveritySecurityRequirementEnum - created_at: String! - eligible_for_bounty: Boolean - eligible_for_submission: Boolean - id: ID! - instruction: String - integrity_requirement: SeveritySecurityRequirementEnum - max_severity: SeverityRatingEnum - reference: String - rendered_instruction: String - reports( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): ReportConnection - structured_scope_versions( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): StructuredScopeVersionConnection - team: Team - updated_at: String - url: URI! -} - -# An edge in a connection. -type StructuredScopeEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: StructuredScope -} - -# A versioned log of a scope of a HackerOne program -type StructuredScopeVersion implements Node, ResourceInterface { - _id: ID! - archived_at: String - created_at: String! - id: ID! - instruction: String - max_severity: String - team: Team - url: URI! -} - -# The connection type for StructuredScopeVersion. -type StructuredScopeVersionConnection { - # A list of edges. - edges: [StructuredScopeVersionEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type StructuredScopeVersionEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: StructuredScopeVersion -} - -# Structured Scope asset type enum -enum StructuredScopedAssetTypeEnum { - APPLE_STORE_APP_ID - CIDR - DOWNLOADABLE_EXECUTABLES - GOOGLE_PLAY_APP_ID - HARDWARE - OTHER - OTHER_APK - OTHER_IPA - SOURCE_CODE - TESTFLIGHT - URL - WINDOWS_APP_STORE_APP_ID -} - -# The connection type for StructuredScope. -type StructuredScopesConnection { - # A list of edges. - edges: [StructuredScopeEdge] - max_updated_at: String - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# Submission states -enum SubmissionStateEnum { - disabled - open - paused -} - -# Team subscription action enum -enum SubscriptionActionEnum { - subscribe_to_all - unsubscribe_from_all -} - -# A HackerOne summary for a report -type Summary implements Node { - _id: ID! - category: String! @deprecated(reason: "The implementation of this field contains hard to reason about polymorphism") - content: String! - created_at: String! - id: ID! - updated_at: String! - user: User! -} - -# A HackerOne survey -type Survey implements Node, ResourceInterface { - category: String! - id: ID! - question: String! - structured_responses( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): SurveyStructuredResponseConnectionType - url: URI! -} - -# Survey categories -enum SurveyCategoryEnum { - invitation_rejection -} - -# The connection type for Survey. -type SurveyConnectionType { - # A list of edges. - edges: [SurveyEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type SurveyEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Survey -} - -# Prepared survey response reasons -type SurveyStructuredResponse implements Node { - _id: ID! - enabled: Boolean! - helper_text: String - id: ID! - reason: String! - survey: Survey -} - -# The connection type for SurveyStructuredResponse. -type SurveyStructuredResponseConnectionType { - # A list of edges. - edges: [SurveyStructuredResponseEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type SurveyStructuredResponseEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: SurveyStructuredResponse -} - -# A HackerOne swag awarded for a report -type Swag implements Node { - _id: ID! - address: Address @deprecated(reason: "This field is deprecated. The preferred way to access this data is using swag.user.address.") - created_at: String - id: ID! - report: Report - sent: Boolean! - team: Team - user: User -} - -# The connection type for Swag. -type SwagConnection { - # A list of edges. - edges: [SwagEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type SwagEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Swag -} - -# A tax form for a user -type TaxForm implements Node { - created_at: String! - hello_sign_client_id: String - id: ID! - signed_at: String - status: TaxFormStateEnum! - type: TaxFormTypeEnum! - url: String -} - -# Status of a tax form -enum TaxFormStateEnum { - expired - needs_review - rejected - requested - unavailable - valid -} - -# Type of a tax form -enum TaxFormTypeEnum { - W8BEN - W8BENE - W9 - W9Corporate -} - -# A HackerOne team -type Team implements Node, ResourceInterface { - # The primary key from the database - _id: ID! - about: String - abuse: Boolean - activities( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - not_types: [ActivityTypes] - order_by: ActivityOrderInput - types: [ActivityTypes] - - # A timestamp encoded as a string that. When provided, only activities with a - # updated_at greater than this value will be resolved. Example: - # activities(updated_at_after: "2017-11-30 13:37:12 UTC") - updated_at_after: String - ): ActivityConnection - automatic_invites: Boolean - base_bounty: Int - bug_count: Int - child_teams( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): TeamConnection - common_responses( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: CommonResponseOrder = {direction: ASC, field: title} - ): CommonResponseConnection - created_at: String - currency: String - fancy_slack_integration: Boolean - fancy_slack_integration_enabled: Boolean! - first_response_time: Float - hackerone_to_jira_events_configuration: HackeroneToJiraEventsConfiguration - handle: String! - has_avatar: Boolean - has_policy: Boolean - i_can_create_jira_webhook: Boolean - i_can_destroy_jira_webhook: Boolean! - i_can_view_base_bounty: Boolean - i_can_view_jira_integration: Boolean - i_can_view_jira_webhook: Boolean - i_can_view_phabricator_integration: Boolean - i_can_view_program_health: Boolean! - i_can_view_reports_resolved: Boolean - i_can_view_weaknesses: Boolean - i_cannot_create_jira_webhook_reasons: [TeamCannotCreateJiraWebhookReasons]! - id: ID! - inbox_views( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: TeamInboxViewOrder = {direction: ASC, field: position} - visible: Boolean - ): TeamInboxViewConnection - internet_bug_bounty: Boolean - jira_integration: JiraIntegration - jira_oauth: JiraOauth - jira_phase_3_enabled: Boolean! - jira_webhook: JiraWebhook - maximum_number_of_team_mediation_requests: Float - name: String! - new_staleness_threshold: Int - offers_bounties: Boolean - offers_swag: Boolean - open_soft_launch_invitations( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): InvitationUnionConnection @deprecated(reason: "This should be a generic invitation connection.\n Used interim until generic invitation type is defined") - participants( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - limit: Int = null - year: Int = null - ): ParticipantConnection - phabricator_integration: PhabricatorIntegration - policy_html: String - profile_picture(size: ProfilePictureSizes!): String! - report_submission_form_intro: String - report_template: String - reporters( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): ReporterConnection - reports( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - database_id: Int = null - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - - # Relay classic does not have support for starting paginationsomewhere in the - # middle, see https://github.com/facebook/relay/issues/1312 Workaround is to - # add a page argument till Relay supports this - limit: Int = null - order_by: ReportOrderInput = {direction: DESC, field: id} - - # Relay classic does not have support for starting paginationsomewhere in the - # middle, see https://github.com/facebook/relay/issues/1312 Workaround is to - # add a page argument till Relay supports this - page: Int = null - pre_submission_review_states: [ReportPreSubmissionReviewStateEnum] = null - state: ReportStateEnum - substate: ReportStateEnum - violates_minimum_sla: SlaTypeEnum - without_scope: Boolean = null - ): ReportConnection - review_requested_at: String - sla_failed_count: Int - sla_missed_count: Int - slack_integration: SlackIntegration - slack_pipelines( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): SlackPipelineConnection - state: TeamState! - static_participants( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): StaticParticipantConnection - structured_scopes( - # Returns the elements in the list that come after the specified cursor. - after: String - archived: Boolean = null - asset_type: String = null - - # Returns the elements in the list that come before the specified cursor. - before: String - eligible_for_submission: Boolean = null - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - search: String = null - ): StructuredScopesConnection - submission_state: SubmissionStateEnum! - target_signal: Float - team_member_groups( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): TeamMemberGroupConnection - team_members( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: TeamMemberOrder = {direction: ASC, field: username} - ): TeamMemberConnection - triage_active: Boolean - triage_time: Float - triaged_staleness_threshold: Int - twitter_handle: String - updated_at: String - url: URI! - weaknesses( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - cluster_id: ID = null - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: WeaknessOrder = {direction: ASC, field: name} - search: String = null - team_weakness_state: [TeamWeaknessStates] = [enabled, disabled, hidden] - ): TeamWeaknessConnection - website: String - whitelisted_hackers( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): ReporterConnection -} - -# Different reasons why a team cannot create a jira webhook -enum TeamCannotCreateJiraWebhookReasons { - CANNOT_VIEW - FEATURE_GATED - PROGRAM_PERMISSION_REQUIRED -} - -# The connection type for Team. -type TeamConnection { - # A list of edges. - edges: [TeamEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type TeamEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Team -} - -# A team report filter preset -type TeamInboxView implements Node { - _id: ID! - assigned_to_group_ids: [Int!]! - assigned_to_user_ids: [Int!]! - built_in: Boolean! - created_at: String! - filters: [ReportFilterEnum!]! - hackathons: [Int!]! - id: ID! - key: String! - name: String! - position: Int! - reporter_ids: [Int!]! - severities: [String!]! - substates: [ReportStateEnum!]! - team: Team! - text_query: String! - updated_at: String! - visible: Boolean! -} - -# The connection type for TeamInboxView. -type TeamInboxViewConnection { - # A list of edges. - edges: [TeamInboxViewEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type TeamInboxViewEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: TeamInboxView -} - -input TeamInboxViewOrder { - direction: OrderDirection! - field: TeamInboxViewOrderField! -} - -# Fields on which a collection of team inbox views can be ordered -enum TeamInboxViewOrderField { - position -} - -# A HackerOne team member -type TeamMember implements Node { - # The primary key from the database - _id: ID! - auto_subscribe: Boolean - concealed: Boolean - created_at: String! - i_can_leave_team: Boolean! - id: ID! - permissions: [String]! - slack_user_id: String - team: Team! - user: User! -} - -# The connection type for TeamMember. -type TeamMemberConnection { - # A list of edges. - edges: [TeamMemberEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type TeamMemberEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: TeamMember -} - -# A HackerOne team member group -type TeamMemberGroup implements Node { - _id: ID! - created_at: String - - # ID of the object. - id: ID! - name: String! - permissions: [String]! -} - -# The connection type for TeamMemberGroup. -type TeamMemberGroupConnection { - # A list of edges. - edges: [TeamMemberGroupEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! -} - -# An edge in a connection. -type TeamMemberGroupEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: TeamMemberGroup -} - -input TeamMemberOrder { - direction: OrderDirection! - field: TeamMemberOrderField! -} - -# Fields on which a collection of team members can be ordered -enum TeamMemberOrderField { - username -} - -# Fields on which a collection of Teams can be ordered -enum TeamOrderField { - missing_pressure - name -} - -input TeamOrderInput { - direction: OrderDirection! - field: TeamOrderField! -} - -# Different possible team states -enum TeamState { - da_mode - inactive - public_mode - sandboxed - soft_launched -} - -# Team configuration of a weakness -type TeamWeakness implements Node { - id: ID! - instruction: String - state: TeamWeaknessStates - team: Team! - weakness: Weakness! -} - -# The connection type for Weakness. -type TeamWeaknessConnection { - # A list of edges. - edges: [TeamWeaknessEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type TeamWeaknessEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: Weakness - team_weakness: TeamWeakness -} - -# Possible states of how a weakness can be configured for a team -enum TeamWeaknessStates { - disabled - enabled - hidden -} - -# A HackerOne trigger -type Trigger implements Node { - _id: ID! - id: ID! - title: String! -} - -# Tshirt size -enum TshirtSizeEnum { - M_Large - M_Medium - M_Small - M_XLarge - M_XXLarge - W_Large - W_Medium - W_Small - W_XLarge - W_XXLarge -} - -# Represents a RFC compliant URI string. It is often used to fetch an object. -scalar URI - -# Autogenerated input type of UpdateAutomaticInvites -input UpdateAutomaticInvitesInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - enabled: Boolean! - handle: String! -} - -# Autogenerated return type of UpdateAutomaticInvites -type UpdateAutomaticInvitesPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team: Team -} - -# Autogenerated input type of UpdateBackupCodes -input UpdateBackupCodesInput { - backup_codes: [String]! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - password: String! - signature: String! - totp_code: String! - totp_secret: String! -} - -# Autogenerated return type of UpdateBackupCodes -type UpdateBackupCodesPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [String] - me: User -} - -# Autogenerated input type of UpdateInvitationPreferences -input UpdateInvitationPreferencesInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - invitation_preference: InvitationPreferenceTypeEnum! -} - -# Autogenerated return type of UpdateInvitationPreferences -type UpdateInvitationPreferencesPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of UpdateJiraWebhook -input UpdateJiraWebhookInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - jira_webhook_id: ID! - process_assignee_change: Boolean - process_comment_add: Boolean - process_priority_change: Boolean - process_resolution_change: Boolean - process_status_change: Boolean -} - -# Autogenerated return type of UpdateJiraWebhook -type UpdateJiraWebhookPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - jira_webhook: JiraWebhook! -} - -# Autogenerated input type of UpdateMe -input UpdateMeInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - tshirt_size: String! -} - -# Autogenerated return type of UpdateMe -type UpdateMePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of UpdatePhabricatorIntegration -input UpdatePhabricatorIntegrationInput { - api_token: String - base_url: String - - # A unique identifier for the client performing the mutation. - clientMutationId: String - description: String - process_h1_comment_added: Boolean - process_h1_status_change: Boolean - process_phabricator_comment_added: Boolean - process_phabricator_status_change: Boolean - team_id: ID! - title: String -} - -# Autogenerated return type of UpdatePhabricatorIntegration -type UpdatePhabricatorIntegrationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team: Team -} - -# Autogenerated input type of UpdateReportStateToNeedsMoreInfo -input UpdateReportStateToNeedsMoreInfoInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - message: String! - report_id: ID! -} - -# Autogenerated return type of UpdateReportStateToNeedsMoreInfo -type UpdateReportStateToNeedsMoreInfoPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - report: Report -} - -# Autogenerated input type of UpdateReportStructuredScope -input UpdateReportStructuredScopeInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - report_id: ID! - structured_scope_id: ID -} - -# Autogenerated return type of UpdateReportStructuredScope -type UpdateReportStructuredScopePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - report: Report -} - -# Autogenerated input type of UpdateSlackPipeline -input UpdateSlackPipelineInput { - channel: String! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - descriptive_label: String - notification_report_agreed_on_going_public: Boolean! - notification_report_assignee_changed: Boolean! - notification_report_became_public: Boolean! - notification_report_bounty_paid: Boolean! - notification_report_bounty_suggested: Boolean! - notification_report_bug_closed_as_spam: Boolean! - notification_report_bug_duplicate: Boolean! - notification_report_bug_informative: Boolean! - notification_report_bug_needs_more_info: Boolean! - notification_report_bug_new: Boolean! - notification_report_bug_not_applicable: Boolean! - notification_report_closed_as_resolved: Boolean! - notification_report_comments_closed: Boolean! - notification_report_created: Boolean! - notification_report_internal_comment_added: Boolean! - notification_report_manually_disclosed: Boolean! - notification_report_not_eligible_for_bounty: Boolean! - notification_report_public_comment_added: Boolean! - notification_report_reopened: Boolean! - notification_report_swag_awarded: Boolean! - notification_report_triaged: Boolean! - slack_pipeline_id: ID! -} - -# Autogenerated return type of UpdateSlackPipeline -type UpdateSlackPipelinePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - slack_pipeline: SlackPipeline! -} - -# Autogenerated input type of UpdateSlackUser -input UpdateSlackUserInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - slack_user_id: String! - team_member_id: ID! -} - -# Autogenerated return type of UpdateSlackUser -type UpdateSlackUserPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_member: TeamMember! -} - -# Autogenerated input type of UpdateStructuredScope -input UpdateStructuredScopeInput { - asset_identifier: String - availability_requirement: String - - # A unique identifier for the client performing the mutation. - clientMutationId: String - confidentiality_requirement: String - eligible_for_bounty: Boolean - eligible_for_submission: Boolean - instruction: String - integrity_requirement: String - reference: String - structured_scope_id: ID! -} - -# Autogenerated return type of UpdateStructuredScope -type UpdateStructuredScopePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - query: Query - structured_scope: StructuredScope -} - -# Autogenerated input type of UpdateTeamFancySlackIntegration -input UpdateTeamFancySlackIntegrationInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - fancy_slack_integration: Boolean! - team_id: ID! -} - -# Autogenerated return type of UpdateTeamFancySlackIntegration -type UpdateTeamFancySlackIntegrationPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - team: Team -} - -# Autogenerated input type of UpdateTeamMemberVisibility -input UpdateTeamMemberVisibilityInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - concealed: Boolean! - team_member_id: ID! -} - -# Autogenerated return type of UpdateTeamMemberVisibility -type UpdateTeamMemberVisibilityPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team_member: TeamMember -} - -# Autogenerated input type of UpdateTeamSubmissionState -input UpdateTeamSubmissionStateInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - handle: String! - submission_state: SubmissionStateEnum! -} - -# Autogenerated return type of UpdateTeamSubmissionState -type UpdateTeamSubmissionStatePayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team: Team -} - -# Autogenerated input type of UpdateTeamSubscription -input UpdateTeamSubscriptionInput { - action: SubscriptionActionEnum - auto_subscribe: Boolean! - - # A unique identifier for the client performing the mutation. - clientMutationId: String - team_member_id: ID! -} - -# Autogenerated return type of UpdateTeamSubscription -type UpdateTeamSubscriptionPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - team_member: TeamMember -} - -# Autogenerated input type of UpdateTeamWeakness -input UpdateTeamWeaknessInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - instruction: String - state: TeamWeaknessStates! - team_weakness_id: ID! -} - -# Autogenerated return type of UpdateTeamWeakness -type UpdateTeamWeaknessPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - query: Query - team_weakness: TeamWeakness -} - -# Autogenerated input type of UpdateUserEmail -input UpdateUserEmailInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - email: String! - password: String! -} - -# Autogenerated return type of UpdateUserEmail -type UpdateUserEmailPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: [Error] - me: User - was_successful: Boolean! -} - -# Autogenerated input type of UpdateUserLufthansaAccount -input UpdateUserLufthansaAccountInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - first_name: String! - last_name: String! - number: String! -} - -# Autogenerated return type of UpdateUserLufthansaAccount -type UpdateUserLufthansaAccountPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# Autogenerated input type of UpdateUserPassword -input UpdateUserPasswordInput { - # A unique identifier for the client performing the mutation. - clientMutationId: String - current_password: String! - password: String! - password_confirmation: String! -} - -# Autogenerated return type of UpdateUserPassword -type UpdateUserPasswordPayload { - # A unique identifier for the client performing the mutation. - clientMutationId: String - errors: Hash - me: User -} - -# A HackerOne user -type User implements Node, ResourceInterface { - _id: ID! - address: Address - authentication_service: AuthenticationServiceEnum! - badges( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): BadgesUsersConnection - bio: String - bounties( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - currency: CurrencyCode = null - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): BountyConnection - created_at: String! - demo_hacker: Boolean! - disabled: Boolean! - duplicate_users( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): UserConnection - email: String - i_can_update_username: Boolean - id: ID! - impact: Float - impact_percentile: Float - invitation_preference: InvitationPreferenceTypeEnum - location: String - lufthansa_account: LufthansaAccount - member_of_verified_team: Boolean - membership(team_handle: String!): TeamMember - memberships( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): TeamMemberConnection - name: String - next_update_username_date: String - otp_backup_codes: [String] - payout_preferences: [PayoutPreferenceUnion] - profile_picture(size: ProfilePictureSizes!): String! - profile_pictures: Hash! @deprecated(reason: "Returns all the possible profile pictures instead of just the one you want use .profile_picture instead.") - program_health_acknowledgements( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): ProgramHealthAcknowledgementConnection - rank: Int - remaining_reports(team_handle: String!): Int - reports( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - database_id: Int = null - - # Returns the first _n_ elements from the list. - first: Int - handle: String - - # Returns the last _n_ elements from the list. - last: Int - - # Relay classic does not have support for starting paginationsomewhere in the - # middle, see https://github.com/facebook/relay/issues/1312 Workaround is to - # add a page argument till Relay supports this - limit: Int = null - order_by: ReportOrderInput = {direction: DESC, field: id} - - # Relay classic does not have support for starting paginationsomewhere in the - # middle, see https://github.com/facebook/relay/issues/1312 Workaround is to - # add a page argument till Relay supports this - page: Int = null - pre_submission_review_states: [ReportPreSubmissionReviewStateEnum] = null - substate: ReportStateEnum - without_scope: Boolean = null - ): ReportConnection - reputation: Int - signal: Float - signal_percentile: Float - soft_launch_invitation(team_handle: String!): InvitationsSoftLaunch - swag( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): SwagConnection - tax_form: TaxForm - teams( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - enrollable: Boolean - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - order_by: TeamOrderInput - permissions: [PermissionEnum] = [] - ): TeamConnection - totp_enabled: Boolean - totp_supported: Boolean - triage_user: Boolean - tshirt_size: TshirtSizeEnum - unconfirmed_email: String - url: URI! - username: String! - website: String - whitelisted_teams( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): TeamConnection -} - -# The connection type for User. -type UserConnection { - # A list of edges. - edges: [UserEdge] - - # Information to aid in pagination. - pageInfo: PageInfo! - total_count: Int! -} - -# An edge in a connection. -type UserEdge { - # A cursor for use in pagination. - cursor: String! - - # The item at the end of the edge. - node: User -} - -# The type of vulnerability on a HackerOne report -type Weakness implements Node { - _id: ID! - clusters( - # Returns the elements in the list that come after the specified cursor. - after: String - - # Returns the elements in the list that come before the specified cursor. - before: String - - # Returns the first _n_ elements from the list. - first: Int - - # Returns the last _n_ elements from the list. - last: Int - ): ClusterConnection - created_at: String! - description: String - external_id: String - id: ID! - name: String! - updated_at: String! -} - -input WeaknessOrder { - direction: OrderDirection! - field: WeaknessOrderField! -} - -# Fields on which a collection of weaknesses can be ordered -enum WeaknessOrderField { - name -} diff --git a/vendor/gems/graphql/benchmark/run.rb b/vendor/gems/graphql/benchmark/run.rb deleted file mode 100644 index 935f4495548..00000000000 --- a/vendor/gems/graphql/benchmark/run.rb +++ /dev/null @@ -1,700 +0,0 @@ -# frozen_string_literal: true -require "graphql" -ADD_WARDEN = false -require "jazz" -require "benchmark/ips" -require "stackprof" -require "memory_profiler" -require "graphql/batch" -require "securerandom" - -module GraphQLBenchmark - QUERY_STRING = GraphQL::Introspection::INTROSPECTION_QUERY - DOCUMENT = GraphQL.parse(QUERY_STRING) - SCHEMA = Jazz::Schema - - BENCHMARK_PATH = File.expand_path("../", __FILE__) - CARD_SCHEMA = GraphQL::Schema.from_definition(File.read(File.join(BENCHMARK_PATH, "schema.graphql"))) - ABSTRACT_FRAGMENTS = GraphQL.parse(File.read(File.join(BENCHMARK_PATH, "abstract_fragments.graphql"))) - ABSTRACT_FRAGMENTS_2_QUERY_STRING = File.read(File.join(BENCHMARK_PATH, "abstract_fragments_2.graphql")) - ABSTRACT_FRAGMENTS_2 = GraphQL.parse(ABSTRACT_FRAGMENTS_2_QUERY_STRING) - - BIG_SCHEMA = GraphQL::Schema.from_definition(File.join(BENCHMARK_PATH, "big_schema.graphql")) - BIG_QUERY_STRING = File.read(File.join(BENCHMARK_PATH, "big_query.graphql")) - BIG_QUERY = GraphQL.parse(BIG_QUERY_STRING) - - FIELDS_WILL_MERGE_SCHEMA = GraphQL::Schema.from_definition("type Query { hello: String }") - FIELDS_WILL_MERGE_QUERY = GraphQL.parse("{ #{Array.new(5000, "hello").join(" ")} }") - - module_function - def self.run(task) - Benchmark.ips do |x| - case task - when "query" - x.report("query") { SCHEMA.execute(document: DOCUMENT) } - when "validate" - x.report("validate - introspection ") { CARD_SCHEMA.validate(DOCUMENT) } - x.report("validate - abstract fragments") { CARD_SCHEMA.validate(ABSTRACT_FRAGMENTS) } - x.report("validate - abstract fragments 2") { CARD_SCHEMA.validate(ABSTRACT_FRAGMENTS_2) } - x.report("validate - big query") { BIG_SCHEMA.validate(BIG_QUERY) } - x.report("validate - fields will merge") { FIELDS_WILL_MERGE_SCHEMA.validate(FIELDS_WILL_MERGE_QUERY) } - when "scan" - require "graphql/c_parser" - x.report("scan c - introspection") { GraphQL.scan_with_c(QUERY_STRING) } - x.report("scan - introspection") { GraphQL.scan_with_ruby(QUERY_STRING) } - x.report("scan c - fragments") { GraphQL.scan_with_c(ABSTRACT_FRAGMENTS_2_QUERY_STRING) } - x.report("scan - fragments") { GraphQL.scan_with_ruby(ABSTRACT_FRAGMENTS_2_QUERY_STRING) } - x.report("scan c - big query") { GraphQL.scan_with_c(BIG_QUERY_STRING) } - x.report("scan - big query") { GraphQL.scan_with_ruby(BIG_QUERY_STRING) } - when "parse" - # Uncomment this to use the C parser: - # require "graphql/c_parser" - x.report("parse - introspection") { GraphQL.parse(QUERY_STRING) } - x.report("parse - fragments") { GraphQL.parse(ABSTRACT_FRAGMENTS_2_QUERY_STRING) } - x.report("parse - big query") { GraphQL.parse(BIG_QUERY_STRING) } - else - raise("Unexpected task #{task}") - end - end - end - - def self.profile_parse - # To profile the C parser instead: - # require "graphql/c_parser" - - report = MemoryProfiler.report do - GraphQL.parse(BIG_QUERY_STRING) - GraphQL.parse(QUERY_STRING) - GraphQL.parse(ABSTRACT_FRAGMENTS_2_QUERY_STRING) - end - report.pretty_print - end - - def self.validate_memory - FIELDS_WILL_MERGE_SCHEMA.validate(FIELDS_WILL_MERGE_QUERY) - - report = MemoryProfiler.report do - FIELDS_WILL_MERGE_SCHEMA.validate(FIELDS_WILL_MERGE_QUERY) - nil - end - - report.pretty_print - end - - def self.profile - # Warm up any caches: - SCHEMA.execute(document: DOCUMENT) - # CARD_SCHEMA.validate(ABSTRACT_FRAGMENTS) - res = nil - result = StackProf.run(mode: :wall) do - # CARD_SCHEMA.validate(ABSTRACT_FRAGMENTS) - res = SCHEMA.execute(document: DOCUMENT) - end - StackProf::Report.new(result).print_text - end - - def self.build_large_schema - Class.new(GraphQL::Schema) do - query_t = Class.new(GraphQL::Schema::Object) do - graphql_name("Query") - int_ts = 5.times.map do |i| - int_t = Module.new do - include GraphQL::Schema::Interface - graphql_name "Interface#{i}" - 5.times do |n2| - field :"field#{n2}", String do - argument :arg, String - end - end - end - field :"int_field_#{i}", int_t - int_t - end - - obj_ts = 100.times.map do |n| - input_obj_t = Class.new(GraphQL::Schema::InputObject) do - graphql_name("Input#{n}") - argument :arg, String - end - obj_t = Class.new(GraphQL::Schema::Object) do - graphql_name("Object#{n}") - implements(*int_ts) - 20.times do |n2| - field :"field#{n2}", String do - argument :input, input_obj_t - end - - end - field :self_field, self - field :int_0_field, int_ts[0] - end - - field :"rootfield#{n}", obj_t - obj_t - end - - 10.times do |n| - union_t = Class.new(GraphQL::Schema::Union) do - graphql_name "Union#{n}" - possible_types(*obj_ts.sample(10)) - end - field :"unionfield#{n}", union_t - end - end - query(query_t) - end - end - - def self.profile_boot - Benchmark.ips do |x| - x.config(time: 10) - x.report("Booting large schema") { - build_large_schema - } - end - - result = StackProf.run(mode: :wall, interval: 1) do - build_large_schema - end - StackProf::Report.new(result).print_text - - retained_schema = nil - report = MemoryProfiler.report do - retained_schema = build_large_schema - end - - report.pretty_print - end - - SILLY_LARGE_SCHEMA = build_large_schema - - def self.profile_small_query_on_large_schema - schema = Class.new(SILLY_LARGE_SCHEMA) - Benchmark.ips do |x| - x.report("Run small query") { - schema.execute("{ __typename }") - } - end - - result = StackProf.run(mode: :wall, interval: 1) do - schema.execute("{ __typename }") - end - StackProf::Report.new(result).print_text - - StackProf.run(mode: :wall, out: "tmp/small_query.dump", interval: 1) do - schema.execute("{ __typename }") - end - - report = MemoryProfiler.report do - schema.execute("{ __typename }") - end - puts "\n\n" - report.pretty_print - end - - def self.profile_large_introspection - schema = SILLY_LARGE_SCHEMA - Benchmark.ips do |x| - x.config(time: 10) - x.report("Run large introspection") { - schema.to_json - } - end - - result = StackProf.run(mode: :wall) do - schema.to_json - end - StackProf::Report.new(result).print_text - - retained_schema = nil - report = MemoryProfiler.report do - schema.to_json - end - puts "\n\n" - report.pretty_print - end - - def self.profile_large_analysis - query_str = "query {\n".dup - 5.times do |n| - query_str << " intField#{n} { " - 20.times do |o| - query_str << "...Obj#{o}Fields " - end - query_str << "}\n" - end - query_str << "}" - - 20.times do |o| - query_str << "fragment Obj#{o}Fields on Object#{o} { " - 20.times do |f| - query_str << " field#{f}(arg: \"a\")\n" - end - query_str << " selfField { selfField { selfField { __typename } } }\n" - # query_str << " int0Field { ...Int0Fields }" - query_str << "}\n" - end - # query_str << "fragment Int0Fields on Interface0 { __typename }" - query = GraphQL::Query.new(SILLY_LARGE_SCHEMA, query_str) - analyzers = [ - GraphQL::Analysis::AST::FieldUsage, - GraphQL::Analysis::AST::QueryDepth, - GraphQL::Analysis::AST::QueryComplexity - ] - Benchmark.ips do |x| - x.report("Running introspection") { - GraphQL::Analysis::AST.analyze_query(query, analyzers) - } - end - - StackProf.run(mode: :wall, out: "last-stackprof.dump", interval: 1) do - GraphQL::Analysis::AST.analyze_query(query, analyzers) - end - - result = StackProf.run(mode: :wall, interval: 1) do - GraphQL::Analysis::AST.analyze_query(query, analyzers) - end - - StackProf::Report.new(result).print_text - - report = MemoryProfiler.report do - GraphQL::Analysis::AST.analyze_query(query, analyzers) - end - puts "\n\n" - report.pretty_print - end - - # Adapted from https://github.com/rmosolgo/graphql-ruby/issues/861 - def self.profile_large_result - schema = ProfileLargeResult::Schema - document = ProfileLargeResult::ALL_FIELDS - Benchmark.ips do |x| - x.config(time: 10) - x.report("Querying for #{ProfileLargeResult::DATA.size} objects") { - schema.execute(document: document) - } - end - - result = StackProf.run(mode: :wall, interval: 1) do - schema.execute(document: document) - end - StackProf::Report.new(result).print_text - - report = MemoryProfiler.report do - schema.execute(document: document) - end - - report.pretty_print - end - - def self.profile_small_result - schema = ProfileLargeResult::Schema - document = GraphQL.parse <<-GRAPHQL - query { - foos(first: 5) { - __typename - id - int1 - int2 - string1 - string2 - foos(first: 5) { - __typename - string1 - string2 - foo { - __typename - int1 - } - } - } - } - GRAPHQL - - Benchmark.ips do |x| - x.config(time: 10) - x.report("Querying for #{ProfileLargeResult::DATA.size} objects") { - schema.execute(document: document) - } - end - - StackProf.run(mode: :wall, interval: 1, out: "tmp/small.dump") do - schema.execute(document: document) - end - - result = StackProf.run(mode: :wall, interval: 1) do - schema.execute(document: document) - end - StackProf::Report.new(result).print_text - - report = MemoryProfiler.report do - schema.execute(document: document) - end - - report.pretty_print - end - - def self.profile_small_introspection - schema = ProfileLargeResult::Schema - document = GraphQL.parse(GraphQL::Introspection::INTROSPECTION_QUERY) - - Benchmark.ips do |x| - x.config(time: 5) - x.report("Introspection") { - schema.execute(document: document) - } - end - - result = StackProf.run(mode: :wall, interval: 1) do - schema.execute(document: document) - end - - StackProf::Report.new(result).print_text - - report = MemoryProfiler.report do - schema.execute(document: document) - end - - report.pretty_print - end - - module ProfileLargeResult - def self.eager_or_proc(value) - ENV["EAGER"] ? value : -> { value } - end - DATA_SIZE = 1000 - DATA = DATA_SIZE.times.map { - eager_or_proc({ - id: SecureRandom.uuid, - int1: SecureRandom.random_number(100000), - int2: SecureRandom.random_number(100000), - string1: eager_or_proc(SecureRandom.base64), - string2: SecureRandom.base64, - boolean1: SecureRandom.random_number(1) == 0, - boolean2: SecureRandom.random_number(1) == 0, - int_array: eager_or_proc(10.times.map { eager_or_proc(SecureRandom.random_number(100000)) } ), - string_array: 10.times.map { SecureRandom.base64 }, - boolean_array: 10.times.map { SecureRandom.random_number(1) == 0 }, - }) - } - - module Bar - include GraphQL::Schema::Interface - field :string_array, [String], null: false - end - - module Baz - include GraphQL::Schema::Interface - implements Bar - field :int_array, [Integer], null: false - field :boolean_array, [Boolean], null: false - end - - - class ExampleExtension < GraphQL::Schema::FieldExtension - end - - class FooType < GraphQL::Schema::Object - implements Baz - field :id, ID, null: false, extensions: [ExampleExtension] - field :int1, Integer, null: false, extensions: [ExampleExtension] - field :int2, Integer, null: false, extensions: [ExampleExtension] - field :string1, String, null: false do - argument :arg1, String, required: false - argument :arg2, String, required: false - argument :arg3, String, required: false - argument :arg4, String, required: false - end - - field :string2, String, null: false do - argument :arg1, String, required: false - argument :arg2, String, required: false - argument :arg3, String, required: false - argument :arg4, String, required: false - end - - field :boolean1, Boolean, null: false do - argument :arg1, String, required: false - argument :arg2, String, required: false - argument :arg3, String, required: false - argument :arg4, String, required: false - end - field :boolean2, Boolean, null: false do - argument :arg1, String, required: false - argument :arg2, String, required: false - argument :arg3, String, required: false - argument :arg4, String, required: false - end - - field :foos, [FooType], null: false, description: "Return a list of Foo objects" do - argument :first, Integer, default_value: DATA_SIZE - end - - def foos(first:) - DATA.first(first) - end - - field :foo, FooType - def foo - DATA.sample - end - end - - class QueryType < GraphQL::Schema::Object - description "Query root of the system" - field :foos, [FooType], null: false, description: "Return a list of Foo objects" do - argument :first, Integer, default_value: DATA_SIZE - end - def foos(first:) - DATA.first(first) - end - end - - class Schema < GraphQL::Schema - query QueryType - # use GraphQL::Dataloader - lazy_resolve Proc, :call - end - - ALL_FIELDS = GraphQL.parse <<-GRAPHQL - query($skip: Boolean = false) { - foos { - id @skip(if: $skip) - int1 - int2 - string1 - string2 - boolean1 - boolean2 - stringArray - intArray - booleanArray - } - } - GRAPHQL - end - - def self.profile_to_definition - require_relative "./batch_loading" - schema = ProfileLargeResult::Schema - schema.to_definition - - Benchmark.ips do |x| - x.report("to_definition") { schema.to_definition } - end - - result = StackProf.run(mode: :wall, interval: 1) do - schema.to_definition - end - StackProf::Report.new(result).print_text - - report = MemoryProfiler.report do - schema.to_definition - end - - report.pretty_print - end - - def self.profile_from_definition - # require "graphql/c_parser" - schema_str = SILLY_LARGE_SCHEMA.to_definition - - Benchmark.ips do |x| - x.report("from_definition") { GraphQL::Schema.from_definition(schema_str) } - end - - result = StackProf.run(mode: :wall, interval: 1) do - GraphQL::Schema.from_definition(schema_str) - end - StackProf::Report.new(result).print_text - - report = MemoryProfiler.report do - GraphQL::Schema.from_definition(schema_str) - end - - report.pretty_print - end - - def self.profile_batch_loaders - require_relative "./batch_loading" - include BatchLoading - - document = GraphQL.parse <<-GRAPHQL - { - braves: team(name: "Braves") { ...TeamFields } - bulls: team(name: "Bulls") { ...TeamFields } - } - - fragment TeamFields on Team { - players { - team { - players { - team { - name - } - } - } - } - } - GRAPHQL - batch_result = GraphQLBatchSchema.execute(document: document).to_h - dataloader_result = GraphQLDataloaderSchema.execute(document: document).to_h - no_batch_result = GraphQLNoBatchingSchema.execute(document: document).to_h - - results = [batch_result, dataloader_result, no_batch_result].uniq - if results.size > 1 - puts "Batch result:" - pp batch_result - puts "Dataloader result:" - pp dataloader_result - puts "No-batch result:" - pp no_batch_result - raise "Got different results -- fix implementation before benchmarking." - end - - Benchmark.ips do |x| - x.report("GraphQL::Batch") { GraphQLBatchSchema.execute(document: document) } - x.report("GraphQL::Dataloader") { GraphQLDataloaderSchema.execute(document: document) } - x.report("No Batching") { GraphQLNoBatchingSchema.execute(document: document) } - - x.compare! - end - - puts "========== GraphQL-Batch Memory ==============" - report = MemoryProfiler.report do - GraphQLBatchSchema.execute(document: document) - end - - report.pretty_print - - puts "========== Dataloader Memory =================" - report = MemoryProfiler.report do - GraphQLDataloaderSchema.execute(document: document) - end - - report.pretty_print - - puts "========== No Batch Memory ==============" - report = MemoryProfiler.report do - GraphQLNoBatchingSchema.execute(document: document) - end - - report.pretty_print - end - - def self.profile_schema_memory_footprint - schema = nil - report = MemoryProfiler.report do - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name "Query" - 100.times do |i| - type = Class.new(GraphQL::Schema::Object) do - graphql_name "Object#{i}" - field :f, Integer - end - field "f#{i}", type - end - end - - thing_type = Class.new(GraphQL::Schema::Object) do - graphql_name "Thing" - field :name, String - end - - mutation_type = Class.new(GraphQL::Schema::Object) do - graphql_name "Mutation" - 100.times do |i| - mutation_class = Class.new(GraphQL::Schema::RelayClassicMutation) do - graphql_name "Do#{i}" - argument :id, "ID" - field :thing, thing_type - field :things, thing_type.connection_type - end - field "f#{i}", mutation: mutation_class - end - end - - schema = Class.new(GraphQL::Schema) do - query(query_type) - mutation(mutation_type) - end - end - - report.pretty_print - end - - class StackDepthSchema < GraphQL::Schema - class Thing < GraphQL::Schema::Object - field :thing, self do - argument :lazy, Boolean, default_value: false - end - - def thing(lazy:) - if lazy - -> { :something } - else - :something - end - end - - field :stack_trace_depth, Integer do - argument :lazy, Boolean, default_value: false - end - - def stack_trace_depth(lazy:) - get_depth = -> { - graphql_caller = caller.select { |c| c.include?("graphql") } - graphql_caller.size - } - - if lazy - get_depth - else - get_depth.call - end - end - end - - class Query < GraphQL::Schema::Object - field :thing, Thing - - def thing - :something - end - end - - query(Query) - lazy_resolve(Proc, :call) - end - - def self.profile_stack_depth - query_str = <<-GRAPHQL - query($lazyThing: Boolean!, $lazyStackTrace: Boolean!) { - thing { - thing(lazy: $lazyThing) { - thing(lazy: $lazyThing) { - thing(lazy: $lazyThing) { - thing(lazy: $lazyThing) { - stackTraceDepth(lazy: $lazyStackTrace) - } - } - } - } - } - } - GRAPHQL - - eager_res = StackDepthSchema.execute(query_str, variables: { lazyThing: false, lazyStackTrace: false }) - lazy_res = StackDepthSchema.execute(query_str, variables: { lazyThing: true, lazyStackTrace: false }) - very_lazy_res = StackDepthSchema.execute(query_str, variables: { lazyThing: true, lazyStackTrace: true }) - get_depth = ->(result) { result["data"]["thing"]["thing"]["thing"]["thing"]["thing"]["stackTraceDepth"] } - - puts <<~RESULT - Result Depth - --------------------- - Eager #{get_depth.call(eager_res)} - Lazy #{get_depth.call(lazy_res)} - Very Lazy #{get_depth.call(very_lazy_res)} - RESULT - end -end diff --git a/vendor/gems/graphql/benchmark/schema.graphql b/vendor/gems/graphql/benchmark/schema.graphql deleted file mode 100644 index d67b8d5b499..00000000000 --- a/vendor/gems/graphql/benchmark/schema.graphql +++ /dev/null @@ -1,118 +0,0 @@ -# A big schema for testing -type Query { - node(id: ID!): Node -} - -interface Node { - id: ID! -} - -interface Node2 { - id: ID -} - -interface Commentable { - id: ID! - comments: [Comment!]! -} - -interface Named { - name: String! -} -type Comment implements Node { - author: Player - body: String! - id: ID! -} - -type Card implements Node, Commentable, Node2, Named { - name: String! - converted_mana_cost: Int! - mana_cost: String! - colors: [Color!]! - power: Int - toughness: Int - rules_text: String! - id: ID! - comments: [Comment!]! -} - -type Printing implements Node, Commentable, Node2 { - card: Card! - expansion: Expansion! - rarity: Rarity! - artist: Artist! - id: ID! - comments: [Comment!]! -} - -type Expansion implements Node, Commentable, Named { - name: String! - code: String! - printings: [Printing!]! - block: Block! - id: ID! - comments: [Comment!]! -} - -type Block implements Node, Commentable, Named { - name: String! - expansions: [Expansion!]! - id: ID! - comments: [Comment!]! -} - -# Eg shard, guild, clan -type Watermark implements Node, Commentable, Named { - name: String! - cards: [Card!]! - colors: [Color!]! - id: ID! - comments: [Comment!]! -} - -type Artist implements Node, Commentable, Named { - name: String! - printings: [Printing!]! - id: ID! - comments: [Comment!]! -} - -type Player implements Node, Commentable, Named { - name: String! - decks: [Deck!]! - id: ID! - comments: [Comment!]! -} - -type Deck implements Node, Commentable, Named { - name: String! - colors: [Color!]! - slots: [Slot!]! - id: ID! - comments: [Comment!]! -} - -type Slot implements Node, Commentable { - deck: Deck! - card: Card! - id: ID! - comments: [Comment!]! -} - -enum Color { - WHITE - BLUE - BLACK - RED - GREEN - COLORLESS -} - -enum Rarity { - COMMON - UNCOMMON - RARE - MYTHIC_RARE - TIMESHIFTED -} diff --git a/vendor/gems/graphql/cop/development/context_is_passed_cop.rb b/vendor/gems/graphql/cop/development/context_is_passed_cop.rb deleted file mode 100644 index 9d8049fa9c2..00000000000 --- a/vendor/gems/graphql/cop/development/context_is_passed_cop.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true -require 'rubocop' - -module Cop - module Development - class ContextIsPassedCop < RuboCop::Cop::Base - MSG = <<-MSG -This method also accepts `context` as an argument. Pass it so that the returned value will reflect the current query, or use another method that isn't context-dependent. -MSG - - # These are already context-aware or else not query-related - def_node_matcher :likely_query_specific_receiver?, " - { - (send _ {:ast_node :query :context :warden :ctx :query_ctx :query_context}) - (lvar {:ast_node :query :context :warden :ctx :query_ctx :query_context}) - (ivar {:@query :@context :@warden}) - (send _ {:introspection_system}) - } - " - - def_node_matcher :method_doesnt_receive_second_context_argument?, <<-MATCHER - (send _ {:get_field :get_argument :get_type} _) - MATCHER - - def_node_matcher :method_doesnt_receive_first_context_argument?, <<-MATCHER - (send _ {:fields :arguments :types :enum_values}) - MATCHER - - def_node_matcher :is_enum_values_call_without_arguments?, " - (send (send _ {:enum :enum_type (ivar {:@enum :@enum_type})}) {:values}) - " - - def on_send(node) - if ( - method_doesnt_receive_second_context_argument?(node) || - method_doesnt_receive_first_context_argument?(node) || - is_enum_values_call_without_arguments?(node) - ) && !likely_query_specific_receiver?(node.to_a[0]) - add_offense(node) - end - end - end - end -end diff --git a/vendor/gems/graphql/cop/development/no_eval_cop.rb b/vendor/gems/graphql/cop/development/no_eval_cop.rb deleted file mode 100644 index fd3ce237688..00000000000 --- a/vendor/gems/graphql/cop/development/no_eval_cop.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -require 'rubocop' - -module Cop - module Development - class NoEvalCop < RuboCop::Cop::Base - MSG_TEMPLATE = "Don't use `%{eval_method_name}` which accepts strings and may result evaluating unexpected code. Use `%{exec_method_name}` instead, and pass a block." - - def on_send(node) - case node.method_name - when :module_eval, :class_eval, :instance_eval - message = MSG_TEMPLATE % { eval_method_name: node.method_name, exec_method_name: node.method_name.to_s.sub("eval", "exec").to_sym } - add_offense node, message: message - end - end - end - end -end diff --git a/vendor/gems/graphql/cop/development/no_focus_cop.rb b/vendor/gems/graphql/cop/development/no_focus_cop.rb deleted file mode 100644 index 86228234298..00000000000 --- a/vendor/gems/graphql/cop/development/no_focus_cop.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -require 'rubocop' - -module Cop - module Development - # Make sure no tests are focused, from https://github.com/rubocop-hq/rubocop/issues/3773#issuecomment-420662102 - class NoFocusCop < RuboCop::Cop::Base - MSG = 'Remove `focus` from tests.' - - def_node_matcher :focused?, <<-MATCHER - (send nil? :focus) - MATCHER - - def on_send(node) - return unless focused?(node) - - add_offense node - end - end - end -end diff --git a/vendor/gems/graphql/cop/development/none_without_block_cop.rb b/vendor/gems/graphql/cop/development/none_without_block_cop.rb deleted file mode 100644 index 73b428c700d..00000000000 --- a/vendor/gems/graphql/cop/development/none_without_block_cop.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true -require 'rubocop' - -module Cop - module Development - # A custom Rubocop rule to catch uses of `.none?` without a block. - # - # @see https://github.com/rmosolgo/graphql-ruby/pull/2090 - class NoneWithoutBlockCop < RuboCop::Cop::Base - MSG = <<-MD -Instead of `.none?` or `.any?` without a block: - -- Use `.empty?` to check for an empty collection (faster) -- Add a block to explicitly check for `false` (more clear) - -Run `-a` to replace this with `%{bang}.empty?`. - MD - def on_block(node) - # Since this method was called with a block, it can't be - # a case of `.none?` without a block - ignore_node(node.send_node) - end - - def on_send(node) - if !ignored_node?(node) && (node.method_name == :none? || node.method_name == :any?) && node.arguments.size == 0 - add_offense(node, message: MSG % { bang: node.method_name == :none? ? "" : "!.." } ) - end - end - - def autocorrect(node) - lambda do |corrector| - if node.method_name == :none? - corrector.replace(node.location.selector, "empty?") - else - # Backtrack to any chained method calls so we can insert `!` before them - full_exp = node - while node.parent.send_type? - full_exp = node.parent - end - new_source = "!" + full_exp.source_range.source.sub("any?", "empty?") - corrector.replace(full_exp, new_source) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/cop/development/trace_methods_cop.rb b/vendor/gems/graphql/cop/development/trace_methods_cop.rb deleted file mode 100644 index 1d99f69b483..00000000000 --- a/vendor/gems/graphql/cop/development/trace_methods_cop.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true -require 'rubocop' - -module Cop - module Development - class TraceMethodsCop < RuboCop::Cop::Base - extend RuboCop::Cop::AutoCorrector - - TRACE_HOOKS = [ - :analyze_multiplex, - :analyze_query, - :authorized, - :authorized_lazy, - :begin_analyze_multiplex, - :begin_authorized, - :begin_dataloader, - :begin_dataloader_source, - :begin_execute_field, - :begin_execute_multiplex, - :begin_parse, - :begin_resolve_type, - :begin_validate, - :dataloader_fiber_exit, - :dataloader_fiber_resume, - :dataloader_fiber_yield, - :dataloader_spawn_execution_fiber, - :dataloader_spawn_source_fiber, - :end_analyze_multiplex, - :end_authorized, - :end_dataloader, - :end_dataloader_source, - :end_execute_field, - :end_execute_multiplex, - :end_parse, - :end_resolve_type, - :end_validate, - :execute_field, - :execute_field_lazy, - :execute_multiplex, - :execute_query, - :execute_query_lazy, - :lex, - :parse, - :resolve_type, - :resolve_type_lazy, - :validate, - ] - - MSG = "Trace methods should call `super` to pass control to other traces" - - def on_def(node) - if TRACE_HOOKS.include?(node.method_name) && !node.each_descendant(:super, :zsuper).any? - add_offense(node) do |corrector| - if node.body - offset = node.loc.column + 2 - corrector.insert_after(node.body.loc.expression, "\n#{' ' * offset}super") - end - end - end - end - - def on_module(node) - if node.defined_module_name.to_s.end_with?("Trace") - all_defs = [] - node.body.each_child_node do |body_node| - if body_node.def_type? - all_defs << body_node.method_name - end - end - - missing_defs = TRACE_HOOKS - all_defs - redundant_defs = [ - # Not really necessary for making a good trace: - :lex, :analyze_query, :execute_query, :execute_query_lazy, - # Only useful for isolated event tracking: - :dataloader_fiber_exit, :dataloader_spawn_execution_fiber, :dataloader_spawn_source_fiber - ] - missing_defs.each do |missing_def| - if all_defs.include?(:"begin_#{missing_def}") && all_defs.include?(:"end_#{missing_def}") - redundant_defs << missing_def - redundant_defs << :"#{missing_def}_lazy" - end - end - - missing_defs -= redundant_defs - if missing_defs.any? - add_offense(node, message: "Missing some trace hook methods:\n\n- #{missing_defs.join("\n- ")}") - end - end - end - end - end -end diff --git a/vendor/gems/graphql/gemfiles/mongoid_8.gemfile b/vendor/gems/graphql/gemfiles/mongoid_8.gemfile deleted file mode 100644 index 854056dbe84..00000000000 --- a/vendor/gems/graphql/gemfiles/mongoid_8.gemfile +++ /dev/null @@ -1,17 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem 'logger' -gem "bootsnap" -gem "ruby-prof", platform: :ruby -gem "pry" -gem "pry-stack_explorer", platform: :ruby -gem "mongoid", "~> 8.0" -gem "libev_scheduler" -gem "evt" -gem "async" -gem "fiber-storage" -gem "concurrent-ruby", "1.3.4" - -gemspec path: "../" diff --git a/vendor/gems/graphql/gemfiles/mongoid_9.gemfile b/vendor/gems/graphql/gemfiles/mongoid_9.gemfile deleted file mode 100644 index cda8f0009bc..00000000000 --- a/vendor/gems/graphql/gemfiles/mongoid_9.gemfile +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "bootsnap" -gem "ruby-prof", platform: :ruby -gem "pry" -gem "pry-stack_explorer", platform: :ruby -gem "mongoid", "~> 9.0" -gem "libev_scheduler" -gem "evt" -gem "async" -gem "fiber-storage" - -gemspec path: "../" diff --git a/vendor/gems/graphql/gemfiles/rails_7.0.gemfile b/vendor/gems/graphql/gemfiles/rails_7.0.gemfile deleted file mode 100644 index ce06716aac4..00000000000 --- a/vendor/gems/graphql/gemfiles/rails_7.0.gemfile +++ /dev/null @@ -1,18 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "bootsnap" -gem "ruby-prof", platform: :ruby -gem "pry" -gem "pry-stack_explorer", platform: :ruby -gem "rails", "~> 7.0.0", require: "rails/all" -gem "sqlite3", "~> 1.4", platform: :ruby -gem "sequel" -gem "evt" -gem "async" -gem "concurrent-ruby", "1.3.4" -gem "google-protobuf" -gem "drb" - -gemspec path: "../" diff --git a/vendor/gems/graphql/gemfiles/rails_7.1.gemfile b/vendor/gems/graphql/gemfiles/rails_7.1.gemfile deleted file mode 100644 index f2dc21a6d20..00000000000 --- a/vendor/gems/graphql/gemfiles/rails_7.1.gemfile +++ /dev/null @@ -1,17 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "bootsnap" -gem "ruby-prof", platform: :ruby -gem "pry" -gem "pry-stack_explorer", platform: :ruby -gem "rails", "~> 7.1.0", require: "rails/all" -gem "sqlite3", "~>1.4" -gem "pg", platform: :ruby -gem "sequel" -gem "evt" -gem "async" -gem "google-protobuf" - -gemspec path: "../" diff --git a/vendor/gems/graphql/gemfiles/rails_7.1_postgresql.gemfile b/vendor/gems/graphql/gemfiles/rails_7.1_postgresql.gemfile deleted file mode 100644 index 6311acf21c9..00000000000 --- a/vendor/gems/graphql/gemfiles/rails_7.1_postgresql.gemfile +++ /dev/null @@ -1,17 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "bootsnap" -gem "ruby-prof", platform: :ruby -gem "pry" -gem "pry-stack_explorer", platform: :ruby -gem "rails", "~> 7.1.0", require: "rails/all" -gem "pg", platform: :ruby -gem "sequel" -gem "evt" -gem "async" -gem "libev_scheduler" -gem "google-protobuf" - -gemspec path: "../" diff --git a/vendor/gems/graphql/gemfiles/rails_master.gemfile b/vendor/gems/graphql/gemfiles/rails_master.gemfile deleted file mode 100644 index 7ee8bf3a7e6..00000000000 --- a/vendor/gems/graphql/gemfiles/rails_master.gemfile +++ /dev/null @@ -1,26 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "bootsnap" -gem "ruby-prof", platform: :ruby -gem "pry" -gem "pry-stack_explorer", platform: :ruby -gem "rails", github: "rails/rails", require: "rails/all", ref: "main" -gem 'sqlite3' -gem 'pg' -gem "sequel" -gem "evt" -if RUBY_ENGINE == "ruby" # This doesn't work on truffle-ruby because there's no `IO::READABLE` - gem "libev_scheduler" -end -gem "async" -gem "google-protobuf" -gem "redis" - -gem 'puma' -gem 'sprockets-rails' -gem 'capybara' -gem 'selenium-webdriver' - -gemspec path: "../" diff --git a/vendor/gems/graphql/graphql-c_parser/CHANGELOG.md b/vendor/gems/graphql/graphql-c_parser/CHANGELOG.md deleted file mode 100644 index 5bc4830851e..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/CHANGELOG.md +++ /dev/null @@ -1,43 +0,0 @@ -# GraphQL::CParser - -## 1.1.2 - -- Fix to handle strings with null bytes #5193 - -## 1.1.1 - -- Add support for `Schema.max_query_string_tokens` #4929 - -## 1.1.0 - -- Drop support for Ruby 2.7 #4899 -- Reduce allocation of repeated strings for identifiers when parsing schemas #4899 - -## 1.0.8 - -- Support directives on variable definitions, requires `graphql` 2.2.10+ #4847 - -## 1.0.5 - -- Properly parse integers with leading zeros as Integers, not Floats #4556 - -## 1.0.4 - -- Use UTF-8 encoding for static strings #4526 - -## 1.0.3 - -- Raise a `ParseError` on bad Unicode escapes (like the Ruby parser) #4514 -- Force UTF-8 encoding (like the Ruby parser) #4467 - -## 1.0.2 - -- Remove `.y` and `.rl` files to avoid triggering build tasks during install - -## 1.0.1 - -- Fix gem files (to include `ext`) - -## 1.0.0 - -- Release GraphQL::CParser diff --git a/vendor/gems/graphql/graphql-c_parser/Rakefile b/vendor/gems/graphql/graphql-c_parser/Rakefile deleted file mode 100644 index 35cb636a9e2..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/Rakefile +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -require "bundler/gem_helper" - -# use a custom tag to avoid conflicting with GraphQL-Ruby tags in the same git repo -class CustomGemHelper < Bundler::GemHelper - def version_tag - "graphql-c_parser-v#{version}" - end -end -CustomGemHelper.install_tasks diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/extconf.rb b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/extconf.rb deleted file mode 100644 index 4deacc9eec3..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/extconf.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -require 'mkmf' - -create_makefile 'graphql/graphql_c_parser_ext' diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.c b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.c deleted file mode 100644 index c8a67aede9b..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "graphql_c_parser_ext.h" - -VALUE GraphQL_CParser_Lexer_tokenize_with_c_internal(VALUE self, VALUE query_string, VALUE fstring_identifiers, VALUE reject_numbers_followed_by_names, VALUE max_tokens) { - return tokenize(query_string, RTEST(fstring_identifiers), RTEST(reject_numbers_followed_by_names), FIX2INT(max_tokens)); -} - -VALUE GraphQL_CParser_Parser_c_parse(VALUE self) { - yyparse(self, rb_ivar_get(self, rb_intern("@filename"))); - return Qnil; -} - -void Init_graphql_c_parser_ext() { - VALUE GraphQL = rb_define_module("GraphQL"); - VALUE CParser = rb_define_module_under(GraphQL, "CParser"); - VALUE Lexer = rb_define_module_under(CParser, "Lexer"); - rb_define_singleton_method(Lexer, "tokenize_with_c_internal", GraphQL_CParser_Lexer_tokenize_with_c_internal, 4); - setup_static_token_variables(); - - VALUE Parser = rb_define_class_under(CParser, "Parser", rb_cObject); - rb_define_method(Parser, "c_parse", GraphQL_CParser_Parser_c_parse, 0); - initialize_node_class_variables(); -} diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.h b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.h deleted file mode 100644 index 412b395569f..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/graphql_c_parser_ext.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef Graphql_ext_h -#define Graphql_ext_h -#include -#include -#include "lexer.h" -#include "parser.h" -void Init_graphql_c_parser_ext(); -#endif diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.c b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.c deleted file mode 100644 index ec29aba07ed..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.c +++ /dev/null @@ -1,2040 +0,0 @@ -#line 1 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - -#line 106 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - - - -#line 8 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" -static const char _graphql_c_lexer_trans_keys[] = { - 1, 22, 4, 43, 14, 47, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 49, 4, 22, - 4, 4, 4, 4, 4, 22, 4, 4, - 4, 4, 14, 15, 14, 15, 10, 15, - 12, 12, 0, 49, 0, 0, 1, 22, - 4, 4, 4, 4, 4, 4, 4, 22, - 4, 4, 4, 4, 1, 1, 14, 15, - 12, 12, 10, 29, 14, 15, 12, 15, - 12, 12, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 14, 46, 14, 46, 14, 46, 14, 46, - 0 -}; - -static const signed char _graphql_c_lexer_char_class[] = { - 0, 1, 2, 2, 1, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 0, - 3, 4, 5, 6, 2, 7, 2, 8, - 9, 2, 10, 0, 11, 12, 13, 14, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 16, 2, 2, 17, 2, 2, 18, - 19, 19, 19, 19, 20, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 21, 22, 23, 2, 24, 2, - 25, 26, 27, 28, 29, 30, 31, 32, - 33, 19, 19, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 19, 45, - 46, 19, 47, 48, 49, 0 -}; - -static const short _graphql_c_lexer_index_offsets[] = { - 0, 22, 62, 96, 129, 162, 195, 228, - 261, 294, 327, 363, 382, 383, 384, 403, - 404, 405, 407, 409, 415, 416, 466, 467, - 489, 490, 491, 492, 511, 512, 513, 514, - 516, 517, 537, 539, 543, 544, 577, 610, - 643, 676, 709, 742, 775, 808, 841, 874, - 907, 940, 973, 1006, 1039, 1072, 1105, 1138, - 1171, 1204, 1237, 1270, 1303, 1336, 1369, 1402, - 1435, 1468, 1501, 1534, 1567, 1600, 1633, 1666, - 1699, 1732, 1765, 1798, 1831, 1864, 1897, 1930, - 1963, 1996, 2029, 2062, 2095, 2128, 2161, 2194, - 2227, 2260, 2293, 2326, 2359, 2392, 2425, 2458, - 2491, 2524, 2557, 2590, 2623, 2656, 2689, 2722, - 2755, 2788, 2821, 2854, 2887, 2920, 2953, 2986, - 3019, 3052, 3085, 3118, 3151, 3184, 3217, 3250, - 3283, 3316, 3349, 3382, 3415, 3448, 3481, 3514, - 3547, 3580, 3613, 3646, 0 -}; - -static const short _graphql_c_lexer_indices[] = { - 0, 1, 1, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 3, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 1, 4, 5, 5, - 0, 0, 0, 5, 5, 0, 0, 0, - 0, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 6, - 7, 7, 0, 0, 0, 7, 7, 0, - 0, 0, 0, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 8, 8, 0, 0, 0, 8, 8, - 0, 0, 0, 0, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 1, 1, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 9, 9, 0, 0, 0, - 9, 9, 0, 0, 0, 0, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 10, 10, 0, 0, - 0, 10, 10, 0, 0, 0, 0, 10, - 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 11, 11, 0, - 0, 0, 11, 11, 0, 0, 0, 0, - 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 12, 12, - 0, 0, 0, 12, 12, 0, 0, 0, - 0, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, - 12, 0, 0, 0, 12, 12, 0, 0, - 0, 0, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, - 0, 0, 1, 15, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 16, 17, 18, - 19, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 16, 20, 21, 23, 23, 25, - 25, 26, 26, 24, 24, 25, 25, 27, - 30, 31, 29, 32, 33, 34, 35, 36, - 37, 38, 29, 39, 40, 29, 41, 42, - 43, 44, 45, 46, 46, 47, 29, 48, - 46, 46, 46, 46, 49, 50, 51, 46, - 46, 52, 46, 53, 54, 55, 46, 56, - 57, 58, 59, 60, 46, 46, 46, 61, - 62, 63, 30, 65, 1, 1, 66, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 3, 14, 69, 70, 71, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 16, 72, - 18, 73, 41, 42, 75, 26, 26, 76, - 76, 23, 23, 76, 76, 76, 76, 77, - 76, 76, 76, 76, 76, 76, 76, 76, - 77, 25, 25, 75, 74, 42, 42, 78, - 46, 46, 13, 13, 13, 46, 46, 13, - 13, 13, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 80, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 81, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 82, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 83, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 84, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 85, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 86, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 87, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 88, - 46, 46, 46, 46, 46, 46, 46, 46, - 89, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 90, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 91, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 92, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 93, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 94, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 95, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 96, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 97, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 98, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 99, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 100, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 101, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 102, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 103, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 104, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 105, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 106, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 107, - 108, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 109, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 110, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 111, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 112, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 113, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 114, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 115, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 116, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 117, 46, 46, 46, 118, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 119, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 120, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 121, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 122, 46, 46, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 46, - 123, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 124, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 125, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 126, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 127, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 128, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 129, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 130, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 131, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 132, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 133, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 134, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 135, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 136, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 137, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 138, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 139, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 140, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 141, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 142, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 143, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 144, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 145, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 146, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 147, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 148, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 149, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 150, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 151, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 152, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 153, 46, 46, 46, 46, 46, 46, 154, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 155, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 156, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 157, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 158, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 159, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 160, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 161, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 162, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 163, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 164, 46, 46, 46, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 165, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 166, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 167, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 168, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 169, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 170, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 171, 46, 46, 46, 46, 46, 172, 46, - 46, 79, 79, 79, 46, 46, 79, 79, - 79, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 173, 46, 46, 46, - 46, 46, 79, 79, 79, 46, 46, 79, - 79, 79, 46, 46, 46, 46, 46, 174, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 79, 79, 79, 46, 46, - 79, 79, 79, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 175, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 79, 79, 79, 46, - 46, 79, 79, 79, 46, 46, 46, 46, - 46, 176, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 79, 79, 79, - 46, 46, 79, 79, 79, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 177, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 79, 79, - 79, 46, 46, 79, 79, 79, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 178, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 79, - 79, 79, 46, 46, 79, 79, 79, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 179, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 79, 79, 79, 46, 46, 79, 79, 79, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 180, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 0 -}; - -static const signed char _graphql_c_lexer_index_defaults[] = { - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 14, 14, 14, 14, 14, - 14, 22, 24, 24, 0, 29, 64, 1, - 67, 68, 68, 14, 14, 14, 34, 65, - 74, 76, 76, 74, 65, 13, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 0 -}; - -static const short _graphql_c_lexer_cond_targs[] = { - 21, 0, 21, 1, 2, 3, 6, 4, - 5, 7, 8, 9, 10, 21, 11, 12, - 14, 13, 25, 15, 16, 27, 21, 33, - 21, 34, 18, 21, 21, 21, 22, 21, - 21, 23, 30, 21, 21, 21, 21, 31, - 36, 32, 35, 21, 21, 21, 37, 21, - 21, 38, 46, 53, 63, 81, 88, 91, - 92, 96, 105, 123, 128, 21, 21, 21, - 21, 21, 24, 21, 21, 26, 21, 28, - 29, 21, 21, 17, 21, 19, 20, 21, - 39, 40, 41, 42, 43, 44, 45, 37, - 47, 49, 48, 37, 50, 51, 52, 37, - 54, 57, 55, 56, 37, 58, 59, 60, - 61, 62, 37, 64, 72, 65, 66, 67, - 68, 69, 70, 71, 37, 73, 75, 74, - 37, 76, 77, 78, 79, 80, 37, 82, - 83, 84, 85, 86, 87, 37, 89, 90, - 37, 37, 93, 94, 95, 37, 97, 98, - 99, 100, 101, 102, 103, 104, 37, 106, - 113, 107, 110, 108, 109, 37, 111, 112, - 37, 114, 115, 116, 117, 118, 119, 120, - 121, 122, 37, 124, 126, 125, 37, 127, - 37, 129, 130, 131, 37, 0 -}; - -static const signed char _graphql_c_lexer_cond_actions[] = { - 1, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 4, 5, 6, - 7, 0, 0, 8, 0, 11, 0, 12, - 13, 6, 0, 14, 15, 16, 17, 0, - 6, 6, 6, 18, 19, 20, 21, 22, - 23, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 25, 26, - 27, 28, 29, 30, 31, 0, 32, 4, - 4, 33, 34, 0, 35, 0, 0, 36, - 0, 0, 0, 0, 0, 0, 0, 37, - 0, 0, 0, 38, 0, 0, 0, 39, - 0, 0, 0, 0, 40, 0, 0, 0, - 0, 0, 41, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 42, 0, 0, 0, - 43, 0, 0, 0, 0, 0, 44, 0, - 0, 0, 0, 0, 0, 45, 0, 0, - 46, 47, 0, 0, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, 49, 0, - 0, 0, 0, 0, 0, 50, 0, 0, - 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 52, 0, 0, 0, 53, 0, - 54, 0, 0, 0, 55, 0 -}; - -static const signed char _graphql_c_lexer_to_state_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 9, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 -}; - -static const signed char _graphql_c_lexer_from_state_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 -}; - -static const signed char _graphql_c_lexer_eof_trans[] = { - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 14, 14, 14, 14, 14, - 14, 23, 25, 25, 1, 29, 65, 66, - 68, 69, 69, 69, 69, 69, 74, 66, - 75, 77, 77, 75, 66, 14, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 0 -}; - -static const int graphql_c_lexer_start = 21; -static const int graphql_c_lexer_first_final = 21; -static const int graphql_c_lexer_error = -1; - -static const int graphql_c_lexer_en_main = 21; - - -#line 108 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - - -#include -#include - -#define INIT_STATIC_TOKEN_VARIABLE(token_name) \ -static VALUE GraphQLTokenString##token_name; - -INIT_STATIC_TOKEN_VARIABLE(ON) -INIT_STATIC_TOKEN_VARIABLE(FRAGMENT) -INIT_STATIC_TOKEN_VARIABLE(QUERY) -INIT_STATIC_TOKEN_VARIABLE(MUTATION) -INIT_STATIC_TOKEN_VARIABLE(SUBSCRIPTION) -INIT_STATIC_TOKEN_VARIABLE(REPEATABLE) -INIT_STATIC_TOKEN_VARIABLE(RCURLY) -INIT_STATIC_TOKEN_VARIABLE(LCURLY) -INIT_STATIC_TOKEN_VARIABLE(RBRACKET) -INIT_STATIC_TOKEN_VARIABLE(LBRACKET) -INIT_STATIC_TOKEN_VARIABLE(RPAREN) -INIT_STATIC_TOKEN_VARIABLE(LPAREN) -INIT_STATIC_TOKEN_VARIABLE(COLON) -INIT_STATIC_TOKEN_VARIABLE(VAR_SIGN) -INIT_STATIC_TOKEN_VARIABLE(DIR_SIGN) -INIT_STATIC_TOKEN_VARIABLE(ELLIPSIS) -INIT_STATIC_TOKEN_VARIABLE(EQUALS) -INIT_STATIC_TOKEN_VARIABLE(BANG) -INIT_STATIC_TOKEN_VARIABLE(PIPE) -INIT_STATIC_TOKEN_VARIABLE(AMP) -INIT_STATIC_TOKEN_VARIABLE(SCHEMA) -INIT_STATIC_TOKEN_VARIABLE(SCALAR) -INIT_STATIC_TOKEN_VARIABLE(EXTEND) -INIT_STATIC_TOKEN_VARIABLE(IMPLEMENTS) -INIT_STATIC_TOKEN_VARIABLE(INTERFACE) -INIT_STATIC_TOKEN_VARIABLE(UNION) -INIT_STATIC_TOKEN_VARIABLE(ENUM) -INIT_STATIC_TOKEN_VARIABLE(DIRECTIVE) -INIT_STATIC_TOKEN_VARIABLE(INPUT) - -static VALUE GraphQL_type_str; -static VALUE GraphQL_true_str; -static VALUE GraphQL_false_str; -static VALUE GraphQL_null_str; -typedef enum TokenType { - AMP, - BANG, - COLON, - DIRECTIVE, - DIR_SIGN, - ENUM, - ELLIPSIS, - EQUALS, - EXTEND, - FALSE_LITERAL, - FLOAT, - FRAGMENT, - IDENTIFIER, - INPUT, - IMPLEMENTS, - INT, - INTERFACE, - LBRACKET, - LCURLY, - LPAREN, - MUTATION, - NULL_LITERAL, - ON, - PIPE, - QUERY, - RBRACKET, - RCURLY, - REPEATABLE, - RPAREN, - SCALAR, - SCHEMA, - STRING, - SUBSCRIPTION, - TRUE_LITERAL, - TYPE_LITERAL, - UNION, - VAR_SIGN, - BLOCK_STRING, - QUOTED_STRING, - UNKNOWN_CHAR, - COMMENT, - BAD_UNICODE_ESCAPE -} TokenType; - -typedef struct Meta { - int line; - int col; - char *query_cstr; - char *pe; - VALUE tokens; - int dedup_identifiers; - int reject_numbers_followed_by_names; - int preceeded_by_number; - int max_tokens; - int tokens_count; -} Meta; - -#define STATIC_VALUE_TOKEN(token_type, content_str) \ -case token_type: \ -token_sym = ID2SYM(rb_intern(#token_type)); \ -token_content = GraphQLTokenString##token_type; \ -break; - -#define DYNAMIC_VALUE_TOKEN(token_type) \ -case token_type: \ -token_sym = ID2SYM(rb_intern(#token_type)); \ -token_content = rb_utf8_str_new(ts, te - ts); \ -break; - -void emit(TokenType tt, char *ts, char *te, Meta *meta) { - meta->tokens_count++; - // -1 indicates that there is no limit: - if (meta->max_tokens > 0 && meta->tokens_count > meta->max_tokens) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE cParseError = rb_const_get_at(mGraphQL, rb_intern("ParseError")); - VALUE exception = rb_funcall( - cParseError, rb_intern("new"), 4, - rb_str_new_cstr("This query is too large to execute."), - LONG2NUM(meta->line), - LONG2NUM(meta->col), - rb_str_new_cstr(meta->query_cstr) - ); - rb_exc_raise(exception); - } - int quotes_length = 0; // set by string tokens below - int line_incr = 0; - VALUE token_sym = Qnil; - VALUE token_content = Qnil; - int this_token_is_number = 0; - switch(tt) { - STATIC_VALUE_TOKEN(ON, "on") - STATIC_VALUE_TOKEN(FRAGMENT, "fragment") - STATIC_VALUE_TOKEN(QUERY, "query") - STATIC_VALUE_TOKEN(MUTATION, "mutation") - STATIC_VALUE_TOKEN(SUBSCRIPTION, "subscription") - STATIC_VALUE_TOKEN(REPEATABLE, "repeatable") - STATIC_VALUE_TOKEN(RCURLY, "}") - STATIC_VALUE_TOKEN(LCURLY, "{") - STATIC_VALUE_TOKEN(RBRACKET, "]") - STATIC_VALUE_TOKEN(LBRACKET, "[") - STATIC_VALUE_TOKEN(RPAREN, ")") - STATIC_VALUE_TOKEN(LPAREN, "(") - STATIC_VALUE_TOKEN(COLON, ":") - STATIC_VALUE_TOKEN(VAR_SIGN, "$") - STATIC_VALUE_TOKEN(DIR_SIGN, "@") - STATIC_VALUE_TOKEN(ELLIPSIS, "...") - STATIC_VALUE_TOKEN(EQUALS, "=") - STATIC_VALUE_TOKEN(BANG, "!") - STATIC_VALUE_TOKEN(PIPE, "|") - STATIC_VALUE_TOKEN(AMP, "&") - STATIC_VALUE_TOKEN(SCHEMA, "schema") - STATIC_VALUE_TOKEN(SCALAR, "scalar") - STATIC_VALUE_TOKEN(EXTEND, "extend") - STATIC_VALUE_TOKEN(IMPLEMENTS, "implements") - STATIC_VALUE_TOKEN(INTERFACE, "interface") - STATIC_VALUE_TOKEN(UNION, "union") - STATIC_VALUE_TOKEN(ENUM, "enum") - STATIC_VALUE_TOKEN(DIRECTIVE, "directive") - STATIC_VALUE_TOKEN(INPUT, "input") - // For these, the enum name doesn't match the symbol name: - case TYPE_LITERAL: - token_sym = ID2SYM(rb_intern("TYPE")); - token_content = GraphQL_type_str; - break; - case TRUE_LITERAL: - token_sym = ID2SYM(rb_intern("TRUE")); - token_content = GraphQL_true_str; - break; - case FALSE_LITERAL: - token_sym = ID2SYM(rb_intern("FALSE")); - token_content = GraphQL_false_str; - break; - case NULL_LITERAL: - token_sym = ID2SYM(rb_intern("NULL")); - token_content = GraphQL_null_str; - break; - case IDENTIFIER: - if (meta->reject_numbers_followed_by_names && meta->preceeded_by_number) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mCParser = rb_const_get_at(mGraphQL, rb_intern("CParser")); - VALUE prev_token = rb_ary_entry(meta->tokens, -1); - VALUE exception = rb_funcall( - mCParser, rb_intern("prepare_number_name_parse_error"), 5, - LONG2NUM(meta->line), - LONG2NUM(meta->col), - rb_str_new_cstr(meta->query_cstr), - rb_ary_entry(prev_token, 3), - rb_utf8_str_new(ts, te - ts) - ); - rb_exc_raise(exception); - } - token_sym = ID2SYM(rb_intern("IDENTIFIER")); - if (meta->dedup_identifiers) { - token_content = rb_enc_interned_str(ts, te - ts, rb_utf8_encoding()); - } else { - token_content = rb_utf8_str_new(ts, te - ts); - } - break; - // Can't use these while we're in backwards-compat mode: - // DYNAMIC_VALUE_TOKEN(INT) - // DYNAMIC_VALUE_TOKEN(FLOAT) - case INT: - token_sym = ID2SYM(rb_intern("INT")); - token_content = rb_utf8_str_new(ts, te - ts); - this_token_is_number = 1; - break; - case FLOAT: - token_sym = ID2SYM(rb_intern("FLOAT")); - token_content = rb_utf8_str_new(ts, te - ts); - this_token_is_number = 1; - break; - DYNAMIC_VALUE_TOKEN(COMMENT) - case UNKNOWN_CHAR: - if (ts[0] == '\0') { - return; - } else { - token_content = rb_utf8_str_new(ts, te - ts); - token_sym = ID2SYM(rb_intern("UNKNOWN_CHAR")); - break; - } - case QUOTED_STRING: - quotes_length = 1; - token_content = rb_utf8_str_new(ts + quotes_length, (te - ts - (2 * quotes_length))); - token_sym = ID2SYM(rb_intern("STRING")); - break; - case BLOCK_STRING: - token_sym = ID2SYM(rb_intern("STRING")); - quotes_length = 3; - token_content = rb_utf8_str_new(ts + quotes_length, (te - ts - (2 * quotes_length))); - line_incr = FIX2INT(rb_funcall(token_content, rb_intern("count"), 1, rb_utf8_str_new_cstr("\n"))); - break; - // These are used only by the parser, this is never reached - case STRING: - case BAD_UNICODE_ESCAPE: - break; - } - - if (token_sym != Qnil) { - if (tt == BLOCK_STRING || tt == QUOTED_STRING) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mGraphQLLanguage = rb_const_get_at(mGraphQL, rb_intern("Language")); - VALUE mGraphQLLanguageLexer = rb_const_get_at(mGraphQLLanguage, rb_intern("Lexer")); - VALUE valid_string_pattern = rb_const_get_at(mGraphQLLanguageLexer, rb_intern("VALID_STRING")); - if (tt == BLOCK_STRING) { - VALUE mGraphQLLanguageBlockString = rb_const_get_at(mGraphQLLanguage, rb_intern("BlockString")); - token_content = rb_funcall(mGraphQLLanguageBlockString, rb_intern("trim_whitespace"), 1, token_content); - tt = STRING; - } else { - tt = STRING; - if ( - RB_TEST(rb_funcall(token_content, rb_intern("valid_encoding?"), 0)) && - RB_TEST(rb_funcall(token_content, rb_intern("match?"), 1, valid_string_pattern)) - ) { - rb_funcall(mGraphQLLanguageLexer, rb_intern("replace_escaped_characters_in_place"), 1, token_content); - if (!RB_TEST(rb_funcall(token_content, rb_intern("valid_encoding?"), 0))) { - token_sym = ID2SYM(rb_intern("BAD_UNICODE_ESCAPE")); - tt = BAD_UNICODE_ESCAPE; - } - } else { - token_sym = ID2SYM(rb_intern("BAD_UNICODE_ESCAPE")); - tt = BAD_UNICODE_ESCAPE; - } - } - } - - VALUE token = rb_ary_new_from_args(5, - token_sym, - rb_int2inum(meta->line), - rb_int2inum(meta->col), - token_content, - INT2FIX(200 + (int)tt) - ); - - if (tt != COMMENT) { - rb_ary_push(meta->tokens, token); - } - meta->preceeded_by_number = this_token_is_number; - } - // Bump the column counter for the next token - meta->col += te - ts; - meta->line += line_incr; -} - -VALUE tokenize(VALUE query_rbstr, int fstring_identifiers, int reject_numbers_followed_by_names, int max_tokens) { - int cs = 0; - int act = 0; - char *p = StringValuePtr(query_rbstr); - long query_len = RSTRING_LEN(query_rbstr); - char *pe = p + query_len; - char *eof = pe; - char *ts = 0; - char *te = 0; - VALUE tokens = rb_ary_new(); - struct Meta meta_s = {1, 1, p, pe, tokens, fstring_identifiers, reject_numbers_followed_by_names, 0, max_tokens, 0}; - Meta *meta = &meta_s; - - -#line 987 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - { - cs = (int)graphql_c_lexer_start; - ts = 0; - te = 0; - act = 0; - } - -#line 407 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - - -#line 998 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - { - unsigned int _trans = 0; - const char * _keys; - const short * _inds; - int _ic; - _resume: {} - if ( p == pe && p != eof ) - goto _out; - switch ( _graphql_c_lexer_from_state_actions[cs] ) { - case 10: { - { -#line 1 "NONE" - {ts = p;}} - -#line 1013 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - } - - if ( p == eof ) { - if ( _graphql_c_lexer_eof_trans[cs] > 0 ) { - _trans = (unsigned int)_graphql_c_lexer_eof_trans[cs] - 1; - } - } - else { - _keys = ( _graphql_c_lexer_trans_keys + ((cs<<1))); - _inds = ( _graphql_c_lexer_indices + (_graphql_c_lexer_index_offsets[cs])); - - if ( ( (*( p))) <= 125 && ( (*( p))) >= 9 ) { - _ic = (int)_graphql_c_lexer_char_class[(int)( (*( p))) - 9]; - if ( _ic <= (int)(*( _keys+1)) && _ic >= (int)(*( _keys)) ) - _trans = (unsigned int)(*( _inds + (int)( _ic - (int)(*( _keys)) ) )); - else - _trans = (unsigned int)_graphql_c_lexer_index_defaults[cs]; - } - else { - _trans = (unsigned int)_graphql_c_lexer_index_defaults[cs]; - } - - } - cs = (int)_graphql_c_lexer_cond_targs[_trans]; - - if ( _graphql_c_lexer_cond_actions[_trans] != 0 ) { - - switch ( _graphql_c_lexer_cond_actions[_trans] ) { - case 6: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1051 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 26: { - { -#line 75 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 75 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(RCURLY, ts, te, meta); } - }} - -#line 1064 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 24: { - { -#line 76 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 76 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(LCURLY, ts, te, meta); } - }} - -#line 1077 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 17: { - { -#line 77 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 77 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(RPAREN, ts, te, meta); } - }} - -#line 1090 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 16: { - { -#line 78 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 78 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(LPAREN, ts, te, meta); } - }} - -#line 1103 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 23: { - { -#line 79 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 79 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(RBRACKET, ts, te, meta); } - }} - -#line 1116 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 22: { - { -#line 80 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 80 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(LBRACKET, ts, te, meta); } - }} - -#line 1129 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 18: { - { -#line 81 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 81 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(COLON, ts, te, meta); } - }} - -#line 1142 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 32: { - { -#line 82 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 82 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(BLOCK_STRING, ts, te, meta); } - }} - -#line 1155 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 2: { - { -#line 83 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 83 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(QUOTED_STRING, ts, te, meta); } - }} - -#line 1168 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 14: { - { -#line 84 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 84 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(VAR_SIGN, ts, te, meta); } - }} - -#line 1181 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 20: { - { -#line 85 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 85 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(DIR_SIGN, ts, te, meta); } - }} - -#line 1194 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 8: { - { -#line 86 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 86 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(ELLIPSIS, ts, te, meta); } - }} - -#line 1207 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 19: { - { -#line 87 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 87 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(EQUALS, ts, te, meta); } - }} - -#line 1220 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 13: { - { -#line 88 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 88 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(BANG, ts, te, meta); } - }} - -#line 1233 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 25: { - { -#line 89 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 89 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(PIPE, ts, te, meta); } - }} - -#line 1246 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 15: { - { -#line 90 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 90 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(AMP, ts, te, meta); } - }} - -#line 1259 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 12: { - { -#line 93 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 93 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - - meta->line += 1; - meta->col = 1; - meta->preceeded_by_number = 0; - } - }} - -#line 1276 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 11: { - { -#line 104 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p+1;{ -#line 104 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(UNKNOWN_CHAR, ts, te, meta); } - }} - -#line 1289 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 34: { - { -#line 54 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 54 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(INT, ts, te, meta); } - }} - -#line 1302 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 35: { - { -#line 55 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 55 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(FLOAT, ts, te, meta); } - }} - -#line 1315 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 31: { - { -#line 82 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 82 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(BLOCK_STRING, ts, te, meta); } - }} - -#line 1328 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 30: { - { -#line 83 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 83 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(QUOTED_STRING, ts, te, meta); } - }} - -#line 1341 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 36: { - { -#line 91 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 91 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(IDENTIFIER, ts, te, meta); } - }} - -#line 1354 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 33: { - { -#line 92 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 92 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(COMMENT, ts, te, meta); } - }} - -#line 1367 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 27: { - { -#line 99 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 99 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - - meta->col += te - ts; - meta->preceeded_by_number = 0; - } - }} - -#line 1383 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 28: { - { -#line 104 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {te = p;p = p - 1;{ -#line 104 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(UNKNOWN_CHAR, ts, te, meta); } - }} - -#line 1396 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 5: { - { -#line 54 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {p = ((te))-1; - { -#line 54 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(INT, ts, te, meta); } - }} - -#line 1410 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 7: { - { -#line 55 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {p = ((te))-1; - { -#line 55 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(FLOAT, ts, te, meta); } - }} - -#line 1424 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 1: { - { -#line 104 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {p = ((te))-1; - { -#line 104 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(UNKNOWN_CHAR, ts, te, meta); } - }} - -#line 1438 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 3: { - { -#line 1 "NONE" - {switch( act ) { - case 3: { - p = ((te))-1; - { -#line 56 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(ON, ts, te, meta); } - break; - } - case 4: { - p = ((te))-1; - { -#line 57 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(FRAGMENT, ts, te, meta); } - break; - } - case 5: { - p = ((te))-1; - { -#line 58 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(TRUE_LITERAL, ts, te, meta); } - break; - } - case 6: { - p = ((te))-1; - { -#line 59 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(FALSE_LITERAL, ts, te, meta); } - break; - } - case 7: { - p = ((te))-1; - { -#line 60 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(NULL_LITERAL, ts, te, meta); } - break; - } - case 8: { - p = ((te))-1; - { -#line 61 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(QUERY, ts, te, meta); } - break; - } - case 9: { - p = ((te))-1; - { -#line 62 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(MUTATION, ts, te, meta); } - break; - } - case 10: { - p = ((te))-1; - { -#line 63 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(SUBSCRIPTION, ts, te, meta); } - break; - } - case 11: { - p = ((te))-1; - { -#line 64 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(SCHEMA, ts, te, meta); } - break; - } - case 12: { - p = ((te))-1; - { -#line 65 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(SCALAR, ts, te, meta); } - break; - } - case 13: { - p = ((te))-1; - { -#line 66 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(TYPE_LITERAL, ts, te, meta); } - break; - } - case 14: { - p = ((te))-1; - { -#line 67 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(EXTEND, ts, te, meta); } - break; - } - case 15: { - p = ((te))-1; - { -#line 68 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(IMPLEMENTS, ts, te, meta); } - break; - } - case 16: { - p = ((te))-1; - { -#line 69 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(INTERFACE, ts, te, meta); } - break; - } - case 17: { - p = ((te))-1; - { -#line 70 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(UNION, ts, te, meta); } - break; - } - case 18: { - p = ((te))-1; - { -#line 71 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(ENUM, ts, te, meta); } - break; - } - case 19: { - p = ((te))-1; - { -#line 72 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(INPUT, ts, te, meta); } - break; - } - case 20: { - p = ((te))-1; - { -#line 73 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(DIRECTIVE, ts, te, meta); } - break; - } - case 21: { - p = ((te))-1; - { -#line 74 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(REPEATABLE, ts, te, meta); } - break; - } - case 29: { - p = ((te))-1; - { -#line 82 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(BLOCK_STRING, ts, te, meta); } - break; - } - case 30: { - p = ((te))-1; - { -#line 83 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(QUOTED_STRING, ts, te, meta); } - break; - } - case 38: { - p = ((te))-1; - { -#line 91 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - emit(IDENTIFIER, ts, te, meta); } - break; - } - }} - } - -#line 1604 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 47: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1614 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 56 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 3;}} - -#line 1620 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 41: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1630 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 57 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 4;}} - -#line 1636 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 53: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1646 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 58 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 5;}} - -#line 1652 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 40: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1662 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 59 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 6;}} - -#line 1668 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 46: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1678 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 60 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 7;}} - -#line 1684 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 48: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1694 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 61 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 8;}} - -#line 1700 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 45: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1710 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 62 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 9;}} - -#line 1716 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 52: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1726 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 63 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 10;}} - -#line 1732 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 51: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1742 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 64 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 11;}} - -#line 1748 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 50: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1758 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 65 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 12;}} - -#line 1764 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 54: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1774 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 66 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 13;}} - -#line 1780 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 39: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1790 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 67 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 14;}} - -#line 1796 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 42: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1806 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 68 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 15;}} - -#line 1812 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 44: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1822 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 69 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 16;}} - -#line 1828 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 55: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1838 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 70 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 17;}} - -#line 1844 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 38: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1854 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 71 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 18;}} - -#line 1860 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 43: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1870 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 72 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 19;}} - -#line 1876 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 37: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1886 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 73 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 20;}} - -#line 1892 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 49: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1902 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 74 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 21;}} - -#line 1908 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 4: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1918 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 82 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 29;}} - -#line 1924 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 29: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1934 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 83 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 30;}} - -#line 1940 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - case 21: { - { -#line 1 "NONE" - {te = p+1;}} - -#line 1950 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - { -#line 91 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - {act = 38;}} - -#line 1956 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - } - - } - - if ( p == eof ) { - if ( cs >= 21 ) - goto _out; - } - else { - switch ( _graphql_c_lexer_to_state_actions[cs] ) { - case 9: { - { -#line 1 "NONE" - {ts = 0;}} - -#line 1976 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.c" - - - break; - } - } - - p += 1; - goto _resume; - } - _out: {} - } - -#line 408 "graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl" - - - return tokens; -} - - -#define SETUP_STATIC_TOKEN_VARIABLE(token_name, token_content) \ -GraphQLTokenString##token_name = rb_utf8_str_new_cstr(token_content); \ -rb_funcall(GraphQLTokenString##token_name, rb_intern("-@"), 0); \ -rb_global_variable(&GraphQLTokenString##token_name); \ - -#define SETUP_STATIC_STRING(var_name, str_content) \ -var_name = rb_utf8_str_new_cstr(str_content); \ -rb_global_variable(&var_name); \ -rb_str_freeze(var_name); \ - -void setup_static_token_variables() { - SETUP_STATIC_TOKEN_VARIABLE(ON, "on") - SETUP_STATIC_TOKEN_VARIABLE(FRAGMENT, "fragment") - SETUP_STATIC_TOKEN_VARIABLE(QUERY, "query") - SETUP_STATIC_TOKEN_VARIABLE(MUTATION, "mutation") - SETUP_STATIC_TOKEN_VARIABLE(SUBSCRIPTION, "subscription") - SETUP_STATIC_TOKEN_VARIABLE(REPEATABLE, "repeatable") - SETUP_STATIC_TOKEN_VARIABLE(RCURLY, "}") -SETUP_STATIC_TOKEN_VARIABLE(LCURLY, "{") - SETUP_STATIC_TOKEN_VARIABLE(RBRACKET, "]") - SETUP_STATIC_TOKEN_VARIABLE(LBRACKET, "[") - SETUP_STATIC_TOKEN_VARIABLE(RPAREN, ")") - SETUP_STATIC_TOKEN_VARIABLE(LPAREN, "(") - SETUP_STATIC_TOKEN_VARIABLE(COLON, ":") - SETUP_STATIC_TOKEN_VARIABLE(VAR_SIGN, "$") - SETUP_STATIC_TOKEN_VARIABLE(DIR_SIGN, "@") - SETUP_STATIC_TOKEN_VARIABLE(ELLIPSIS, "...") - SETUP_STATIC_TOKEN_VARIABLE(EQUALS, "=") - SETUP_STATIC_TOKEN_VARIABLE(BANG, "!") - SETUP_STATIC_TOKEN_VARIABLE(PIPE, "|") - SETUP_STATIC_TOKEN_VARIABLE(AMP, "&") - SETUP_STATIC_TOKEN_VARIABLE(SCHEMA, "schema") - SETUP_STATIC_TOKEN_VARIABLE(SCALAR, "scalar") - SETUP_STATIC_TOKEN_VARIABLE(EXTEND, "extend") - SETUP_STATIC_TOKEN_VARIABLE(IMPLEMENTS, "implements") - SETUP_STATIC_TOKEN_VARIABLE(INTERFACE, "interface") - SETUP_STATIC_TOKEN_VARIABLE(UNION, "union") - SETUP_STATIC_TOKEN_VARIABLE(ENUM, "enum") - SETUP_STATIC_TOKEN_VARIABLE(DIRECTIVE, "directive") - SETUP_STATIC_TOKEN_VARIABLE(INPUT, "input") - - SETUP_STATIC_STRING(GraphQL_type_str, "type") - SETUP_STATIC_STRING(GraphQL_true_str, "true") - SETUP_STATIC_STRING(GraphQL_false_str, "false") - SETUP_STATIC_STRING(GraphQL_null_str, "null") -} diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.h b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.h deleted file mode 100644 index 199628266b4..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef Graphql_lexer_h -#define Graphql_lexer_h -#include -VALUE tokenize(VALUE query_rbstr, int fstring_identifiers, int reject_numbers_followed_by_names, int max_tokens); -void setup_static_token_variables(); -#endif diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl deleted file mode 100644 index 51d886d58a7..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/lexer.rl +++ /dev/null @@ -1,459 +0,0 @@ -%%{ - machine graphql_c_lexer; - - IDENTIFIER = [_A-Za-z][_0-9A-Za-z]*; - NEWLINE = [\c\r\n]; - BLANK = [, \t]+; - COMMENT = '#' [^\n\r]*; - INT = '-'? ('0'|[1-9][0-9]*); - FLOAT = INT ('.'[0-9]+) (('e' | 'E')?('+' | '-')?[0-9]+)?; - ON = 'on'; - FRAGMENT = 'fragment'; - TRUE_LITERAL = 'true'; - FALSE_LITERAL = 'false'; - NULL_LITERAL = 'null'; - QUERY = 'query'; - MUTATION = 'mutation'; - SUBSCRIPTION = 'subscription'; - SCHEMA = 'schema'; - SCALAR = 'scalar'; - TYPE_LITERAL = 'type'; - EXTEND = 'extend'; - IMPLEMENTS = 'implements'; - INTERFACE = 'interface'; - UNION = 'union'; - ENUM = 'enum'; - INPUT = 'input'; - DIRECTIVE = 'directive'; - REPEATABLE = 'repeatable'; - LCURLY = '{'; - RCURLY = '}'; - LPAREN = '('; - RPAREN = ')'; - LBRACKET = '['; - RBRACKET = ']'; - COLON = ':'; - # Could limit to hex here, but “bad unicode escape” on 0XXF is probably a - # more helpful error than “unknown char” - UNICODE_ESCAPE = "\\u" ([0-9A-Za-z]{4} | LCURLY [0-9A-Za-z]{4,} RCURLY); - VAR_SIGN = '$'; - DIR_SIGN = '@'; - ELLIPSIS = '...'; - EQUALS = '='; - BANG = '!'; - PIPE = '|'; - AMP = '&'; - - QUOTED_STRING = ('"' ((('\\"' | ^'"') - "\\" - "\n" - "\r") | UNICODE_ESCAPE | '\\' [\\/bfnrt])* '"'); - # catch-all for anything else. must be at the bottom for precedence. - UNKNOWN_CHAR = /./; - - BLOCK_STRING = ('"""' ('\\"""' | ^'"' | '"'{1,2} ^'"')* '"'{0,2} '"""'); - - main := |* - INT => { emit(INT, ts, te, meta); }; - FLOAT => { emit(FLOAT, ts, te, meta); }; - ON => { emit(ON, ts, te, meta); }; - FRAGMENT => { emit(FRAGMENT, ts, te, meta); }; - TRUE_LITERAL => { emit(TRUE_LITERAL, ts, te, meta); }; - FALSE_LITERAL => { emit(FALSE_LITERAL, ts, te, meta); }; - NULL_LITERAL => { emit(NULL_LITERAL, ts, te, meta); }; - QUERY => { emit(QUERY, ts, te, meta); }; - MUTATION => { emit(MUTATION, ts, te, meta); }; - SUBSCRIPTION => { emit(SUBSCRIPTION, ts, te, meta); }; - SCHEMA => { emit(SCHEMA, ts, te, meta); }; - SCALAR => { emit(SCALAR, ts, te, meta); }; - TYPE_LITERAL => { emit(TYPE_LITERAL, ts, te, meta); }; - EXTEND => { emit(EXTEND, ts, te, meta); }; - IMPLEMENTS => { emit(IMPLEMENTS, ts, te, meta); }; - INTERFACE => { emit(INTERFACE, ts, te, meta); }; - UNION => { emit(UNION, ts, te, meta); }; - ENUM => { emit(ENUM, ts, te, meta); }; - INPUT => { emit(INPUT, ts, te, meta); }; - DIRECTIVE => { emit(DIRECTIVE, ts, te, meta); }; - REPEATABLE => { emit(REPEATABLE, ts, te, meta); }; - RCURLY => { emit(RCURLY, ts, te, meta); }; - LCURLY => { emit(LCURLY, ts, te, meta); }; - RPAREN => { emit(RPAREN, ts, te, meta); }; - LPAREN => { emit(LPAREN, ts, te, meta); }; - RBRACKET => { emit(RBRACKET, ts, te, meta); }; - LBRACKET => { emit(LBRACKET, ts, te, meta); }; - COLON => { emit(COLON, ts, te, meta); }; - BLOCK_STRING => { emit(BLOCK_STRING, ts, te, meta); }; - QUOTED_STRING => { emit(QUOTED_STRING, ts, te, meta); }; - VAR_SIGN => { emit(VAR_SIGN, ts, te, meta); }; - DIR_SIGN => { emit(DIR_SIGN, ts, te, meta); }; - ELLIPSIS => { emit(ELLIPSIS, ts, te, meta); }; - EQUALS => { emit(EQUALS, ts, te, meta); }; - BANG => { emit(BANG, ts, te, meta); }; - PIPE => { emit(PIPE, ts, te, meta); }; - AMP => { emit(AMP, ts, te, meta); }; - IDENTIFIER => { emit(IDENTIFIER, ts, te, meta); }; - COMMENT => { emit(COMMENT, ts, te, meta); }; - NEWLINE => { - meta->line += 1; - meta->col = 1; - meta->preceeded_by_number = 0; - }; - - BLANK => { - meta->col += te - ts; - meta->preceeded_by_number = 0; - }; - - UNKNOWN_CHAR => { emit(UNKNOWN_CHAR, ts, te, meta); }; - *|; -}%% - -%% write data; - -#include -#include - -#define INIT_STATIC_TOKEN_VARIABLE(token_name) \ - static VALUE GraphQLTokenString##token_name; - -INIT_STATIC_TOKEN_VARIABLE(ON) -INIT_STATIC_TOKEN_VARIABLE(FRAGMENT) -INIT_STATIC_TOKEN_VARIABLE(QUERY) -INIT_STATIC_TOKEN_VARIABLE(MUTATION) -INIT_STATIC_TOKEN_VARIABLE(SUBSCRIPTION) -INIT_STATIC_TOKEN_VARIABLE(REPEATABLE) -INIT_STATIC_TOKEN_VARIABLE(RCURLY) -INIT_STATIC_TOKEN_VARIABLE(LCURLY) -INIT_STATIC_TOKEN_VARIABLE(RBRACKET) -INIT_STATIC_TOKEN_VARIABLE(LBRACKET) -INIT_STATIC_TOKEN_VARIABLE(RPAREN) -INIT_STATIC_TOKEN_VARIABLE(LPAREN) -INIT_STATIC_TOKEN_VARIABLE(COLON) -INIT_STATIC_TOKEN_VARIABLE(VAR_SIGN) -INIT_STATIC_TOKEN_VARIABLE(DIR_SIGN) -INIT_STATIC_TOKEN_VARIABLE(ELLIPSIS) -INIT_STATIC_TOKEN_VARIABLE(EQUALS) -INIT_STATIC_TOKEN_VARIABLE(BANG) -INIT_STATIC_TOKEN_VARIABLE(PIPE) -INIT_STATIC_TOKEN_VARIABLE(AMP) -INIT_STATIC_TOKEN_VARIABLE(SCHEMA) -INIT_STATIC_TOKEN_VARIABLE(SCALAR) -INIT_STATIC_TOKEN_VARIABLE(EXTEND) -INIT_STATIC_TOKEN_VARIABLE(IMPLEMENTS) -INIT_STATIC_TOKEN_VARIABLE(INTERFACE) -INIT_STATIC_TOKEN_VARIABLE(UNION) -INIT_STATIC_TOKEN_VARIABLE(ENUM) -INIT_STATIC_TOKEN_VARIABLE(DIRECTIVE) -INIT_STATIC_TOKEN_VARIABLE(INPUT) - -static VALUE GraphQL_type_str; -static VALUE GraphQL_true_str; -static VALUE GraphQL_false_str; -static VALUE GraphQL_null_str; -typedef enum TokenType { - AMP, - BANG, - COLON, - DIRECTIVE, - DIR_SIGN, - ENUM, - ELLIPSIS, - EQUALS, - EXTEND, - FALSE_LITERAL, - FLOAT, - FRAGMENT, - IDENTIFIER, - INPUT, - IMPLEMENTS, - INT, - INTERFACE, - LBRACKET, - LCURLY, - LPAREN, - MUTATION, - NULL_LITERAL, - ON, - PIPE, - QUERY, - RBRACKET, - RCURLY, - REPEATABLE, - RPAREN, - SCALAR, - SCHEMA, - STRING, - SUBSCRIPTION, - TRUE_LITERAL, - TYPE_LITERAL, - UNION, - VAR_SIGN, - BLOCK_STRING, - QUOTED_STRING, - UNKNOWN_CHAR, - COMMENT, - BAD_UNICODE_ESCAPE -} TokenType; - -typedef struct Meta { - int line; - int col; - char *query_cstr; - char *pe; - VALUE tokens; - int dedup_identifiers; - int reject_numbers_followed_by_names; - int preceeded_by_number; - int max_tokens; - int tokens_count; -} Meta; - -#define STATIC_VALUE_TOKEN(token_type, content_str) \ - case token_type: \ - token_sym = ID2SYM(rb_intern(#token_type)); \ - token_content = GraphQLTokenString##token_type; \ - break; - -#define DYNAMIC_VALUE_TOKEN(token_type) \ - case token_type: \ - token_sym = ID2SYM(rb_intern(#token_type)); \ - token_content = rb_utf8_str_new(ts, te - ts); \ - break; - -void emit(TokenType tt, char *ts, char *te, Meta *meta) { - meta->tokens_count++; - // -1 indicates that there is no limit: - if (meta->max_tokens > 0 && meta->tokens_count > meta->max_tokens) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE cParseError = rb_const_get_at(mGraphQL, rb_intern("ParseError")); - VALUE exception = rb_funcall( - cParseError, rb_intern("new"), 4, - rb_str_new_cstr("This query is too large to execute."), - LONG2NUM(meta->line), - LONG2NUM(meta->col), - rb_str_new_cstr(meta->query_cstr) - ); - rb_exc_raise(exception); - } - int quotes_length = 0; // set by string tokens below - int line_incr = 0; - VALUE token_sym = Qnil; - VALUE token_content = Qnil; - int this_token_is_number = 0; - switch(tt) { - STATIC_VALUE_TOKEN(ON, "on") - STATIC_VALUE_TOKEN(FRAGMENT, "fragment") - STATIC_VALUE_TOKEN(QUERY, "query") - STATIC_VALUE_TOKEN(MUTATION, "mutation") - STATIC_VALUE_TOKEN(SUBSCRIPTION, "subscription") - STATIC_VALUE_TOKEN(REPEATABLE, "repeatable") - STATIC_VALUE_TOKEN(RCURLY, "}") - STATIC_VALUE_TOKEN(LCURLY, "{") - STATIC_VALUE_TOKEN(RBRACKET, "]") - STATIC_VALUE_TOKEN(LBRACKET, "[") - STATIC_VALUE_TOKEN(RPAREN, ")") - STATIC_VALUE_TOKEN(LPAREN, "(") - STATIC_VALUE_TOKEN(COLON, ":") - STATIC_VALUE_TOKEN(VAR_SIGN, "$") - STATIC_VALUE_TOKEN(DIR_SIGN, "@") - STATIC_VALUE_TOKEN(ELLIPSIS, "...") - STATIC_VALUE_TOKEN(EQUALS, "=") - STATIC_VALUE_TOKEN(BANG, "!") - STATIC_VALUE_TOKEN(PIPE, "|") - STATIC_VALUE_TOKEN(AMP, "&") - STATIC_VALUE_TOKEN(SCHEMA, "schema") - STATIC_VALUE_TOKEN(SCALAR, "scalar") - STATIC_VALUE_TOKEN(EXTEND, "extend") - STATIC_VALUE_TOKEN(IMPLEMENTS, "implements") - STATIC_VALUE_TOKEN(INTERFACE, "interface") - STATIC_VALUE_TOKEN(UNION, "union") - STATIC_VALUE_TOKEN(ENUM, "enum") - STATIC_VALUE_TOKEN(DIRECTIVE, "directive") - STATIC_VALUE_TOKEN(INPUT, "input") - // For these, the enum name doesn't match the symbol name: - case TYPE_LITERAL: - token_sym = ID2SYM(rb_intern("TYPE")); - token_content = GraphQL_type_str; - break; - case TRUE_LITERAL: - token_sym = ID2SYM(rb_intern("TRUE")); - token_content = GraphQL_true_str; - break; - case FALSE_LITERAL: - token_sym = ID2SYM(rb_intern("FALSE")); - token_content = GraphQL_false_str; - break; - case NULL_LITERAL: - token_sym = ID2SYM(rb_intern("NULL")); - token_content = GraphQL_null_str; - break; - case IDENTIFIER: - if (meta->reject_numbers_followed_by_names && meta->preceeded_by_number) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mCParser = rb_const_get_at(mGraphQL, rb_intern("CParser")); - VALUE prev_token = rb_ary_entry(meta->tokens, -1); - VALUE exception = rb_funcall( - mCParser, rb_intern("prepare_number_name_parse_error"), 5, - LONG2NUM(meta->line), - LONG2NUM(meta->col), - rb_str_new_cstr(meta->query_cstr), - rb_ary_entry(prev_token, 3), - rb_utf8_str_new(ts, te - ts) - ); - rb_exc_raise(exception); - } - token_sym = ID2SYM(rb_intern("IDENTIFIER")); - if (meta->dedup_identifiers) { - token_content = rb_enc_interned_str(ts, te - ts, rb_utf8_encoding()); - } else { - token_content = rb_utf8_str_new(ts, te - ts); - } - break; - // Can't use these while we're in backwards-compat mode: - // DYNAMIC_VALUE_TOKEN(INT) - // DYNAMIC_VALUE_TOKEN(FLOAT) - case INT: - token_sym = ID2SYM(rb_intern("INT")); - token_content = rb_utf8_str_new(ts, te - ts); - this_token_is_number = 1; - break; - case FLOAT: - token_sym = ID2SYM(rb_intern("FLOAT")); - token_content = rb_utf8_str_new(ts, te - ts); - this_token_is_number = 1; - break; - DYNAMIC_VALUE_TOKEN(COMMENT) - case UNKNOWN_CHAR: - if (ts[0] == '\0') { - return; - } else { - token_content = rb_utf8_str_new(ts, te - ts); - token_sym = ID2SYM(rb_intern("UNKNOWN_CHAR")); - break; - } - case QUOTED_STRING: - quotes_length = 1; - token_content = rb_utf8_str_new(ts + quotes_length, (te - ts - (2 * quotes_length))); - token_sym = ID2SYM(rb_intern("STRING")); - break; - case BLOCK_STRING: - token_sym = ID2SYM(rb_intern("STRING")); - quotes_length = 3; - token_content = rb_utf8_str_new(ts + quotes_length, (te - ts - (2 * quotes_length))); - line_incr = FIX2INT(rb_funcall(token_content, rb_intern("count"), 1, rb_utf8_str_new_cstr("\n"))); - break; - // These are used only by the parser, this is never reached - case STRING: - case BAD_UNICODE_ESCAPE: - break; - } - - if (token_sym != Qnil) { - if (tt == BLOCK_STRING || tt == QUOTED_STRING) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mGraphQLLanguage = rb_const_get_at(mGraphQL, rb_intern("Language")); - VALUE mGraphQLLanguageLexer = rb_const_get_at(mGraphQLLanguage, rb_intern("Lexer")); - VALUE valid_string_pattern = rb_const_get_at(mGraphQLLanguageLexer, rb_intern("VALID_STRING")); - if (tt == BLOCK_STRING) { - VALUE mGraphQLLanguageBlockString = rb_const_get_at(mGraphQLLanguage, rb_intern("BlockString")); - token_content = rb_funcall(mGraphQLLanguageBlockString, rb_intern("trim_whitespace"), 1, token_content); - tt = STRING; - } else { - tt = STRING; - if ( - RB_TEST(rb_funcall(token_content, rb_intern("valid_encoding?"), 0)) && - RB_TEST(rb_funcall(token_content, rb_intern("match?"), 1, valid_string_pattern)) - ) { - rb_funcall(mGraphQLLanguageLexer, rb_intern("replace_escaped_characters_in_place"), 1, token_content); - if (!RB_TEST(rb_funcall(token_content, rb_intern("valid_encoding?"), 0))) { - token_sym = ID2SYM(rb_intern("BAD_UNICODE_ESCAPE")); - tt = BAD_UNICODE_ESCAPE; - } - } else { - token_sym = ID2SYM(rb_intern("BAD_UNICODE_ESCAPE")); - tt = BAD_UNICODE_ESCAPE; - } - } - } - - VALUE token = rb_ary_new_from_args(5, - token_sym, - rb_int2inum(meta->line), - rb_int2inum(meta->col), - token_content, - INT2FIX(200 + (int)tt) - ); - - if (tt != COMMENT) { - rb_ary_push(meta->tokens, token); - } - meta->preceeded_by_number = this_token_is_number; - } - // Bump the column counter for the next token - meta->col += te - ts; - meta->line += line_incr; -} - -VALUE tokenize(VALUE query_rbstr, int fstring_identifiers, int reject_numbers_followed_by_names, int max_tokens) { - int cs = 0; - int act = 0; - char *p = StringValuePtr(query_rbstr); - long query_len = RSTRING_LEN(query_rbstr); - char *pe = p + query_len; - char *eof = pe; - char *ts = 0; - char *te = 0; - VALUE tokens = rb_ary_new(); - struct Meta meta_s = {1, 1, p, pe, tokens, fstring_identifiers, reject_numbers_followed_by_names, 0, max_tokens, 0}; - Meta *meta = &meta_s; - - %% write init; - %% write exec; - - return tokens; -} - - -#define SETUP_STATIC_TOKEN_VARIABLE(token_name, token_content) \ - GraphQLTokenString##token_name = rb_utf8_str_new_cstr(token_content); \ - rb_funcall(GraphQLTokenString##token_name, rb_intern("-@"), 0); \ - rb_global_variable(&GraphQLTokenString##token_name); \ - -#define SETUP_STATIC_STRING(var_name, str_content) \ - var_name = rb_utf8_str_new_cstr(str_content); \ - rb_global_variable(&var_name); \ - rb_str_freeze(var_name); \ - -void setup_static_token_variables() { - SETUP_STATIC_TOKEN_VARIABLE(ON, "on") - SETUP_STATIC_TOKEN_VARIABLE(FRAGMENT, "fragment") - SETUP_STATIC_TOKEN_VARIABLE(QUERY, "query") - SETUP_STATIC_TOKEN_VARIABLE(MUTATION, "mutation") - SETUP_STATIC_TOKEN_VARIABLE(SUBSCRIPTION, "subscription") - SETUP_STATIC_TOKEN_VARIABLE(REPEATABLE, "repeatable") - SETUP_STATIC_TOKEN_VARIABLE(RCURLY, "}") - SETUP_STATIC_TOKEN_VARIABLE(LCURLY, "{") - SETUP_STATIC_TOKEN_VARIABLE(RBRACKET, "]") - SETUP_STATIC_TOKEN_VARIABLE(LBRACKET, "[") - SETUP_STATIC_TOKEN_VARIABLE(RPAREN, ")") - SETUP_STATIC_TOKEN_VARIABLE(LPAREN, "(") - SETUP_STATIC_TOKEN_VARIABLE(COLON, ":") - SETUP_STATIC_TOKEN_VARIABLE(VAR_SIGN, "$") - SETUP_STATIC_TOKEN_VARIABLE(DIR_SIGN, "@") - SETUP_STATIC_TOKEN_VARIABLE(ELLIPSIS, "...") - SETUP_STATIC_TOKEN_VARIABLE(EQUALS, "=") - SETUP_STATIC_TOKEN_VARIABLE(BANG, "!") - SETUP_STATIC_TOKEN_VARIABLE(PIPE, "|") - SETUP_STATIC_TOKEN_VARIABLE(AMP, "&") - SETUP_STATIC_TOKEN_VARIABLE(SCHEMA, "schema") - SETUP_STATIC_TOKEN_VARIABLE(SCALAR, "scalar") - SETUP_STATIC_TOKEN_VARIABLE(EXTEND, "extend") - SETUP_STATIC_TOKEN_VARIABLE(IMPLEMENTS, "implements") - SETUP_STATIC_TOKEN_VARIABLE(INTERFACE, "interface") - SETUP_STATIC_TOKEN_VARIABLE(UNION, "union") - SETUP_STATIC_TOKEN_VARIABLE(ENUM, "enum") - SETUP_STATIC_TOKEN_VARIABLE(DIRECTIVE, "directive") - SETUP_STATIC_TOKEN_VARIABLE(INPUT, "input") - - SETUP_STATIC_STRING(GraphQL_type_str, "type") - SETUP_STATIC_STRING(GraphQL_true_str, "true") - SETUP_STATIC_STRING(GraphQL_false_str, "false") - SETUP_STATIC_STRING(GraphQL_null_str, "null") -} diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.c b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.c deleted file mode 100644 index b2b2ea8e013..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.c +++ /dev/null @@ -1,3341 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output, and Bison version. */ -#define YYBISON 30802 - -/* Bison version string. */ -#define YYBISON_VERSION "3.8.2" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 2 - -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - - - - -/* First part of user prologue. */ -#line 5 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - -// C Declarations -#include -#define YYSTYPE VALUE -int yylex(YYSTYPE *, VALUE, VALUE); -void yyerror(VALUE, VALUE, const char*); - -static VALUE GraphQL_Language_Nodes_NONE; -static VALUE r_string_query; - -#define MAKE_AST_NODE(node_class_name, nargs, ...) rb_funcall(GraphQL_Language_Nodes_##node_class_name, rb_intern("from_a"), nargs + 1, filename,__VA_ARGS__) - -#define SETUP_NODE_CLASS_VARIABLE(node_class_name) static VALUE GraphQL_Language_Nodes_##node_class_name; - -SETUP_NODE_CLASS_VARIABLE(Argument) -SETUP_NODE_CLASS_VARIABLE(Directive) -SETUP_NODE_CLASS_VARIABLE(Document) -SETUP_NODE_CLASS_VARIABLE(Enum) -SETUP_NODE_CLASS_VARIABLE(Field) -SETUP_NODE_CLASS_VARIABLE(FragmentDefinition) -SETUP_NODE_CLASS_VARIABLE(FragmentSpread) -SETUP_NODE_CLASS_VARIABLE(InlineFragment) -SETUP_NODE_CLASS_VARIABLE(InputObject) -SETUP_NODE_CLASS_VARIABLE(ListType) -SETUP_NODE_CLASS_VARIABLE(NonNullType) -SETUP_NODE_CLASS_VARIABLE(NullValue) -SETUP_NODE_CLASS_VARIABLE(OperationDefinition) -SETUP_NODE_CLASS_VARIABLE(TypeName) -SETUP_NODE_CLASS_VARIABLE(VariableDefinition) -SETUP_NODE_CLASS_VARIABLE(VariableIdentifier) - -SETUP_NODE_CLASS_VARIABLE(ScalarTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(ObjectTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(InterfaceTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(UnionTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(EnumTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(InputObjectTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(EnumValueDefinition) -SETUP_NODE_CLASS_VARIABLE(DirectiveDefinition) -SETUP_NODE_CLASS_VARIABLE(DirectiveLocation) -SETUP_NODE_CLASS_VARIABLE(FieldDefinition) -SETUP_NODE_CLASS_VARIABLE(InputValueDefinition) -SETUP_NODE_CLASS_VARIABLE(SchemaDefinition) - -SETUP_NODE_CLASS_VARIABLE(ScalarTypeExtension) -SETUP_NODE_CLASS_VARIABLE(ObjectTypeExtension) -SETUP_NODE_CLASS_VARIABLE(InterfaceTypeExtension) -SETUP_NODE_CLASS_VARIABLE(UnionTypeExtension) -SETUP_NODE_CLASS_VARIABLE(EnumTypeExtension) -SETUP_NODE_CLASS_VARIABLE(InputObjectTypeExtension) -SETUP_NODE_CLASS_VARIABLE(SchemaExtension) - -#line 124 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - -# ifndef YY_CAST -# ifdef __cplusplus -# define YY_CAST(Type, Val) static_cast (Val) -# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) -# else -# define YY_CAST(Type, Val) ((Type) (Val)) -# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) -# endif -# endif -# ifndef YY_NULLPTR -# if defined __cplusplus -# if 201103L <= __cplusplus -# define YY_NULLPTR nullptr -# else -# define YY_NULLPTR 0 -# endif -# else -# define YY_NULLPTR ((void*)0) -# endif -# endif - - -/* Debug traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -#if YYDEBUG -extern int yydebug; -#endif - -/* Token kinds. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - enum yytokentype - { - YYEMPTY = -2, - YYEOF = 0, /* "end of file" */ - YYerror = 256, /* error */ - YYUNDEF = 257, /* "invalid token" */ - AMP = 200, /* AMP */ - BANG = 201, /* BANG */ - COLON = 202, /* COLON */ - DIRECTIVE = 203, /* DIRECTIVE */ - DIR_SIGN = 204, /* DIR_SIGN */ - ENUM = 205, /* ENUM */ - ELLIPSIS = 206, /* ELLIPSIS */ - EQUALS = 207, /* EQUALS */ - EXTEND = 208, /* EXTEND */ - FALSE_LITERAL = 209, /* FALSE_LITERAL */ - FLOAT = 210, /* FLOAT */ - FRAGMENT = 211, /* FRAGMENT */ - IDENTIFIER = 212, /* IDENTIFIER */ - INPUT = 213, /* INPUT */ - IMPLEMENTS = 214, /* IMPLEMENTS */ - INT = 215, /* INT */ - INTERFACE = 216, /* INTERFACE */ - LBRACKET = 217, /* LBRACKET */ - LCURLY = 218, /* LCURLY */ - LPAREN = 219, /* LPAREN */ - MUTATION = 220, /* MUTATION */ - NULL_LITERAL = 221, /* NULL_LITERAL */ - ON = 222, /* ON */ - PIPE = 223, /* PIPE */ - QUERY = 224, /* QUERY */ - RBRACKET = 225, /* RBRACKET */ - RCURLY = 226, /* RCURLY */ - REPEATABLE = 227, /* REPEATABLE */ - RPAREN = 228, /* RPAREN */ - SCALAR = 229, /* SCALAR */ - SCHEMA = 230, /* SCHEMA */ - STRING = 231, /* STRING */ - SUBSCRIPTION = 232, /* SUBSCRIPTION */ - TRUE_LITERAL = 233, /* TRUE_LITERAL */ - TYPE_LITERAL = 234, /* TYPE_LITERAL */ - UNION = 235, /* UNION */ - VAR_SIGN = 236 /* VAR_SIGN */ - }; - typedef enum yytokentype yytoken_kind_t; -#endif -/* Token kinds. */ -#define YYEMPTY -2 -#define YYEOF 0 -#define YYerror 256 -#define YYUNDEF 257 -#define AMP 200 -#define BANG 201 -#define COLON 202 -#define DIRECTIVE 203 -#define DIR_SIGN 204 -#define ENUM 205 -#define ELLIPSIS 206 -#define EQUALS 207 -#define EXTEND 208 -#define FALSE_LITERAL 209 -#define FLOAT 210 -#define FRAGMENT 211 -#define IDENTIFIER 212 -#define INPUT 213 -#define IMPLEMENTS 214 -#define INT 215 -#define INTERFACE 216 -#define LBRACKET 217 -#define LCURLY 218 -#define LPAREN 219 -#define MUTATION 220 -#define NULL_LITERAL 221 -#define ON 222 -#define PIPE 223 -#define QUERY 224 -#define RBRACKET 225 -#define RCURLY 226 -#define REPEATABLE 227 -#define RPAREN 228 -#define SCALAR 229 -#define SCHEMA 230 -#define STRING 231 -#define SUBSCRIPTION 232 -#define TRUE_LITERAL 233 -#define TYPE_LITERAL 234 -#define UNION 235 -#define VAR_SIGN 236 - -/* Value type. */ -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef int YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 -# define YYSTYPE_IS_DECLARED 1 -#endif - - - - -int yyparse (VALUE parser, VALUE filename); - - - -/* Symbol kind. */ -enum yysymbol_kind_t -{ - YYSYMBOL_YYEMPTY = -2, - YYSYMBOL_YYEOF = 0, /* "end of file" */ - YYSYMBOL_YYerror = 1, /* error */ - YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ - YYSYMBOL_AMP = 3, /* AMP */ - YYSYMBOL_BANG = 4, /* BANG */ - YYSYMBOL_COLON = 5, /* COLON */ - YYSYMBOL_DIRECTIVE = 6, /* DIRECTIVE */ - YYSYMBOL_DIR_SIGN = 7, /* DIR_SIGN */ - YYSYMBOL_ENUM = 8, /* ENUM */ - YYSYMBOL_ELLIPSIS = 9, /* ELLIPSIS */ - YYSYMBOL_EQUALS = 10, /* EQUALS */ - YYSYMBOL_EXTEND = 11, /* EXTEND */ - YYSYMBOL_FALSE_LITERAL = 12, /* FALSE_LITERAL */ - YYSYMBOL_FLOAT = 13, /* FLOAT */ - YYSYMBOL_FRAGMENT = 14, /* FRAGMENT */ - YYSYMBOL_IDENTIFIER = 15, /* IDENTIFIER */ - YYSYMBOL_INPUT = 16, /* INPUT */ - YYSYMBOL_IMPLEMENTS = 17, /* IMPLEMENTS */ - YYSYMBOL_INT = 18, /* INT */ - YYSYMBOL_INTERFACE = 19, /* INTERFACE */ - YYSYMBOL_LBRACKET = 20, /* LBRACKET */ - YYSYMBOL_LCURLY = 21, /* LCURLY */ - YYSYMBOL_LPAREN = 22, /* LPAREN */ - YYSYMBOL_MUTATION = 23, /* MUTATION */ - YYSYMBOL_NULL_LITERAL = 24, /* NULL_LITERAL */ - YYSYMBOL_ON = 25, /* ON */ - YYSYMBOL_PIPE = 26, /* PIPE */ - YYSYMBOL_QUERY = 27, /* QUERY */ - YYSYMBOL_RBRACKET = 28, /* RBRACKET */ - YYSYMBOL_RCURLY = 29, /* RCURLY */ - YYSYMBOL_REPEATABLE = 30, /* REPEATABLE */ - YYSYMBOL_RPAREN = 31, /* RPAREN */ - YYSYMBOL_SCALAR = 32, /* SCALAR */ - YYSYMBOL_SCHEMA = 33, /* SCHEMA */ - YYSYMBOL_STRING = 34, /* STRING */ - YYSYMBOL_SUBSCRIPTION = 35, /* SUBSCRIPTION */ - YYSYMBOL_TRUE_LITERAL = 36, /* TRUE_LITERAL */ - YYSYMBOL_TYPE_LITERAL = 37, /* TYPE_LITERAL */ - YYSYMBOL_UNION = 38, /* UNION */ - YYSYMBOL_VAR_SIGN = 39, /* VAR_SIGN */ - YYSYMBOL_YYACCEPT = 40, /* $accept */ - YYSYMBOL_start = 41, /* start */ - YYSYMBOL_document = 42, /* document */ - YYSYMBOL_definitions_list = 43, /* definitions_list */ - YYSYMBOL_definition = 44, /* definition */ - YYSYMBOL_executable_definition = 45, /* executable_definition */ - YYSYMBOL_operation_definition = 46, /* operation_definition */ - YYSYMBOL_operation_type = 47, /* operation_type */ - YYSYMBOL_operation_name_opt = 48, /* operation_name_opt */ - YYSYMBOL_variable_definitions_opt = 49, /* variable_definitions_opt */ - YYSYMBOL_variable_definitions_list = 50, /* variable_definitions_list */ - YYSYMBOL_variable_definition = 51, /* variable_definition */ - YYSYMBOL_default_value_opt = 52, /* default_value_opt */ - YYSYMBOL_selection_list = 53, /* selection_list */ - YYSYMBOL_selection = 54, /* selection */ - YYSYMBOL_selection_set = 55, /* selection_set */ - YYSYMBOL_selection_set_opt = 56, /* selection_set_opt */ - YYSYMBOL_field = 57, /* field */ - YYSYMBOL_arguments_opt = 58, /* arguments_opt */ - YYSYMBOL_arguments_list = 59, /* arguments_list */ - YYSYMBOL_argument = 60, /* argument */ - YYSYMBOL_literal_value = 61, /* literal_value */ - YYSYMBOL_input_value = 62, /* input_value */ - YYSYMBOL_null_value = 63, /* null_value */ - YYSYMBOL_variable = 64, /* variable */ - YYSYMBOL_list_value = 65, /* list_value */ - YYSYMBOL_list_value_list = 66, /* list_value_list */ - YYSYMBOL_enum_name = 67, /* enum_name */ - YYSYMBOL_enum_value = 68, /* enum_value */ - YYSYMBOL_object_value = 69, /* object_value */ - YYSYMBOL_object_value_list_opt = 70, /* object_value_list_opt */ - YYSYMBOL_object_value_list = 71, /* object_value_list */ - YYSYMBOL_object_value_field = 72, /* object_value_field */ - YYSYMBOL_object_literal_value = 73, /* object_literal_value */ - YYSYMBOL_object_literal_value_list_opt = 74, /* object_literal_value_list_opt */ - YYSYMBOL_object_literal_value_list = 75, /* object_literal_value_list */ - YYSYMBOL_object_literal_value_field = 76, /* object_literal_value_field */ - YYSYMBOL_directives_list_opt = 77, /* directives_list_opt */ - YYSYMBOL_directives_list = 78, /* directives_list */ - YYSYMBOL_directive = 79, /* directive */ - YYSYMBOL_name = 80, /* name */ - YYSYMBOL_schema_keyword = 81, /* schema_keyword */ - YYSYMBOL_name_without_on = 82, /* name_without_on */ - YYSYMBOL_fragment_spread = 83, /* fragment_spread */ - YYSYMBOL_inline_fragment = 84, /* inline_fragment */ - YYSYMBOL_fragment_definition = 85, /* fragment_definition */ - YYSYMBOL_fragment_name_opt = 86, /* fragment_name_opt */ - YYSYMBOL_type = 87, /* type */ - YYSYMBOL_nullable_type = 88, /* nullable_type */ - YYSYMBOL_type_system_definition = 89, /* type_system_definition */ - YYSYMBOL_schema_definition = 90, /* schema_definition */ - YYSYMBOL_operation_type_definition_list_opt = 91, /* operation_type_definition_list_opt */ - YYSYMBOL_operation_type_definition_list = 92, /* operation_type_definition_list */ - YYSYMBOL_operation_type_definition = 93, /* operation_type_definition */ - YYSYMBOL_type_definition = 94, /* type_definition */ - YYSYMBOL_description = 95, /* description */ - YYSYMBOL_description_opt = 96, /* description_opt */ - YYSYMBOL_scalar_type_definition = 97, /* scalar_type_definition */ - YYSYMBOL_object_type_definition = 98, /* object_type_definition */ - YYSYMBOL_implements_opt = 99, /* implements_opt */ - YYSYMBOL_interfaces_list = 100, /* interfaces_list */ - YYSYMBOL_legacy_interfaces_list = 101, /* legacy_interfaces_list */ - YYSYMBOL_input_value_definition = 102, /* input_value_definition */ - YYSYMBOL_input_value_definition_list = 103, /* input_value_definition_list */ - YYSYMBOL_arguments_definitions_opt = 104, /* arguments_definitions_opt */ - YYSYMBOL_field_definition = 105, /* field_definition */ - YYSYMBOL_field_definition_list_opt = 106, /* field_definition_list_opt */ - YYSYMBOL_field_definition_list = 107, /* field_definition_list */ - YYSYMBOL_interface_type_definition = 108, /* interface_type_definition */ - YYSYMBOL_pipe_opt = 109, /* pipe_opt */ - YYSYMBOL_union_members = 110, /* union_members */ - YYSYMBOL_union_type_definition = 111, /* union_type_definition */ - YYSYMBOL_enum_type_definition = 112, /* enum_type_definition */ - YYSYMBOL_enum_value_definition = 113, /* enum_value_definition */ - YYSYMBOL_enum_value_definitions = 114, /* enum_value_definitions */ - YYSYMBOL_input_object_type_definition = 115, /* input_object_type_definition */ - YYSYMBOL_directive_definition = 116, /* directive_definition */ - YYSYMBOL_directive_repeatable_opt = 117, /* directive_repeatable_opt */ - YYSYMBOL_directive_locations = 118, /* directive_locations */ - YYSYMBOL_type_system_extension = 119, /* type_system_extension */ - YYSYMBOL_schema_extension = 120, /* schema_extension */ - YYSYMBOL_type_extension = 121, /* type_extension */ - YYSYMBOL_scalar_type_extension = 122, /* scalar_type_extension */ - YYSYMBOL_object_type_extension = 123, /* object_type_extension */ - YYSYMBOL_interface_type_extension = 124, /* interface_type_extension */ - YYSYMBOL_union_type_extension = 125, /* union_type_extension */ - YYSYMBOL_enum_type_extension = 126, /* enum_type_extension */ - YYSYMBOL_input_object_type_extension = 127 /* input_object_type_extension */ -}; -typedef enum yysymbol_kind_t yysymbol_kind_t; - - - - -#ifdef short -# undef short -#endif - -/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure - and (if available) are included - so that the code can choose integer types of a good width. */ - -#ifndef __PTRDIFF_MAX__ -# include /* INFRINGES ON USER NAME SPACE */ -# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_STDINT_H -# endif -#endif - -/* Narrow types that promote to a signed type and that can represent a - signed or unsigned integer of at least N bits. In tables they can - save space and decrease cache pressure. Promoting to a signed type - helps avoid bugs in integer arithmetic. */ - -#ifdef __INT_LEAST8_MAX__ -typedef __INT_LEAST8_TYPE__ yytype_int8; -#elif defined YY_STDINT_H -typedef int_least8_t yytype_int8; -#else -typedef signed char yytype_int8; -#endif - -#ifdef __INT_LEAST16_MAX__ -typedef __INT_LEAST16_TYPE__ yytype_int16; -#elif defined YY_STDINT_H -typedef int_least16_t yytype_int16; -#else -typedef short yytype_int16; -#endif - -/* Work around bug in HP-UX 11.23, which defines these macros - incorrectly for preprocessor constants. This workaround can likely - be removed in 2023, as HPE has promised support for HP-UX 11.23 - (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of - . */ -#ifdef __hpux -# undef UINT_LEAST8_MAX -# undef UINT_LEAST16_MAX -# define UINT_LEAST8_MAX 255 -# define UINT_LEAST16_MAX 65535 -#endif - -#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST8_TYPE__ yytype_uint8; -#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ - && UINT_LEAST8_MAX <= INT_MAX) -typedef uint_least8_t yytype_uint8; -#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX -typedef unsigned char yytype_uint8; -#else -typedef short yytype_uint8; -#endif - -#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST16_TYPE__ yytype_uint16; -#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ - && UINT_LEAST16_MAX <= INT_MAX) -typedef uint_least16_t yytype_uint16; -#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX -typedef unsigned short yytype_uint16; -#else -typedef int yytype_uint16; -#endif - -#ifndef YYPTRDIFF_T -# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ -# define YYPTRDIFF_T __PTRDIFF_TYPE__ -# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ -# elif defined PTRDIFF_MAX -# ifndef ptrdiff_t -# include /* INFRINGES ON USER NAME SPACE */ -# endif -# define YYPTRDIFF_T ptrdiff_t -# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX -# else -# define YYPTRDIFF_T long -# define YYPTRDIFF_MAXIMUM LONG_MAX -# endif -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned -# endif -#endif - -#define YYSIZE_MAXIMUM \ - YY_CAST (YYPTRDIFF_T, \ - (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ - ? YYPTRDIFF_MAXIMUM \ - : YY_CAST (YYSIZE_T, -1))) - -#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) - - -/* Stored state numbers (used for stacks). */ -typedef yytype_int16 yy_state_t; - -/* State numbers in computations. */ -typedef int yy_state_fast_t; - -#ifndef YY_ -# if defined YYENABLE_NLS && YYENABLE_NLS -# if ENABLE_NLS -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_(Msgid) dgettext ("bison-runtime", Msgid) -# endif -# endif -# ifndef YY_ -# define YY_(Msgid) Msgid -# endif -#endif - - -#ifndef YY_ATTRIBUTE_PURE -# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) -# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) -# else -# define YY_ATTRIBUTE_PURE -# endif -#endif - -#ifndef YY_ATTRIBUTE_UNUSED -# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) -# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -# else -# define YY_ATTRIBUTE_UNUSED -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YY_USE(E) ((void) (E)) -#else -# define YY_USE(E) /* empty */ -#endif - -/* Suppress an incorrect diagnostic about yylval being uninitialized. */ -#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ -# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") -# else -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ - _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -# endif -# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ - _Pragma ("GCC diagnostic pop") -#else -# define YY_INITIAL_VALUE(Value) Value -#endif -#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_END -#endif -#ifndef YY_INITIAL_VALUE -# define YY_INITIAL_VALUE(Value) /* Nothing. */ -#endif - -#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ -# define YY_IGNORE_USELESS_CAST_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") -# define YY_IGNORE_USELESS_CAST_END \ - _Pragma ("GCC diagnostic pop") -#endif -#ifndef YY_IGNORE_USELESS_CAST_BEGIN -# define YY_IGNORE_USELESS_CAST_BEGIN -# define YY_IGNORE_USELESS_CAST_END -#endif - - -#define YY_ASSERT(E) ((void) (0 && (E))) - -#if 1 - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS -# include /* INFRINGES ON USER NAME SPACE */ - /* Use EXIT_SUCCESS as a witness for stdlib.h. */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's 'empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined EXIT_SUCCESS -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* 1 */ - -#if (! defined yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - yy_state_t yyss_alloc; - YYSTYPE yyvs_alloc; -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -# define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYPTRDIFF_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / YYSIZEOF (*yyptr); \ - } \ - while (0) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from SRC to DST. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(Dst, Src, Count) \ - __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) -# else -# define YYCOPY(Dst, Src, Count) \ - do \ - { \ - YYPTRDIFF_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (Dst)[yyi] = (Src)[yyi]; \ - } \ - while (0) -# endif -# endif -#endif /* !YYCOPY_NEEDED */ - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 79 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 777 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 40 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 88 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 184 -/* YYNSTATES -- Number of states. */ -#define YYNSTATES 313 - -/* YYMAXUTOK -- Last valid token kind. */ -#define YYMAXUTOK 257 - - -/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, with out-of-bounds checking. */ -#define YYTRANSLATE(YYX) \ - (0 <= (YYX) && (YYX) <= YYMAXUTOK \ - ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ - : YYSYMBOL_YYUNDEF) - -/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex. */ -static const yytype_int8 yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2 -}; - -#if YYDEBUG -/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_int16 yyrline[] = -{ - 0, 103, 103, 105, 119, 120, 123, 124, 125, 128, - 129, 132, 143, 154, 167, 168, 169, 172, 173, 176, - 177, 180, 181, 184, 196, 197, 200, 201, 204, 205, - 206, 209, 212, 213, 216, 227, 240, 241, 244, 245, - 248, 258, 259, 260, 261, 262, 263, 264, 265, 266, - 269, 270, 271, 273, 281, 290, 291, 294, 295, 298, - 299, 300, 301, 303, 312, 321, 322, 325, 326, 329, - 340, 349, 350, 353, 354, 357, 368, 369, 372, 373, - 375, 385, 386, 389, 390, 391, 392, 393, 394, 395, - 396, 397, 398, 399, 400, 403, 404, 405, 406, 407, - 408, 412, 422, 431, 442, 454, 455, 458, 459, 462, - 469, 478, 479, 480, 483, 496, 497, 500, 504, 509, - 514, 515, 516, 517, 518, 519, 521, 524, 525, 528, - 540, 554, 555, 556, 557, 560, 568, 574, 582, 587, - 601, 602, 605, 606, 609, 623, 624, 627, 628, 629, - 632, 646, 647, 650, 658, 663, 676, 689, 701, 702, - 705, 718, 732, 733, 736, 737, 741, 742, 745, 756, - 768, 769, 770, 771, 772, 773, 775, 785, 797, 809, - 818, 829, 838, 849, 858 -}; -#endif - -/** Accessing symbol of state STATE. */ -#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) - -#if 1 -/* The user-facing name of the symbol whose (internal) number is - YYSYMBOL. No bounds checking. */ -static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; - -static const char * -yysymbol_name (yysymbol_kind_t yysymbol) -{ - static const char *const yy_sname[] = - { - "end of file", "error", "invalid token", "AMP", "BANG", "COLON", - "DIRECTIVE", "DIR_SIGN", "ENUM", "ELLIPSIS", "EQUALS", "EXTEND", - "FALSE_LITERAL", "FLOAT", "FRAGMENT", "IDENTIFIER", "INPUT", - "IMPLEMENTS", "INT", "INTERFACE", "LBRACKET", "LCURLY", "LPAREN", - "MUTATION", "NULL_LITERAL", "ON", "PIPE", "QUERY", "RBRACKET", "RCURLY", - "REPEATABLE", "RPAREN", "SCALAR", "SCHEMA", "STRING", "SUBSCRIPTION", - "TRUE_LITERAL", "TYPE_LITERAL", "UNION", "VAR_SIGN", "$accept", "start", - "document", "definitions_list", "definition", "executable_definition", - "operation_definition", "operation_type", "operation_name_opt", - "variable_definitions_opt", "variable_definitions_list", - "variable_definition", "default_value_opt", "selection_list", - "selection", "selection_set", "selection_set_opt", "field", - "arguments_opt", "arguments_list", "argument", "literal_value", - "input_value", "null_value", "variable", "list_value", "list_value_list", - "enum_name", "enum_value", "object_value", "object_value_list_opt", - "object_value_list", "object_value_field", "object_literal_value", - "object_literal_value_list_opt", "object_literal_value_list", - "object_literal_value_field", "directives_list_opt", "directives_list", - "directive", "name", "schema_keyword", "name_without_on", - "fragment_spread", "inline_fragment", "fragment_definition", - "fragment_name_opt", "type", "nullable_type", "type_system_definition", - "schema_definition", "operation_type_definition_list_opt", - "operation_type_definition_list", "operation_type_definition", - "type_definition", "description", "description_opt", - "scalar_type_definition", "object_type_definition", "implements_opt", - "interfaces_list", "legacy_interfaces_list", "input_value_definition", - "input_value_definition_list", "arguments_definitions_opt", - "field_definition", "field_definition_list_opt", "field_definition_list", - "interface_type_definition", "pipe_opt", "union_members", - "union_type_definition", "enum_type_definition", "enum_value_definition", - "enum_value_definitions", "input_object_type_definition", - "directive_definition", "directive_repeatable_opt", - "directive_locations", "type_system_extension", "schema_extension", - "type_extension", "scalar_type_extension", "object_type_extension", - "interface_type_extension", "union_type_extension", - "enum_type_extension", "input_object_type_extension", YY_NULLPTR - }; - return yy_sname[yysymbol]; -} -#endif - -#define YYPACT_NINF (-195) - -#define yypact_value_is_default(Yyn) \ - ((Yyn) == YYPACT_NINF) - -#define YYTABLE_NINF (-148) - -#define yytable_value_is_error(Yyn) \ - 0 - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -static const yytype_int16 yypact[] = -{ - 234, 155, 739, 475, -195, -195, 4, -195, -195, 44, - -195, 95, -195, -195, -195, 706, -195, -195, -195, -195, - -195, 15, -195, -195, -195, -195, -195, -195, -195, -195, - -195, -195, -195, -195, -195, -195, -195, -195, 706, 706, - 706, 706, 4, 706, 706, -195, -195, -195, -195, -195, - -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, - -195, -195, -195, -195, 48, 574, -195, -195, 508, -195, - -195, 10, -195, -195, -195, 706, 24, 4, -195, -195, - -195, 58, -195, 81, 706, 706, 706, 706, 706, 706, - 4, 4, 77, 4, 82, 7, 77, 4, 607, 607, - 83, 4, -195, -195, 706, 706, 4, 89, 73, -195, - -195, 74, 4, 706, 4, 4, 77, 4, 77, 4, - 91, 7, 93, 7, 306, 4, 4, 73, 4, 113, - 19, 607, -195, 4, 111, 4, 640, -195, -195, 89, - 673, -195, 127, 83, -195, 138, 115, -195, 706, -15, - -195, 83, 132, 135, 136, 4, -195, 4, 166, 144, - 144, 706, 261, 176, 706, 160, 137, 160, 156, 157, - 83, -195, 83, 541, 4, -195, -195, 408, -195, -195, - 706, -195, -195, 178, -195, -195, -195, 144, 154, 144, - 144, 160, 160, 156, 205, -195, 20, 706, -195, 32, - -195, 176, 706, -195, 41, -195, -195, -195, -195, 706, - 164, -195, -195, -195, -195, 83, -195, -195, -195, -195, - 340, 706, -195, -195, -195, -195, 706, -195, -195, -195, - -195, -195, -195, -195, -195, -195, -195, -195, -195, 607, - -4, -195, 171, 105, 118, -195, -195, 164, 4, -195, - -195, 192, -195, -195, -195, 706, -195, 139, -195, 706, - -195, -195, -195, 374, 169, 706, -195, 172, 706, -195, - 195, -195, 194, -195, 706, -195, -195, -195, 607, 132, - -195, -195, -195, -195, -195, -195, -195, 200, -195, -195, - 202, 408, 442, 4, -195, 186, 194, 209, 408, 442, - -195, -195, 706, -195, -195, 706, 4, 607, -195, -195, - -195, 4, -195 -}; - -/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ -static const yytype_uint8 yydefact[] = -{ - 127, 0, 105, 0, 15, 14, 76, 126, 16, 0, - 2, 127, 4, 6, 9, 17, 10, 7, 111, 112, - 128, 0, 120, 121, 122, 123, 124, 125, 113, 8, - 166, 167, 170, 171, 172, 173, 174, 175, 0, 0, - 0, 0, 76, 0, 0, 91, 89, 92, 97, 93, - 95, 90, 86, 87, 98, 94, 84, 83, 96, 85, - 88, 99, 100, 106, 0, 76, 82, 13, 0, 26, - 28, 36, 81, 29, 30, 0, 115, 77, 78, 1, - 5, 19, 18, 0, 0, 0, 0, 0, 0, 0, - 76, 76, 131, 0, 0, 169, 131, 76, 0, 0, - 0, 76, 12, 27, 0, 0, 76, 36, 0, 114, - 79, 0, 76, 0, 76, 76, 131, 76, 131, 76, - 0, 182, 0, 184, 0, 76, 176, 0, 76, 0, - 180, 0, 109, 76, 107, 76, 0, 103, 101, 36, - 0, 38, 0, 32, 80, 0, 0, 117, 0, 0, - 21, 0, 142, 0, 0, 76, 129, 76, 0, 127, - 127, 0, 135, 133, 134, 145, 0, 145, 151, 0, - 0, 108, 0, 0, 76, 37, 39, 0, 33, 35, - 0, 116, 118, 0, 20, 22, 11, 127, 162, 127, - 127, 145, 145, 151, 0, 158, 127, 0, 140, 127, - 135, 132, 0, 138, 127, 178, 168, 177, 152, 0, - 179, 110, 104, 102, 31, 32, 45, 41, 59, 42, - 0, 65, 53, 60, 43, 44, 0, 61, 50, 40, - 46, 51, 48, 63, 47, 52, 49, 62, 119, 0, - 127, 163, 0, 127, 127, 150, 130, 155, 76, 181, - 159, 0, 183, 141, 136, 0, 148, 127, 153, 0, - 34, 55, 57, 0, 0, 66, 67, 0, 72, 73, - 0, 54, 24, 143, 0, 156, 160, 157, 0, 142, - 146, 149, 154, 56, 58, 64, 68, 0, 70, 74, - 0, 0, 0, 76, 164, 161, 24, 0, 0, 0, - 50, 69, 71, 25, 23, 0, 76, 0, 75, 165, - 139, 76, 144 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int16 yypgoto[] = -{ - -195, -195, -195, -195, 204, -195, -195, 9, -195, -195, - -195, 68, -71, 90, -66, -93, 12, -195, -89, -195, - 94, -194, -174, -195, -195, -195, -195, 37, -195, -195, - -195, -195, -32, -195, -195, -195, -29, 34, -34, -52, - -3, -172, 2, -195, -195, -195, -195, -98, -195, -195, - -195, -195, 109, -127, -195, -195, 6, -195, -195, -54, - 80, -195, -189, -100, -35, -11, -99, -195, -195, -195, - 54, -195, -195, -183, 61, -195, -195, -195, -195, -195, - -195, -195, -195, -195, -195, -195, -195, -195 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int16 yydefgoto[] = -{ - 0, 9, 10, 11, 12, 13, 14, 61, 81, 112, - 149, 150, 293, 68, 69, 178, 179, 70, 106, 140, - 141, 228, 301, 230, 231, 232, 263, 233, 234, 235, - 264, 265, 266, 236, 267, 268, 269, 76, 77, 78, - 132, 62, 72, 73, 74, 16, 64, 133, 134, 17, - 18, 109, 146, 147, 19, 20, 197, 22, 23, 125, - 163, 164, 198, 199, 188, 256, 205, 257, 24, 209, - 210, 25, 26, 195, 196, 27, 28, 242, 295, 29, - 30, 31, 32, 33, 34, 35, 36, 37 -}; - -/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_int16 yytable[] = -{ - 71, 135, 103, 229, 63, 237, 21, 137, 95, 15, - 253, 75, 82, 250, 75, 104, 184, 21, 144, 182, - 15, 83, 237, 84, 148, 110, 75, 273, -77, -77, - 7, 85, 105, 169, 86, 90, 91, 92, 93, 182, - 96, 97, 128, 110, 79, 108, 262, 87, 237, 249, - 174, 253, 88, 89, 7, 253, 121, 123, 186, 126, - 250, 252, 155, 130, 157, 71, 7, 101, 207, 110, - -147, 110, 107, 98, 110, 7, 94, 212, 110, 213, - 111, 114, 115, 116, 117, 118, 119, 240, 113, 284, - 244, 237, 245, 246, 124, -3, 4, 300, 303, 100, - 5, 139, 142, 127, 136, 308, 1, 103, 8, 2, - 152, 105, 159, 148, 160, 171, 3, 145, 4, 237, - 237, 162, 5, 168, 120, 122, 237, 237, 6, 7, - 8, 129, 177, 71, 275, 138, 145, 142, 4, 7, - 143, 272, 5, 180, 181, 183, 151, 276, 153, 154, - 8, 156, 7, 158, 187, 145, 189, 190, 200, 165, - 4, 203, 167, 38, 5, 194, 206, 170, 280, 172, - 71, 39, 8, 7, 40, 145, 193, 238, 7, 202, - 296, 204, 208, 239, 241, 211, 227, 41, 42, 191, - 259, 192, 43, 44, 251, 194, 274, 278, 285, 254, - 291, 288, 194, 227, 292, 298, 258, 299, 215, 311, - 255, 45, 305, 46, 307, 80, 47, 185, 270, 49, - 218, 51, 52, 271, 53, 306, 173, 260, 4, 227, - 223, 248, 5, 286, 176, 55, 166, 56, 57, 289, - 8, 201, 59, 60, 297, 1, 281, 247, 2, 194, - 243, 0, 279, 0, 0, 3, 282, 4, 0, 0, - 0, 5, 287, 255, 0, 290, 0, 6, 7, 8, - 0, 294, 227, -137, 0, 0, -137, 0, -137, 0, - 0, 0, 277, 0, 0, -137, -137, 0, 0, 0, - 0, -137, 0, 0, 0, 0, 0, -137, 0, 290, - 227, 227, 309, 0, 0, 0, 0, 227, 227, 161, - 0, 0, 45, 0, 46, 0, 0, 47, 48, 0, - 49, 50, 51, 52, 0, 53, 0, 304, 0, 4, - 54, 66, 0, 5, 0, 0, 55, 0, 56, 57, - 310, 8, 58, 59, 60, 312, 45, 0, 46, 0, - 0, 47, 216, 217, 49, 218, 51, 52, 219, 53, - 220, 221, 0, 4, 222, 223, 0, 5, 261, 0, - 55, 0, 56, 57, 224, 8, 225, 59, 60, 226, - 45, 0, 46, 0, 0, 47, 216, 217, 49, 218, - 51, 52, 219, 53, 220, 221, 0, 4, 222, 223, - 0, 5, 283, 0, 55, 0, 56, 57, 224, 8, - 225, 59, 60, 226, 45, 0, 46, 0, 0, 47, - 216, 217, 49, 218, 51, 52, 219, 53, 220, 221, - 0, 4, 222, 223, 0, 5, 0, 0, 55, 0, - 56, 57, 224, 8, 225, 59, 60, 226, 45, 0, - 46, 0, 0, 47, 216, 217, 49, 218, 51, 52, - 219, 53, 220, 302, 0, 4, 222, 223, 0, 5, - 0, 0, 55, 0, 56, 57, 224, 8, 225, 59, - 60, 45, 0, 46, 65, 0, 47, 48, 0, 49, - 50, 51, 52, 0, 53, 0, 0, 0, 4, 54, - 66, 0, 5, 0, 67, 55, 0, 56, 57, 0, - 8, 58, 59, 60, 45, 0, 46, 65, 0, 47, - 48, 0, 49, 50, 51, 52, 0, 53, 0, 0, - 0, 4, 54, 66, 0, 5, 0, 102, 55, 0, - 56, 57, 0, 8, 58, 59, 60, 45, 0, 46, - 65, 0, 47, 48, 0, 49, 50, 51, 52, 0, - 53, 0, 0, 0, 4, 54, 66, 0, 5, 0, - 214, 55, 0, 56, 57, 0, 8, 58, 59, 60, - 45, 75, 46, 0, 0, 47, 48, 0, 49, 50, - 51, 52, 0, 53, 0, 0, 0, 4, 54, 99, - 0, 5, 0, 0, 55, 0, 56, 57, 0, 8, - 58, 59, 60, 45, 0, 46, 0, 0, 47, 48, - 0, 49, 50, 51, 52, 0, 53, 131, 0, 0, - 4, 54, 66, 0, 5, 0, 0, 55, 0, 56, - 57, 0, 8, 58, 59, 60, 45, 0, 46, 65, - 0, 47, 48, 0, 49, 50, 51, 52, 0, 53, - 0, 0, 0, 4, 54, 66, 0, 5, 0, 0, - 55, 0, 56, 57, 0, 8, 58, 59, 60, 45, - 0, 46, 0, 0, 47, 48, 0, 49, 50, 51, - 52, 0, 53, 0, 0, 0, 4, 54, 66, 0, - 5, 0, 0, 55, 175, 56, 57, 0, 8, 58, - 59, 60, 45, 0, 46, 0, 0, 47, 48, 0, - 49, 50, 51, 52, 0, 53, 0, 0, 0, 4, - 54, 66, 0, 5, 0, 0, 55, 0, 56, 57, - 0, 8, 58, 59, 60, 45, 0, 46, 0, 0, - 47, 48, 0, 49, 50, 51, 52, 0, 53, 0, - 0, 0, 4, 54, 0, 0, 5, 0, 0, 55, - 0, 56, 57, 0, 8, 58, 59, 60 -}; - -static const yytype_int16 yycheck[] = -{ - 3, 99, 68, 177, 2, 177, 0, 100, 42, 0, - 199, 7, 15, 196, 7, 5, 31, 11, 107, 146, - 11, 6, 194, 8, 39, 77, 7, 31, 21, 10, - 34, 16, 22, 131, 19, 38, 39, 40, 41, 166, - 43, 44, 96, 95, 0, 21, 220, 32, 220, 29, - 139, 240, 37, 38, 34, 244, 90, 91, 151, 93, - 243, 29, 116, 97, 118, 68, 34, 65, 167, 121, - 29, 123, 75, 25, 126, 34, 42, 170, 130, 172, - 22, 84, 85, 86, 87, 88, 89, 187, 7, 263, - 190, 263, 191, 192, 17, 0, 23, 291, 292, 65, - 27, 104, 105, 21, 21, 299, 11, 173, 35, 14, - 113, 22, 21, 39, 21, 4, 21, 108, 23, 291, - 292, 124, 27, 10, 90, 91, 298, 299, 33, 34, - 35, 97, 5, 136, 29, 101, 127, 140, 23, 34, - 106, 239, 27, 5, 29, 148, 112, 29, 114, 115, - 35, 117, 34, 119, 22, 146, 21, 21, 161, 125, - 23, 164, 128, 8, 27, 159, 29, 133, 29, 135, - 173, 16, 35, 34, 19, 166, 10, 180, 34, 3, - 278, 21, 26, 5, 30, 28, 177, 32, 33, 155, - 26, 157, 37, 38, 197, 189, 25, 5, 29, 202, - 5, 29, 196, 194, 10, 5, 209, 5, 174, 307, - 204, 6, 26, 8, 5, 11, 11, 149, 221, 14, - 15, 16, 17, 226, 19, 296, 136, 215, 23, 220, - 25, 194, 27, 265, 140, 30, 127, 32, 33, 268, - 35, 161, 37, 38, 279, 11, 257, 193, 14, 243, - 189, -1, 255, -1, -1, 21, 259, 23, -1, -1, - -1, 27, 265, 257, -1, 268, -1, 33, 34, 35, - -1, 274, 263, 12, -1, -1, 15, -1, 17, -1, - -1, -1, 248, -1, -1, 24, 25, -1, -1, -1, - -1, 30, -1, -1, -1, -1, -1, 36, -1, 302, - 291, 292, 305, -1, -1, -1, -1, 298, 299, 3, - -1, -1, 6, -1, 8, -1, -1, 11, 12, -1, - 14, 15, 16, 17, -1, 19, -1, 293, -1, 23, - 24, 25, -1, 27, -1, -1, 30, -1, 32, 33, - 306, 35, 36, 37, 38, 311, 6, -1, 8, -1, - -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, -1, 23, 24, 25, -1, 27, 28, -1, - 30, -1, 32, 33, 34, 35, 36, 37, 38, 39, - 6, -1, 8, -1, -1, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, -1, 23, 24, 25, - -1, 27, 28, -1, 30, -1, 32, 33, 34, 35, - 36, 37, 38, 39, 6, -1, 8, -1, -1, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - -1, 23, 24, 25, -1, 27, -1, -1, 30, -1, - 32, 33, 34, 35, 36, 37, 38, 39, 6, -1, - 8, -1, -1, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, -1, 23, 24, 25, -1, 27, - -1, -1, 30, -1, 32, 33, 34, 35, 36, 37, - 38, 6, -1, 8, 9, -1, 11, 12, -1, 14, - 15, 16, 17, -1, 19, -1, -1, -1, 23, 24, - 25, -1, 27, -1, 29, 30, -1, 32, 33, -1, - 35, 36, 37, 38, 6, -1, 8, 9, -1, 11, - 12, -1, 14, 15, 16, 17, -1, 19, -1, -1, - -1, 23, 24, 25, -1, 27, -1, 29, 30, -1, - 32, 33, -1, 35, 36, 37, 38, 6, -1, 8, - 9, -1, 11, 12, -1, 14, 15, 16, 17, -1, - 19, -1, -1, -1, 23, 24, 25, -1, 27, -1, - 29, 30, -1, 32, 33, -1, 35, 36, 37, 38, - 6, 7, 8, -1, -1, 11, 12, -1, 14, 15, - 16, 17, -1, 19, -1, -1, -1, 23, 24, 25, - -1, 27, -1, -1, 30, -1, 32, 33, -1, 35, - 36, 37, 38, 6, -1, 8, -1, -1, 11, 12, - -1, 14, 15, 16, 17, -1, 19, 20, -1, -1, - 23, 24, 25, -1, 27, -1, -1, 30, -1, 32, - 33, -1, 35, 36, 37, 38, 6, -1, 8, 9, - -1, 11, 12, -1, 14, 15, 16, 17, -1, 19, - -1, -1, -1, 23, 24, 25, -1, 27, -1, -1, - 30, -1, 32, 33, -1, 35, 36, 37, 38, 6, - -1, 8, -1, -1, 11, 12, -1, 14, 15, 16, - 17, -1, 19, -1, -1, -1, 23, 24, 25, -1, - 27, -1, -1, 30, 31, 32, 33, -1, 35, 36, - 37, 38, 6, -1, 8, -1, -1, 11, 12, -1, - 14, 15, 16, 17, -1, 19, -1, -1, -1, 23, - 24, 25, -1, 27, -1, -1, 30, -1, 32, 33, - -1, 35, 36, 37, 38, 6, -1, 8, -1, -1, - 11, 12, -1, 14, 15, 16, 17, -1, 19, -1, - -1, -1, 23, 24, -1, -1, 27, -1, -1, 30, - -1, 32, 33, -1, 35, 36, 37, 38 -}; - -/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of - state STATE-NUM. */ -static const yytype_int8 yystos[] = -{ - 0, 11, 14, 21, 23, 27, 33, 34, 35, 41, - 42, 43, 44, 45, 46, 47, 85, 89, 90, 94, - 95, 96, 97, 98, 108, 111, 112, 115, 116, 119, - 120, 121, 122, 123, 124, 125, 126, 127, 8, 16, - 19, 32, 33, 37, 38, 6, 8, 11, 12, 14, - 15, 16, 17, 19, 24, 30, 32, 33, 36, 37, - 38, 47, 81, 82, 86, 9, 25, 29, 53, 54, - 57, 80, 82, 83, 84, 7, 77, 78, 79, 0, - 44, 48, 80, 6, 8, 16, 19, 32, 37, 38, - 80, 80, 80, 80, 77, 78, 80, 80, 25, 25, - 77, 82, 29, 54, 5, 22, 58, 80, 21, 91, - 79, 22, 49, 7, 80, 80, 80, 80, 80, 80, - 77, 78, 77, 78, 17, 99, 78, 21, 99, 77, - 78, 20, 80, 87, 88, 87, 21, 55, 77, 80, - 59, 60, 80, 77, 58, 47, 92, 93, 39, 50, - 51, 77, 80, 77, 77, 99, 77, 99, 77, 21, - 21, 3, 80, 100, 101, 77, 92, 77, 10, 87, - 77, 4, 77, 53, 58, 31, 60, 5, 55, 56, - 5, 29, 93, 80, 31, 51, 55, 22, 104, 21, - 21, 77, 77, 10, 96, 113, 114, 96, 102, 103, - 80, 100, 3, 80, 21, 106, 29, 106, 26, 109, - 110, 28, 55, 55, 29, 77, 12, 13, 15, 18, - 20, 21, 24, 25, 34, 36, 39, 47, 61, 62, - 63, 64, 65, 67, 68, 69, 73, 81, 80, 5, - 103, 30, 117, 114, 103, 106, 106, 110, 67, 29, - 113, 80, 29, 102, 80, 96, 105, 107, 80, 26, - 56, 28, 62, 66, 70, 71, 72, 74, 75, 76, - 80, 80, 87, 31, 25, 29, 29, 77, 5, 80, - 29, 105, 80, 28, 62, 29, 72, 80, 29, 76, - 80, 5, 10, 52, 80, 118, 87, 104, 5, 5, - 61, 62, 21, 61, 77, 26, 52, 5, 61, 80, - 77, 87, 77 -}; - -/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr1[] = -{ - 0, 40, 41, 42, 43, 43, 44, 44, 44, 45, - 45, 46, 46, 46, 47, 47, 47, 48, 48, 49, - 49, 50, 50, 51, 52, 52, 53, 53, 54, 54, - 54, 55, 56, 56, 57, 57, 58, 58, 59, 59, - 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 62, 62, 62, 63, 64, 65, 65, 66, 66, 67, - 67, 67, 67, 68, 69, 70, 70, 71, 71, 72, - 73, 74, 74, 75, 75, 76, 77, 77, 78, 78, - 79, 80, 80, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, - 82, 83, 84, 84, 85, 86, 86, 87, 87, 88, - 88, 89, 89, 89, 90, 91, 91, 92, 92, 93, - 94, 94, 94, 94, 94, 94, 95, 96, 96, 97, - 98, 99, 99, 99, 99, 100, 100, 101, 101, 102, - 103, 103, 104, 104, 105, 106, 106, 107, 107, 107, - 108, 109, 109, 110, 110, 111, 112, 113, 114, 114, - 115, 116, 117, 117, 118, 118, 119, 119, 120, 120, - 121, 121, 121, 121, 121, 121, 122, 123, 124, 125, - 125, 126, 126, 127, 127 -}; - -/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr2[] = -{ - 0, 2, 1, 1, 1, 2, 1, 1, 1, 1, - 1, 5, 3, 2, 1, 1, 1, 0, 1, 0, - 3, 1, 2, 6, 0, 2, 1, 2, 1, 1, - 1, 3, 0, 1, 6, 4, 0, 3, 1, 2, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 3, 1, 2, 1, - 1, 1, 1, 1, 3, 0, 1, 1, 2, 3, - 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 3, 5, 3, 6, 0, 1, 1, 2, 1, - 3, 1, 1, 1, 3, 0, 3, 1, 2, 3, - 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, - 6, 0, 3, 2, 2, 1, 3, 1, 2, 6, - 1, 2, 0, 3, 6, 0, 3, 0, 1, 2, - 6, 0, 1, 2, 3, 6, 7, 3, 1, 2, - 7, 8, 0, 1, 1, 3, 1, 1, 6, 3, - 1, 1, 1, 1, 1, 1, 4, 6, 6, 6, - 4, 7, 4, 7, 4 -}; - - -enum { YYENOMEM = -2 }; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab -#define YYNOMEM goto yyexhaustedlab - - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ - do \ - if (yychar == YYEMPTY) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK (yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (parser, filename, YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ - while (0) - -/* Backward compatibility with an undocumented macro. - Use YYerror or YYUNDEF. */ -#define YYERRCODE YYUNDEF - - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) - - - - -# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Kind, Value, parser, filename); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) - - -/*-----------------------------------. -| Print this symbol's value on YYO. | -`-----------------------------------*/ - -static void -yy_symbol_value_print (FILE *yyo, - yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, VALUE parser, VALUE filename) -{ - FILE *yyoutput = yyo; - YY_USE (yyoutput); - YY_USE (parser); - YY_USE (filename); - if (!yyvaluep) - return; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE (yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - - -/*---------------------------. -| Print this symbol on YYO. | -`---------------------------*/ - -static void -yy_symbol_print (FILE *yyo, - yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, VALUE parser, VALUE filename) -{ - YYFPRINTF (yyo, "%s %s (", - yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); - - yy_symbol_value_print (yyo, yykind, yyvaluep, parser, filename); - YYFPRINTF (yyo, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -static void -yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) -{ - YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (0) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -static void -yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, - int yyrule, VALUE parser, VALUE filename) -{ - int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", - yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - YYFPRINTF (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, - YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), - &yyvsp[(yyi + 1) - (yynrhs)], parser, filename); - YYFPRINTF (stderr, "\n"); - } -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyssp, yyvsp, Rule, parser, filename); \ -} while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) ((void) 0) -# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - -/* Context of a parse error. */ -typedef struct -{ - yy_state_t *yyssp; - yysymbol_kind_t yytoken; -} yypcontext_t; - -/* Put in YYARG at most YYARGN of the expected tokens given the - current YYCTX, and return the number of tokens stored in YYARG. If - YYARG is null, return the number of expected tokens (guaranteed to - be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. - Return 0 if there are more than YYARGN expected tokens, yet fill - YYARG up to YYARGN. */ -static int -yypcontext_expected_tokens (const yypcontext_t *yyctx, - yysymbol_kind_t yyarg[], int yyargn) -{ - /* Actual size of YYARG. */ - int yycount = 0; - int yyn = yypact[+*yyctx->yyssp]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yyx; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (!yyarg) - ++yycount; - else if (yycount == yyargn) - return 0; - else - yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); - } - } - if (yyarg && yycount == 0 && 0 < yyargn) - yyarg[0] = YYSYMBOL_YYEMPTY; - return yycount; -} - - - - -#ifndef yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) -# else -/* Return the length of YYSTR. */ -static YYPTRDIFF_T -yystrlen (const char *yystr) -{ - YYPTRDIFF_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) - continue; - return yylen; -} -# endif -#endif - -#ifndef yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -yystpcpy (char *yydest, const char *yysrc) -{ - char *yyd = yydest; - const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -#endif - - - -static int -yy_syntax_error_arguments (const yypcontext_t *yyctx, - yysymbol_kind_t yyarg[], int yyargn) -{ - /* Actual size of YYARG. */ - int yycount = 0; - /* There are many possibilities here to consider: - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yyctx->yytoken != YYSYMBOL_YYEMPTY) - { - int yyn; - if (yyarg) - yyarg[yycount] = yyctx->yytoken; - ++yycount; - yyn = yypcontext_expected_tokens (yyctx, - yyarg ? yyarg + 1 : yyarg, yyargn - 1); - if (yyn == YYENOMEM) - return YYENOMEM; - else - yycount += yyn; - } - return yycount; -} - -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, - const yypcontext_t *yyctx) -{ - enum { YYARGS_MAX = 5 }; - /* Internationalized format string. */ - const char *yyformat = YY_NULLPTR; - /* Arguments of yyformat: reported tokens (one for the "unexpected", - one per "expected"). */ - yysymbol_kind_t yyarg[YYARGS_MAX]; - /* Cumulated lengths of YYARG. */ - YYPTRDIFF_T yysize = 0; - - /* Actual size of YYARG. */ - int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); - if (yycount == YYENOMEM) - return YYENOMEM; - - switch (yycount) - { -#define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - default: /* Avoid compiler warnings. */ - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -#undef YYCASE_ - } - - /* Compute error message size. Don't count the "%s"s, but reserve - room for the terminator. */ - yysize = yystrlen (yyformat) - 2 * yycount + 1; - { - int yyi; - for (yyi = 0; yyi < yycount; ++yyi) - { - YYPTRDIFF_T yysize1 - = yysize + yystrlen (yysymbol_name (yyarg[yyi])); - if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) - yysize = yysize1; - else - return YYENOMEM; - } - } - - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return -1; - } - - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - { - char *yyp = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp = yystpcpy (yyp, yysymbol_name (yyarg[yyi++])); - yyformat += 2; - } - else - { - ++yyp; - ++yyformat; - } - } - return 0; -} - - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -static void -yydestruct (const char *yymsg, - yysymbol_kind_t yykind, YYSTYPE *yyvaluep, VALUE parser, VALUE filename) -{ - YY_USE (yyvaluep); - YY_USE (parser); - YY_USE (filename); - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE (yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - - - - - - -/*----------. -| yyparse. | -`----------*/ - -int -yyparse (VALUE parser, VALUE filename) -{ -/* Lookahead token kind. */ -int yychar; - - -/* The semantic value of the lookahead symbol. */ -/* Default value used for initialization, for pacifying older GCCs - or non-GCC compilers. */ -YY_INITIAL_VALUE (static YYSTYPE yyval_default;) -YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); - - /* Number of syntax errors so far. */ - int yynerrs = 0; - - yy_state_fast_t yystate = 0; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus = 0; - - /* Refer to the stacks through separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* Their size. */ - YYPTRDIFF_T yystacksize = YYINITDEPTH; - - /* The state stack: array, bottom, top. */ - yy_state_t yyssa[YYINITDEPTH]; - yy_state_t *yyss = yyssa; - yy_state_t *yyssp = yyss; - - /* The semantic value stack: array, bottom, top. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp = yyvs; - - int yyn; - /* The return value of yyparse. */ - int yyresult; - /* Lookahead symbol kind. */ - yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yychar = YYEMPTY; /* Cause a token to be read. */ - - goto yysetstate; - - -/*------------------------------------------------------------. -| yynewstate -- push a new state, which is found in yystate. | -`------------------------------------------------------------*/ -yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - - -/*--------------------------------------------------------------------. -| yysetstate -- set current state (the top of the stack) to yystate. | -`--------------------------------------------------------------------*/ -yysetstate: - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - YY_ASSERT (0 <= yystate && yystate < YYNSTATES); - YY_IGNORE_USELESS_CAST_BEGIN - *yyssp = YY_CAST (yy_state_t, yystate); - YY_IGNORE_USELESS_CAST_END - YY_STACK_PRINT (yyss, yyssp); - - if (yyss + yystacksize - 1 <= yyssp) -#if !defined yyoverflow && !defined YYSTACK_RELOCATE - YYNOMEM; -#else - { - /* Get the current used size of the three stacks, in elements. */ - YYPTRDIFF_T yysize = yyssp - yyss + 1; - -# if defined yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - yy_state_t *yyss1 = yyss; - YYSTYPE *yyvs1 = yyvs; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * YYSIZEOF (*yyssp), - &yyvs1, yysize * YYSIZEOF (*yyvsp), - &yystacksize); - yyss = yyss1; - yyvs = yyvs1; - } -# else /* defined YYSTACK_RELOCATE */ - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - YYNOMEM; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yy_state_t *yyss1 = yyss; - union yyalloc *yyptr = - YY_CAST (union yyalloc *, - YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); - if (! yyptr) - YYNOMEM; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - YY_IGNORE_USELESS_CAST_BEGIN - YYDPRINTF ((stderr, "Stack size increased to %ld\n", - YY_CAST (long, yystacksize))); - YY_IGNORE_USELESS_CAST_END - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } -#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ - - - if (yystate == YYFINAL) - YYACCEPT; - - goto yybackup; - - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to lookahead token. */ - yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token\n")); - yychar = yylex (&yylval, parser, filename); - } - - if (yychar <= YYEOF) - { - yychar = YYEOF; - yytoken = YYSYMBOL_YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else if (yychar == YYerror) - { - /* The scanner already issued an error message, process directly - to error recovery. But do not keep the error token as - lookahead, it is too special and may lead us to an endless - loop in error recovery. */ - yychar = YYUNDEF; - yytoken = YYSYMBOL_YYerror; - goto yyerrlab1; - } - else - { - yytoken = YYTRANSLATE (yychar); - YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yytable_value_is_error (yyn)) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the lookahead token. */ - YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - yystate = yyn; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - /* Discard the shifted token. */ - yychar = YYEMPTY; - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 2: /* start: document */ -#line 103 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ivar_set(parser, rb_intern("@result"), yyvsp[0]); } -#line 1917 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 3: /* document: definitions_list */ -#line 105 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - VALUE position_source = rb_ary_entry(yyvsp[0], 0); - VALUE line, col; - if (RB_TEST(position_source)) { - line = rb_funcall(position_source, rb_intern("line"), 0); - col = rb_funcall(position_source, rb_intern("col"), 0); - } else { - line = INT2FIX(1); - col = INT2FIX(1); - } - yyval = MAKE_AST_NODE(Document, 3, line, col, yyvsp[0]); - } -#line 1934 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 4: /* definitions_list: definition */ -#line 119 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 1940 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 5: /* definitions_list: definitions_list definition */ -#line 120 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 1946 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 11: /* operation_definition: operation_type operation_name_opt variable_definitions_opt directives_list_opt selection_set */ -#line 132 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(OperationDefinition, 7, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - rb_ary_entry(yyvsp[-4], 3), - (RB_TEST(yyvsp[-3]) ? rb_ary_entry(yyvsp[-3], 3) : Qnil), - yyvsp[-2], - yyvsp[-1], - yyvsp[0] - ); - } -#line 1962 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 12: /* operation_definition: LCURLY selection_list RCURLY */ -#line 143 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(OperationDefinition, 7, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - r_string_query, - Qnil, - GraphQL_Language_Nodes_NONE, - GraphQL_Language_Nodes_NONE, - yyvsp[-1] - ); - } -#line 1978 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 13: /* operation_definition: LCURLY RCURLY */ -#line 154 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(OperationDefinition, 7, - rb_ary_entry(yyvsp[-1], 1), - rb_ary_entry(yyvsp[-1], 2), - r_string_query, - Qnil, - GraphQL_Language_Nodes_NONE, - GraphQL_Language_Nodes_NONE, - GraphQL_Language_Nodes_NONE - ); - } -#line 1994 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 17: /* operation_name_opt: %empty */ -#line 172 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qnil; } -#line 2000 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 19: /* variable_definitions_opt: %empty */ -#line 176 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2006 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 20: /* variable_definitions_opt: LPAREN variable_definitions_list RPAREN */ -#line 177 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2012 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 21: /* variable_definitions_list: variable_definition */ -#line 180 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2018 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 22: /* variable_definitions_list: variable_definitions_list variable_definition */ -#line 181 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2024 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 23: /* variable_definition: VAR_SIGN name COLON type default_value_opt directives_list_opt */ -#line 184 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(VariableDefinition, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-4], 3), - yyvsp[-2], - yyvsp[-1], - yyvsp[0] - ); - } -#line 2039 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 24: /* default_value_opt: %empty */ -#line 196 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qnil; } -#line 2045 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 25: /* default_value_opt: EQUALS literal_value */ -#line 197 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[0]; } -#line 2051 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 26: /* selection_list: selection */ -#line 200 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2057 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 27: /* selection_list: selection_list selection */ -#line 201 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2063 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 31: /* selection_set: LCURLY selection_list RCURLY */ -#line 209 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2069 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 32: /* selection_set_opt: %empty */ -#line 212 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new(); } -#line 2075 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 34: /* field: name COLON name arguments_opt directives_list_opt selection_set_opt */ -#line 216 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Field, 7, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-5], 3), // alias - rb_ary_entry(yyvsp[-3], 3), // name - yyvsp[-2], // args - yyvsp[-1], // directives - yyvsp[0] // subselections - ); - } -#line 2091 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 35: /* field: name arguments_opt directives_list_opt selection_set_opt */ -#line 227 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Field, 7, - rb_ary_entry(yyvsp[-3], 1), - rb_ary_entry(yyvsp[-3], 2), - Qnil, // alias - rb_ary_entry(yyvsp[-3], 3), // name - yyvsp[-2], // args - yyvsp[-1], // directives - yyvsp[0] // subselections - ); - } -#line 2107 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 36: /* arguments_opt: %empty */ -#line 240 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2113 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 37: /* arguments_opt: LPAREN arguments_list RPAREN */ -#line 241 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2119 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 38: /* arguments_list: argument */ -#line 244 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2125 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 39: /* arguments_list: arguments_list argument */ -#line 245 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2131 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 40: /* argument: name COLON input_value */ -#line 248 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Argument, 4, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - rb_ary_entry(yyvsp[-2], 3), - yyvsp[0] - ); - } -#line 2144 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 41: /* literal_value: FLOAT */ -#line 258 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_funcall(rb_ary_entry(yyvsp[0], 3), rb_intern("to_f"), 0); } -#line 2150 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 42: /* literal_value: INT */ -#line 259 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_funcall(rb_ary_entry(yyvsp[0], 3), rb_intern("to_i"), 0); } -#line 2156 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 43: /* literal_value: STRING */ -#line 260 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_entry(yyvsp[0], 3); } -#line 2162 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 44: /* literal_value: TRUE_LITERAL */ -#line 261 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qtrue; } -#line 2168 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 45: /* literal_value: FALSE_LITERAL */ -#line 262 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qfalse; } -#line 2174 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 53: /* null_value: NULL_LITERAL */ -#line 273 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(NullValue, 3, - rb_ary_entry(yyvsp[0], 1), - rb_ary_entry(yyvsp[0], 2), - rb_ary_entry(yyvsp[0], 3) - ); - } -#line 2186 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 54: /* variable: VAR_SIGN name */ -#line 281 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(VariableIdentifier, 3, - rb_ary_entry(yyvsp[-1], 1), - rb_ary_entry(yyvsp[-1], 2), - rb_ary_entry(yyvsp[0], 3) - ); - } -#line 2198 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 55: /* list_value: LBRACKET RBRACKET */ -#line 290 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2204 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 56: /* list_value: LBRACKET list_value_list RBRACKET */ -#line 291 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2210 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 57: /* list_value_list: input_value */ -#line 294 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2216 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 58: /* list_value_list: list_value_list input_value */ -#line 295 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2222 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 63: /* enum_value: enum_name */ -#line 303 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Enum, 3, - rb_ary_entry(yyvsp[0], 1), - rb_ary_entry(yyvsp[0], 2), - rb_ary_entry(yyvsp[0], 3) - ); - } -#line 2234 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 64: /* object_value: LCURLY object_value_list_opt RCURLY */ -#line 312 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InputObject, 3, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - yyvsp[-1] - ); - } -#line 2246 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 65: /* object_value_list_opt: %empty */ -#line 321 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2252 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 67: /* object_value_list: object_value_field */ -#line 325 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2258 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 68: /* object_value_list: object_value_list object_value_field */ -#line 326 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2264 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 69: /* object_value_field: name COLON input_value */ -#line 329 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Argument, 4, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - rb_ary_entry(yyvsp[-2], 3), - yyvsp[0] - ); - } -#line 2277 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 70: /* object_literal_value: LCURLY object_literal_value_list_opt RCURLY */ -#line 340 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InputObject, 3, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - yyvsp[-1] - ); - } -#line 2289 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 71: /* object_literal_value_list_opt: %empty */ -#line 349 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2295 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 73: /* object_literal_value_list: object_literal_value_field */ -#line 353 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2301 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 74: /* object_literal_value_list: object_literal_value_list object_literal_value_field */ -#line 354 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2307 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 75: /* object_literal_value_field: name COLON literal_value */ -#line 357 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Argument, 4, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - rb_ary_entry(yyvsp[-2], 3), - yyvsp[0] - ); - } -#line 2320 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 76: /* directives_list_opt: %empty */ -#line 368 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2326 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 78: /* directives_list: directive */ -#line 372 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2332 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 79: /* directives_list: directives_list directive */ -#line 373 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2338 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 80: /* directive: DIR_SIGN name arguments_opt */ -#line 375 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(Directive, 4, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - rb_ary_entry(yyvsp[-1], 3), - yyvsp[0] - ); - } -#line 2351 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 101: /* fragment_spread: ELLIPSIS name_without_on directives_list_opt */ -#line 412 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(FragmentSpread, 4, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - rb_ary_entry(yyvsp[-1], 3), - yyvsp[0] - ); - } -#line 2364 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 102: /* inline_fragment: ELLIPSIS ON type directives_list_opt selection_set */ -#line 422 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InlineFragment, 5, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - yyvsp[-2], - yyvsp[-1], - yyvsp[0] - ); - } -#line 2378 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 103: /* inline_fragment: ELLIPSIS directives_list_opt selection_set */ -#line 431 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InlineFragment, 5, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - Qnil, - yyvsp[-1], - yyvsp[0] - ); - } -#line 2392 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 104: /* fragment_definition: FRAGMENT fragment_name_opt ON type directives_list_opt selection_set */ -#line 442 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(FragmentDefinition, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - yyvsp[-4], - yyvsp[-2], - yyvsp[-1], - yyvsp[0] - ); - } -#line 2407 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 105: /* fragment_name_opt: %empty */ -#line 454 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qnil; } -#line 2413 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 106: /* fragment_name_opt: name_without_on */ -#line 455 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_entry(yyvsp[0], 3); } -#line 2419 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 108: /* type: nullable_type BANG */ -#line 459 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = MAKE_AST_NODE(NonNullType, 3, rb_funcall(yyvsp[-1], rb_intern("line"), 0), rb_funcall(yyvsp[-1], rb_intern("col"), 0), yyvsp[-1]); } -#line 2425 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 109: /* nullable_type: name */ -#line 462 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry(yyvsp[0], 1), - rb_ary_entry(yyvsp[0], 2), - rb_ary_entry(yyvsp[0], 3) - ); - } -#line 2437 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 110: /* nullable_type: LBRACKET type RBRACKET */ -#line 469 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(ListType, 3, - rb_funcall(yyvsp[-1], rb_intern("line"), 0), - rb_funcall(yyvsp[-1], rb_intern("col"), 0), - yyvsp[-1] - ); - } -#line 2449 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 114: /* schema_definition: SCHEMA directives_list_opt operation_type_definition_list_opt */ -#line 483 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(SchemaDefinition, 6, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - // TODO use static strings: - rb_hash_aref(yyvsp[0], rb_str_new_cstr("query")), - rb_hash_aref(yyvsp[0], rb_str_new_cstr("mutation")), - rb_hash_aref(yyvsp[0], rb_str_new_cstr("subscription")), - yyvsp[-1] - ); - } -#line 2465 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 115: /* operation_type_definition_list_opt: %empty */ -#line 496 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_hash_new(); } -#line 2471 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 116: /* operation_type_definition_list_opt: LCURLY operation_type_definition_list RCURLY */ -#line 497 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2477 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 117: /* operation_type_definition_list: operation_type_definition */ -#line 500 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = rb_hash_new(); - rb_hash_aset(yyval, rb_ary_entry(yyvsp[0], 0), rb_ary_entry(yyvsp[0], 1)); - } -#line 2486 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 118: /* operation_type_definition_list: operation_type_definition_list operation_type_definition */ -#line 504 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - rb_hash_aset(yyval, rb_ary_entry(yyvsp[0], 0), rb_ary_entry(yyvsp[0], 1)); - } -#line 2494 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 119: /* operation_type_definition: operation_type COLON name */ -#line 509 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = rb_ary_new_from_args(2, rb_ary_entry(yyvsp[-2], 3), rb_ary_entry(yyvsp[0], 3)); - } -#line 2502 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 127: /* description_opt: %empty */ -#line 524 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qnil; } -#line 2508 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 129: /* scalar_type_definition: description_opt SCALAR name directives_list_opt */ -#line 528 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(ScalarTypeDefinition, 5, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - rb_ary_entry(yyvsp[-1], 3), - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-3]) ? rb_ary_entry(yyvsp[-3], 3) : Qnil), - yyvsp[0] - ); - } -#line 2523 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 130: /* object_type_definition: description_opt TYPE_LITERAL name implements_opt directives_list_opt field_definition_list_opt */ -#line 540 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(ObjectTypeDefinition, 7, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - rb_ary_entry(yyvsp[-3], 3), - yyvsp[-2], // implements - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-5]) ? rb_ary_entry(yyvsp[-5], 3) : Qnil), - yyvsp[-1], - yyvsp[0] - ); - } -#line 2540 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 131: /* implements_opt: %empty */ -#line 554 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2546 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 132: /* implements_opt: IMPLEMENTS AMP interfaces_list */ -#line 555 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[0]; } -#line 2552 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 133: /* implements_opt: IMPLEMENTS interfaces_list */ -#line 556 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[0]; } -#line 2558 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 134: /* implements_opt: IMPLEMENTS legacy_interfaces_list */ -#line 557 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[0]; } -#line 2564 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 135: /* interfaces_list: name */ -#line 560 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - VALUE new_name = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry(yyvsp[0], 1), - rb_ary_entry(yyvsp[0], 2), - rb_ary_entry(yyvsp[0], 3) - ); - yyval = rb_ary_new_from_args(1, new_name); - } -#line 2577 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 136: /* interfaces_list: interfaces_list AMP name */ -#line 568 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - VALUE new_name = MAKE_AST_NODE(TypeName, 3, rb_ary_entry(yyvsp[0], 1), rb_ary_entry(yyvsp[0], 2), rb_ary_entry(yyvsp[0], 3)); - rb_ary_push(yyval, new_name); - } -#line 2586 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 137: /* legacy_interfaces_list: name */ -#line 574 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - VALUE new_name = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry(yyvsp[0], 1), - rb_ary_entry(yyvsp[0], 2), - rb_ary_entry(yyvsp[0], 3) - ); - yyval = rb_ary_new_from_args(1, new_name); - } -#line 2599 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 138: /* legacy_interfaces_list: legacy_interfaces_list name */ -#line 582 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - rb_ary_push(yyval, MAKE_AST_NODE(TypeName, 3, rb_ary_entry(yyvsp[0], 1), rb_ary_entry(yyvsp[0], 2), rb_ary_entry(yyvsp[0], 3))); - } -#line 2607 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 139: /* input_value_definition: description_opt name COLON type default_value_opt directives_list_opt */ -#line 587 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InputValueDefinition, 7, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - rb_ary_entry(yyvsp[-4], 3), - yyvsp[-2], - yyvsp[-1], - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-5]) ? rb_ary_entry(yyvsp[-5], 3) : Qnil), - yyvsp[0] - ); - } -#line 2624 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 140: /* input_value_definition_list: input_value_definition */ -#line 601 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2630 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 141: /* input_value_definition_list: input_value_definition_list input_value_definition */ -#line 602 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2636 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 142: /* arguments_definitions_opt: %empty */ -#line 605 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2642 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 143: /* arguments_definitions_opt: LPAREN input_value_definition_list RPAREN */ -#line 606 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2648 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 144: /* field_definition: description_opt name arguments_definitions_opt COLON type directives_list_opt */ -#line 609 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(FieldDefinition, 7, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - rb_ary_entry(yyvsp[-4], 3), - yyvsp[-1], - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-5]) ? rb_ary_entry(yyvsp[-5], 3) : Qnil), - yyvsp[-3], - yyvsp[0] - ); - } -#line 2665 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 145: /* field_definition_list_opt: %empty */ -#line 623 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2671 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 146: /* field_definition_list_opt: LCURLY field_definition_list RCURLY */ -#line 624 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = yyvsp[-1]; } -#line 2677 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 147: /* field_definition_list: %empty */ -#line 627 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2683 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 148: /* field_definition_list: field_definition */ -#line 628 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2689 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 149: /* field_definition_list: field_definition_list field_definition */ -#line 629 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2695 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 150: /* interface_type_definition: description_opt INTERFACE name implements_opt directives_list_opt field_definition_list_opt */ -#line 632 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InterfaceTypeDefinition, 7, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - rb_ary_entry(yyvsp[-3], 3), - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-5]) ? rb_ary_entry(yyvsp[-5], 3) : Qnil), - yyvsp[-2], - yyvsp[-1], - yyvsp[0] - ); - } -#line 2712 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 151: /* pipe_opt: %empty */ -#line 646 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2718 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 152: /* pipe_opt: PIPE */ -#line 647 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = GraphQL_Language_Nodes_NONE; } -#line 2724 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 153: /* union_members: pipe_opt name */ -#line 650 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - VALUE new_member = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry(yyvsp[0], 1), - rb_ary_entry(yyvsp[0], 2), - rb_ary_entry(yyvsp[0], 3) - ); - yyval = rb_ary_new_from_args(1, new_member); - } -#line 2737 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 154: /* union_members: union_members PIPE name */ -#line 658 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - rb_ary_push(yyval, MAKE_AST_NODE(TypeName, 3, rb_ary_entry(yyvsp[0], 1), rb_ary_entry(yyvsp[0], 2), rb_ary_entry(yyvsp[0], 3))); - } -#line 2745 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 155: /* union_type_definition: description_opt UNION name directives_list_opt EQUALS union_members */ -#line 663 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(UnionTypeDefinition, 6, - rb_ary_entry(yyvsp[-4], 1), - rb_ary_entry(yyvsp[-4], 2), - rb_ary_entry(yyvsp[-3], 3), - yyvsp[0], // types - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-5]) ? rb_ary_entry(yyvsp[-5], 3) : Qnil), - yyvsp[-2] - ); - } -#line 2761 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 156: /* enum_type_definition: description_opt ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY */ -#line 676 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(EnumTypeDefinition, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-4], 3), - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-6]) ? rb_ary_entry(yyvsp[-6], 3) : Qnil), - yyvsp[-3], - yyvsp[-1] - ); - } -#line 2777 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 157: /* enum_value_definition: description_opt enum_name directives_list_opt */ -#line 689 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(EnumValueDefinition, 5, - rb_ary_entry(yyvsp[-1], 1), - rb_ary_entry(yyvsp[-1], 2), - rb_ary_entry(yyvsp[-1], 3), - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-2]) ? rb_ary_entry(yyvsp[-2], 3) : Qnil), - yyvsp[0] - ); - } -#line 2792 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 158: /* enum_value_definitions: enum_value_definition */ -#line 701 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, yyvsp[0]); } -#line 2798 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 159: /* enum_value_definitions: enum_value_definitions enum_value_definition */ -#line 702 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, yyvsp[0]); } -#line 2804 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 160: /* input_object_type_definition: description_opt INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY */ -#line 705 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InputObjectTypeDefinition, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-4], 3), - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-6]) ? rb_ary_entry(yyvsp[-6], 3) : Qnil), - yyvsp[-3], - yyvsp[-1] - ); - } -#line 2820 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 161: /* directive_definition: description_opt DIRECTIVE DIR_SIGN name arguments_definitions_opt directive_repeatable_opt ON directive_locations */ -#line 718 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(DirectiveDefinition, 7, - rb_ary_entry(yyvsp[-6], 1), - rb_ary_entry(yyvsp[-6], 2), - rb_ary_entry(yyvsp[-4], 3), - (RB_TEST(yyvsp[-2]) ? Qtrue : Qfalse), // repeatable - // TODO see get_description for reading a description from comments - (RB_TEST(yyvsp[-7]) ? rb_ary_entry(yyvsp[-7], 3) : Qnil), - yyvsp[-3], - yyvsp[0] - ); - } -#line 2837 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 162: /* directive_repeatable_opt: %empty */ -#line 732 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qnil; } -#line 2843 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 163: /* directive_repeatable_opt: REPEATABLE */ -#line 733 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = Qtrue; } -#line 2849 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 164: /* directive_locations: name */ -#line 736 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { yyval = rb_ary_new_from_args(1, MAKE_AST_NODE(DirectiveLocation, 3, rb_ary_entry(yyvsp[0], 1), rb_ary_entry(yyvsp[0], 2), rb_ary_entry(yyvsp[0], 3))); } -#line 2855 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 165: /* directive_locations: directive_locations PIPE name */ -#line 737 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { rb_ary_push(yyval, MAKE_AST_NODE(DirectiveLocation, 3, rb_ary_entry(yyvsp[0], 1), rb_ary_entry(yyvsp[0], 2), rb_ary_entry(yyvsp[0], 3))); } -#line 2861 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 168: /* schema_extension: EXTEND SCHEMA directives_list_opt LCURLY operation_type_definition_list RCURLY */ -#line 745 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(SchemaExtension, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - // TODO use static strings: - rb_hash_aref(yyvsp[-1], rb_str_new_cstr("query")), - rb_hash_aref(yyvsp[-1], rb_str_new_cstr("mutation")), - rb_hash_aref(yyvsp[-1], rb_str_new_cstr("subscription")), - yyvsp[-3] - ); - } -#line 2877 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 169: /* schema_extension: EXTEND SCHEMA directives_list */ -#line 756 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(SchemaExtension, 6, - rb_ary_entry(yyvsp[-2], 1), - rb_ary_entry(yyvsp[-2], 2), - Qnil, - Qnil, - Qnil, - yyvsp[0] - ); - } -#line 2892 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 176: /* scalar_type_extension: EXTEND SCALAR name directives_list */ -#line 775 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(ScalarTypeExtension, 4, - rb_ary_entry(yyvsp[-3], 1), - rb_ary_entry(yyvsp[-3], 2), - rb_ary_entry(yyvsp[-1], 3), - yyvsp[0] - ); - } -#line 2905 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 177: /* object_type_extension: EXTEND TYPE_LITERAL name implements_opt directives_list_opt field_definition_list_opt */ -#line 785 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(ObjectTypeExtension, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-3], 3), - yyvsp[-2], // implements - yyvsp[-1], - yyvsp[0] - ); - } -#line 2920 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 178: /* interface_type_extension: EXTEND INTERFACE name implements_opt directives_list_opt field_definition_list_opt */ -#line 797 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InterfaceTypeExtension, 6, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-3], 3), - yyvsp[-2], - yyvsp[-1], - yyvsp[0] - ); - } -#line 2935 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 179: /* union_type_extension: EXTEND UNION name directives_list_opt EQUALS union_members */ -#line 809 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(UnionTypeExtension, 5, - rb_ary_entry(yyvsp[-5], 1), - rb_ary_entry(yyvsp[-5], 2), - rb_ary_entry(yyvsp[-3], 3), - yyvsp[0], // types - yyvsp[-2] - ); - } -#line 2949 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 180: /* union_type_extension: EXTEND UNION name directives_list */ -#line 818 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(UnionTypeExtension, 5, - rb_ary_entry(yyvsp[-3], 1), - rb_ary_entry(yyvsp[-3], 2), - rb_ary_entry(yyvsp[-1], 3), - GraphQL_Language_Nodes_NONE, // types - yyvsp[0] - ); - } -#line 2963 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 181: /* enum_type_extension: EXTEND ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY */ -#line 829 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(EnumTypeExtension, 5, - rb_ary_entry(yyvsp[-6], 1), - rb_ary_entry(yyvsp[-6], 2), - rb_ary_entry(yyvsp[-4], 3), - yyvsp[-3], - yyvsp[-1] - ); - } -#line 2977 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 182: /* enum_type_extension: EXTEND ENUM name directives_list */ -#line 838 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(EnumTypeExtension, 5, - rb_ary_entry(yyvsp[-3], 1), - rb_ary_entry(yyvsp[-3], 2), - rb_ary_entry(yyvsp[-1], 3), - yyvsp[0], - GraphQL_Language_Nodes_NONE - ); - } -#line 2991 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 183: /* input_object_type_extension: EXTEND INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY */ -#line 849 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InputObjectTypeExtension, 5, - rb_ary_entry(yyvsp[-6], 1), - rb_ary_entry(yyvsp[-6], 2), - rb_ary_entry(yyvsp[-4], 3), - yyvsp[-3], - yyvsp[-1] - ); - } -#line 3005 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - case 184: /* input_object_type_extension: EXTEND INPUT name directives_list */ -#line 858 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - { - yyval = MAKE_AST_NODE(InputObjectTypeExtension, 5, - rb_ary_entry(yyvsp[-3], 1), - rb_ary_entry(yyvsp[-3], 2), - rb_ary_entry(yyvsp[-1], 3), - yyvsp[0], - GraphQL_Language_Nodes_NONE - ); - } -#line 3019 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - break; - - -#line 3023 "graphql-c_parser/ext/graphql_c_parser_ext/parser.c" - - default: break; - } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); - - YYPOPSTACK (yylen); - yylen = 0; - - *++yyvsp = yyval; - - /* Now 'shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - { - const int yylhs = yyr1[yyn] - YYNTOKENS; - const int yyi = yypgoto[yylhs] + *yyssp; - yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp - ? yytable[yyi] - : yydefgoto[yylhs]); - } - - goto yynewstate; - - -/*--------------------------------------. -| yyerrlab -- here on detecting error. | -`--------------------------------------*/ -yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; - { - yypcontext_t yyctx - = {yyssp, yytoken}; - char const *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == -1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = YY_CAST (char *, - YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); - if (yymsg) - { - yysyntax_error_status - = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); - yymsgp = yymsg; - } - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = YYENOMEM; - } - } - yyerror (parser, filename, yymsgp); - if (yysyntax_error_status == YYENOMEM) - YYNOMEM; - } - } - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } - else - { - yydestruct ("Error: discarding", - yytoken, &yylval, parser, filename); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - /* Pacify compilers when the user code never invokes YYERROR and the - label yyerrorlab therefore never appears in user code. */ - if (0) - YYERROR; - ++yynerrs; - - /* Do not reclaim the symbols of the rule whose action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - /* Pop stack until we find a state that shifts the error token. */ - for (;;) - { - yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) - { - yyn += YYSYMBOL_YYerror; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - - yydestruct ("Error: popping", - YY_ACCESSING_SYMBOL (yystate), yyvsp, parser, filename); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturnlab; - - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturnlab; - - -/*-----------------------------------------------------------. -| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | -`-----------------------------------------------------------*/ -yyexhaustedlab: - yyerror (parser, filename, YY_("memory exhausted")); - yyresult = 2; - goto yyreturnlab; - - -/*----------------------------------------------------------. -| yyreturnlab -- parsing is finished, clean up and return. | -`----------------------------------------------------------*/ -yyreturnlab: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval, parser, filename); - } - /* Do not reclaim the symbols of the rule whose action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", - YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, parser, filename); - YYPOPSTACK (1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - return yyresult; -} - -#line 868 "graphql-c_parser/ext/graphql_c_parser_ext/parser.y" - - -// Custom functions -int yylex (YYSTYPE *lvalp, VALUE parser, VALUE filename) { - VALUE next_token_idx_rb_int = rb_ivar_get(parser, rb_intern("@next_token_index")); - int next_token_idx = FIX2INT(next_token_idx_rb_int); - VALUE tokens = rb_ivar_get(parser, rb_intern("@tokens")); - VALUE next_token = rb_ary_entry(tokens, next_token_idx); - - if (!RB_TEST(next_token)) { - return YYEOF; - } - rb_ivar_set(parser, rb_intern("@next_token_index"), INT2FIX(next_token_idx + 1)); - VALUE token_type_rb_int = rb_ary_entry(next_token, 4); - int next_token_type = FIX2INT(token_type_rb_int); - if (next_token_type == 241) { // BAD_UNICODE_ESCAPE - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mCParser = rb_const_get_at(mGraphQL, rb_intern("CParser")); - VALUE bad_unicode_error = rb_funcall( - mCParser, rb_intern("prepare_bad_unicode_error"), 1, - parser - ); - rb_exc_raise(bad_unicode_error); - } - *lvalp = next_token; - return next_token_type; -} - -void yyerror(VALUE parser, VALUE filename, const char *msg) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mCParser = rb_const_get_at(mGraphQL, rb_intern("CParser")); - VALUE rb_message = rb_str_new_cstr(msg); - VALUE exception = rb_funcall( - mCParser, rb_intern("prepare_parse_error"), 2, - rb_message, - parser - ); - rb_exc_raise(exception); -} - -#define INITIALIZE_NODE_CLASS_VARIABLE(node_class_name) \ - rb_global_variable(&GraphQL_Language_Nodes_##node_class_name); \ - GraphQL_Language_Nodes_##node_class_name = rb_const_get_at(mGraphQLLanguageNodes, rb_intern(#node_class_name)); - -void initialize_node_class_variables() { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mGraphQLLanguage = rb_const_get_at(mGraphQL, rb_intern("Language")); - VALUE mGraphQLLanguageNodes = rb_const_get_at(mGraphQLLanguage, rb_intern("Nodes")); - - rb_global_variable(&GraphQL_Language_Nodes_NONE); - GraphQL_Language_Nodes_NONE = rb_ary_new(); - rb_ary_freeze(GraphQL_Language_Nodes_NONE); - - rb_global_variable(&r_string_query); - r_string_query = rb_str_new_cstr("query"); - rb_str_freeze(r_string_query); - - INITIALIZE_NODE_CLASS_VARIABLE(Argument) - INITIALIZE_NODE_CLASS_VARIABLE(Directive) - INITIALIZE_NODE_CLASS_VARIABLE(Document) - INITIALIZE_NODE_CLASS_VARIABLE(Enum) - INITIALIZE_NODE_CLASS_VARIABLE(Field) - INITIALIZE_NODE_CLASS_VARIABLE(FragmentDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(FragmentSpread) - INITIALIZE_NODE_CLASS_VARIABLE(InlineFragment) - INITIALIZE_NODE_CLASS_VARIABLE(InputObject) - INITIALIZE_NODE_CLASS_VARIABLE(ListType) - INITIALIZE_NODE_CLASS_VARIABLE(NonNullType) - INITIALIZE_NODE_CLASS_VARIABLE(NullValue) - INITIALIZE_NODE_CLASS_VARIABLE(OperationDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(TypeName) - INITIALIZE_NODE_CLASS_VARIABLE(VariableDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(VariableIdentifier) - - INITIALIZE_NODE_CLASS_VARIABLE(ScalarTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(ObjectTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(InterfaceTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(UnionTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(EnumTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(InputObjectTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(EnumValueDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(DirectiveDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(DirectiveLocation) - INITIALIZE_NODE_CLASS_VARIABLE(FieldDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(InputValueDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(SchemaDefinition) - - INITIALIZE_NODE_CLASS_VARIABLE(ScalarTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(ObjectTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(InterfaceTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(UnionTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(EnumTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(InputObjectTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(SchemaExtension) -} diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.h b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.h deleted file mode 100644 index 1e76a505b9a..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef Graphql_parser_h -#define Graphql_parser_h -int yyparse(VALUE parser, VALUE filename); -void initialize_node_class_variables(); -#endif diff --git a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.y b/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.y deleted file mode 100644 index 074b30b9233..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/ext/graphql_c_parser_ext/parser.y +++ /dev/null @@ -1,962 +0,0 @@ -%require "3.8" -%define api.pure full -%define parse.error detailed - -%{ -// C Declarations -#include -#define YYSTYPE VALUE -int yylex(YYSTYPE *, VALUE, VALUE); -void yyerror(VALUE, VALUE, const char*); - -static VALUE GraphQL_Language_Nodes_NONE; -static VALUE r_string_query; - -#define MAKE_AST_NODE(node_class_name, nargs, ...) rb_funcall(GraphQL_Language_Nodes_##node_class_name, rb_intern("from_a"), nargs + 1, filename,__VA_ARGS__) - -#define SETUP_NODE_CLASS_VARIABLE(node_class_name) static VALUE GraphQL_Language_Nodes_##node_class_name; - -SETUP_NODE_CLASS_VARIABLE(Argument) -SETUP_NODE_CLASS_VARIABLE(Directive) -SETUP_NODE_CLASS_VARIABLE(Document) -SETUP_NODE_CLASS_VARIABLE(Enum) -SETUP_NODE_CLASS_VARIABLE(Field) -SETUP_NODE_CLASS_VARIABLE(FragmentDefinition) -SETUP_NODE_CLASS_VARIABLE(FragmentSpread) -SETUP_NODE_CLASS_VARIABLE(InlineFragment) -SETUP_NODE_CLASS_VARIABLE(InputObject) -SETUP_NODE_CLASS_VARIABLE(ListType) -SETUP_NODE_CLASS_VARIABLE(NonNullType) -SETUP_NODE_CLASS_VARIABLE(NullValue) -SETUP_NODE_CLASS_VARIABLE(OperationDefinition) -SETUP_NODE_CLASS_VARIABLE(TypeName) -SETUP_NODE_CLASS_VARIABLE(VariableDefinition) -SETUP_NODE_CLASS_VARIABLE(VariableIdentifier) - -SETUP_NODE_CLASS_VARIABLE(ScalarTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(ObjectTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(InterfaceTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(UnionTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(EnumTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(InputObjectTypeDefinition) -SETUP_NODE_CLASS_VARIABLE(EnumValueDefinition) -SETUP_NODE_CLASS_VARIABLE(DirectiveDefinition) -SETUP_NODE_CLASS_VARIABLE(DirectiveLocation) -SETUP_NODE_CLASS_VARIABLE(FieldDefinition) -SETUP_NODE_CLASS_VARIABLE(InputValueDefinition) -SETUP_NODE_CLASS_VARIABLE(SchemaDefinition) - -SETUP_NODE_CLASS_VARIABLE(ScalarTypeExtension) -SETUP_NODE_CLASS_VARIABLE(ObjectTypeExtension) -SETUP_NODE_CLASS_VARIABLE(InterfaceTypeExtension) -SETUP_NODE_CLASS_VARIABLE(UnionTypeExtension) -SETUP_NODE_CLASS_VARIABLE(EnumTypeExtension) -SETUP_NODE_CLASS_VARIABLE(InputObjectTypeExtension) -SETUP_NODE_CLASS_VARIABLE(SchemaExtension) -%} - -%param {VALUE parser} -%param {VALUE filename} - -// YACC Declarations -%token AMP 200 -%token BANG 201 -%token COLON 202 -%token DIRECTIVE 203 -%token DIR_SIGN 204 -%token ENUM 205 -%token ELLIPSIS 206 -%token EQUALS 207 -%token EXTEND 208 -%token FALSE_LITERAL 209 -%token FLOAT 210 -%token FRAGMENT 211 -%token IDENTIFIER 212 -%token INPUT 213 -%token IMPLEMENTS 214 -%token INT 215 -%token INTERFACE 216 -%token LBRACKET 217 -%token LCURLY 218 -%token LPAREN 219 -%token MUTATION 220 -%token NULL_LITERAL 221 -%token ON 222 -%token PIPE 223 -%token QUERY 224 -%token RBRACKET 225 -%token RCURLY 226 -%token REPEATABLE 227 -%token RPAREN 228 -%token SCALAR 229 -%token SCHEMA 230 -%token STRING 231 -%token SUBSCRIPTION 232 -%token TRUE_LITERAL 233 -%token TYPE_LITERAL 234 -%token UNION 235 -%token VAR_SIGN 236 - -%% - - // YACC Rules - start: document { rb_ivar_set(parser, rb_intern("@result"), $1); } - - document: definitions_list { - VALUE position_source = rb_ary_entry($1, 0); - VALUE line, col; - if (RB_TEST(position_source)) { - line = rb_funcall(position_source, rb_intern("line"), 0); - col = rb_funcall(position_source, rb_intern("col"), 0); - } else { - line = INT2FIX(1); - col = INT2FIX(1); - } - $$ = MAKE_AST_NODE(Document, 3, line, col, $1); - } - - definitions_list: - definition { $$ = rb_ary_new_from_args(1, $1); } - | definitions_list definition { rb_ary_push($$, $2); } - - definition: - executable_definition - | type_system_definition - | type_system_extension - - executable_definition: - operation_definition - | fragment_definition - - operation_definition: - operation_type operation_name_opt variable_definitions_opt directives_list_opt selection_set { - $$ = MAKE_AST_NODE(OperationDefinition, 7, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3), - (RB_TEST($2) ? rb_ary_entry($2, 3) : Qnil), - $3, - $4, - $5 - ); - } - | LCURLY selection_list RCURLY { - $$ = MAKE_AST_NODE(OperationDefinition, 7, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - r_string_query, - Qnil, - GraphQL_Language_Nodes_NONE, - GraphQL_Language_Nodes_NONE, - $2 - ); - } - | LCURLY RCURLY { - $$ = MAKE_AST_NODE(OperationDefinition, 7, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - r_string_query, - Qnil, - GraphQL_Language_Nodes_NONE, - GraphQL_Language_Nodes_NONE, - GraphQL_Language_Nodes_NONE - ); - } - - operation_type: - QUERY - | MUTATION - | SUBSCRIPTION - - operation_name_opt: - /* none */ { $$ = Qnil; } - | name - - variable_definitions_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | LPAREN variable_definitions_list RPAREN { $$ = $2; } - - variable_definitions_list: - variable_definition { $$ = rb_ary_new_from_args(1, $1); } - | variable_definitions_list variable_definition { rb_ary_push($$, $2); } - - variable_definition: - VAR_SIGN name COLON type default_value_opt directives_list_opt { - $$ = MAKE_AST_NODE(VariableDefinition, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($2, 3), - $4, - $5, - $6 - ); - } - - default_value_opt: - /* none */ { $$ = Qnil; } - | EQUALS literal_value { $$ = $2; } - - selection_list: - selection { $$ = rb_ary_new_from_args(1, $1); } - | selection_list selection { rb_ary_push($$, $2); } - - selection: - field - | fragment_spread - | inline_fragment - - selection_set: - LCURLY selection_list RCURLY { $$ = $2; } - - selection_set_opt: - /* none */ { $$ = rb_ary_new(); } - | selection_set - - field: - name COLON name arguments_opt directives_list_opt selection_set_opt { - $$ = MAKE_AST_NODE(Field, 7, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3), // alias - rb_ary_entry($3, 3), // name - $4, // args - $5, // directives - $6 // subselections - ); - } - | name arguments_opt directives_list_opt selection_set_opt { - $$ = MAKE_AST_NODE(Field, 7, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - Qnil, // alias - rb_ary_entry($1, 3), // name - $2, // args - $3, // directives - $4 // subselections - ); - } - - arguments_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | LPAREN arguments_list RPAREN { $$ = $2; } - - arguments_list: - argument { $$ = rb_ary_new_from_args(1, $1); } - | arguments_list argument { rb_ary_push($$, $2); } - - argument: - name COLON input_value { - $$ = MAKE_AST_NODE(Argument, 4, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3), - $3 - ); - } - - literal_value: - FLOAT { $$ = rb_funcall(rb_ary_entry($1, 3), rb_intern("to_f"), 0); } - | INT { $$ = rb_funcall(rb_ary_entry($1, 3), rb_intern("to_i"), 0); } - | STRING { $$ = rb_ary_entry($1, 3); } - | TRUE_LITERAL { $$ = Qtrue; } - | FALSE_LITERAL { $$ = Qfalse; } - | null_value - | enum_value - | list_value - | object_literal_value - - input_value: - literal_value - | variable - | object_value - - null_value: NULL_LITERAL { - $$ = MAKE_AST_NODE(NullValue, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3) - ); - } - - variable: VAR_SIGN name { - $$ = MAKE_AST_NODE(VariableIdentifier, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($2, 3) - ); - } - - list_value: - LBRACKET RBRACKET { $$ = GraphQL_Language_Nodes_NONE; } - | LBRACKET list_value_list RBRACKET { $$ = $2; } - - list_value_list: - input_value { $$ = rb_ary_new_from_args(1, $1); } - | list_value_list input_value { rb_ary_push($$, $2); } - - enum_name: /* any identifier, but not "true", "false" or "null" */ - IDENTIFIER - | ON - | operation_type - | schema_keyword - - enum_value: enum_name { - $$ = MAKE_AST_NODE(Enum, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3) - ); - } - - object_value: - LCURLY object_value_list_opt RCURLY { - $$ = MAKE_AST_NODE(InputObject, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - $2 - ); - } - - object_value_list_opt: - /* nothing */ { $$ = GraphQL_Language_Nodes_NONE; } - | object_value_list - - object_value_list: - object_value_field { $$ = rb_ary_new_from_args(1, $1); } - | object_value_list object_value_field { rb_ary_push($$, $2); } - - object_value_field: - name COLON input_value { - $$ = MAKE_AST_NODE(Argument, 4, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3), - $3 - ); - } - - /* like the previous, but with literals only: */ - object_literal_value: - LCURLY object_literal_value_list_opt RCURLY { - $$ = MAKE_AST_NODE(InputObject, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - $2 - ); - } - - object_literal_value_list_opt: - /* nothing */ { $$ = GraphQL_Language_Nodes_NONE; } - | object_literal_value_list - - object_literal_value_list: - object_literal_value_field { $$ = rb_ary_new_from_args(1, $1); } - | object_literal_value_list object_literal_value_field { rb_ary_push($$, $2); } - - object_literal_value_field: - name COLON literal_value { - $$ = MAKE_AST_NODE(Argument, 4, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3), - $3 - ); - } - - - directives_list_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | directives_list - - directives_list: - directive { $$ = rb_ary_new_from_args(1, $1); } - | directives_list directive { rb_ary_push($$, $2); } - - directive: DIR_SIGN name arguments_opt { - $$ = MAKE_AST_NODE(Directive, 4, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($2, 3), - $3 - ); - } - - name: - name_without_on - | ON - - schema_keyword: - SCHEMA - | SCALAR - | TYPE_LITERAL - | IMPLEMENTS - | INTERFACE - | UNION - | ENUM - | INPUT - | DIRECTIVE - | EXTEND - | FRAGMENT - | REPEATABLE - - name_without_on: - IDENTIFIER - | TRUE_LITERAL - | FALSE_LITERAL - | NULL_LITERAL - | operation_type - | schema_keyword - - - fragment_spread: - ELLIPSIS name_without_on directives_list_opt { - $$ = MAKE_AST_NODE(FragmentSpread, 4, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($2, 3), - $3 - ); - } - - inline_fragment: - ELLIPSIS ON type directives_list_opt selection_set { - $$ = MAKE_AST_NODE(InlineFragment, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - $3, - $4, - $5 - ); - } - | ELLIPSIS directives_list_opt selection_set { - $$ = MAKE_AST_NODE(InlineFragment, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - Qnil, - $2, - $3 - ); - } - - fragment_definition: - FRAGMENT fragment_name_opt ON type directives_list_opt selection_set { - $$ = MAKE_AST_NODE(FragmentDefinition, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - $2, - $4, - $5, - $6 - ); - } - - fragment_name_opt: - /* none */ { $$ = Qnil; } - | name_without_on { $$ = rb_ary_entry($1, 3); } - - type: - nullable_type - | nullable_type BANG { $$ = MAKE_AST_NODE(NonNullType, 3, rb_funcall($1, rb_intern("line"), 0), rb_funcall($1, rb_intern("col"), 0), $1); } - - nullable_type: - name { - $$ = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3) - ); - } - | LBRACKET type RBRACKET { - $$ = MAKE_AST_NODE(ListType, 3, - rb_funcall($2, rb_intern("line"), 0), - rb_funcall($2, rb_intern("col"), 0), - $2 - ); - } - -type_system_definition: - schema_definition - | type_definition - | directive_definition - - schema_definition: - SCHEMA directives_list_opt operation_type_definition_list_opt { - $$ = MAKE_AST_NODE(SchemaDefinition, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - // TODO use static strings: - rb_hash_aref($3, rb_str_new_cstr("query")), - rb_hash_aref($3, rb_str_new_cstr("mutation")), - rb_hash_aref($3, rb_str_new_cstr("subscription")), - $2 - ); - } - - operation_type_definition_list_opt: - /* none */ { $$ = rb_hash_new(); } - | LCURLY operation_type_definition_list RCURLY { $$ = $2; } - - operation_type_definition_list: - operation_type_definition { - $$ = rb_hash_new(); - rb_hash_aset($$, rb_ary_entry($1, 0), rb_ary_entry($1, 1)); - } - | operation_type_definition_list operation_type_definition { - rb_hash_aset($$, rb_ary_entry($2, 0), rb_ary_entry($2, 1)); - } - - operation_type_definition: - operation_type COLON name { - $$ = rb_ary_new_from_args(2, rb_ary_entry($1, 3), rb_ary_entry($3, 3)); - } - - type_definition: - scalar_type_definition - | object_type_definition - | interface_type_definition - | union_type_definition - | enum_type_definition - | input_object_type_definition - - description: STRING - - description_opt: - /* none */ { $$ = Qnil; } - | description - - scalar_type_definition: - description_opt SCALAR name directives_list_opt { - $$ = MAKE_AST_NODE(ScalarTypeDefinition, 5, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($3, 3), - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $4 - ); - } - - object_type_definition: - description_opt TYPE_LITERAL name implements_opt directives_list_opt field_definition_list_opt { - $$ = MAKE_AST_NODE(ObjectTypeDefinition, 7, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($3, 3), - $4, // implements - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $5, - $6 - ); - } - - implements_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | IMPLEMENTS AMP interfaces_list { $$ = $3; } - | IMPLEMENTS interfaces_list { $$ = $2; } - | IMPLEMENTS legacy_interfaces_list { $$ = $2; } - - interfaces_list: - name { - VALUE new_name = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3) - ); - $$ = rb_ary_new_from_args(1, new_name); - } - | interfaces_list AMP name { - VALUE new_name = MAKE_AST_NODE(TypeName, 3, rb_ary_entry($3, 1), rb_ary_entry($3, 2), rb_ary_entry($3, 3)); - rb_ary_push($$, new_name); - } - - legacy_interfaces_list: - name { - VALUE new_name = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($1, 3) - ); - $$ = rb_ary_new_from_args(1, new_name); - } - | legacy_interfaces_list name { - rb_ary_push($$, MAKE_AST_NODE(TypeName, 3, rb_ary_entry($2, 1), rb_ary_entry($2, 2), rb_ary_entry($2, 3))); - } - - input_value_definition: - description_opt name COLON type default_value_opt directives_list_opt { - $$ = MAKE_AST_NODE(InputValueDefinition, 7, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($2, 3), - $4, - $5, - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $6 - ); - } - - input_value_definition_list: - input_value_definition { $$ = rb_ary_new_from_args(1, $1); } - | input_value_definition_list input_value_definition { rb_ary_push($$, $2); } - - arguments_definitions_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | LPAREN input_value_definition_list RPAREN { $$ = $2; } - - field_definition: - description_opt name arguments_definitions_opt COLON type directives_list_opt { - $$ = MAKE_AST_NODE(FieldDefinition, 7, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($2, 3), - $5, - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $3, - $6 - ); - } - - field_definition_list_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | LCURLY field_definition_list RCURLY { $$ = $2; } - - field_definition_list: - /* none - this is not actually valid but graphql-ruby used to print this */ { $$ = GraphQL_Language_Nodes_NONE; } - | field_definition { $$ = rb_ary_new_from_args(1, $1); } - | field_definition_list field_definition { rb_ary_push($$, $2); } - - interface_type_definition: - description_opt INTERFACE name implements_opt directives_list_opt field_definition_list_opt { - $$ = MAKE_AST_NODE(InterfaceTypeDefinition, 7, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($3, 3), - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $4, - $5, - $6 - ); - } - - pipe_opt: - /* none */ { $$ = GraphQL_Language_Nodes_NONE; } - | PIPE { $$ = GraphQL_Language_Nodes_NONE; } - - union_members: - pipe_opt name { - VALUE new_member = MAKE_AST_NODE(TypeName, 3, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($2, 3) - ); - $$ = rb_ary_new_from_args(1, new_member); - } - | union_members PIPE name { - rb_ary_push($$, MAKE_AST_NODE(TypeName, 3, rb_ary_entry($3, 1), rb_ary_entry($3, 2), rb_ary_entry($3, 3))); - } - - union_type_definition: - description_opt UNION name directives_list_opt EQUALS union_members { - $$ = MAKE_AST_NODE(UnionTypeDefinition, 6, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($3, 3), - $6, // types - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $4 - ); - } - - enum_type_definition: - description_opt ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY { - $$ = MAKE_AST_NODE(EnumTypeDefinition, 6, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($3, 3), - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $4, - $6 - ); - } - - enum_value_definition: - description_opt enum_name directives_list_opt { - $$ = MAKE_AST_NODE(EnumValueDefinition, 5, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($2, 3), - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $3 - ); - } - - enum_value_definitions: - enum_value_definition { $$ = rb_ary_new_from_args(1, $1); } - | enum_value_definitions enum_value_definition { rb_ary_push($$, $2); } - - input_object_type_definition: - description_opt INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY { - $$ = MAKE_AST_NODE(InputObjectTypeDefinition, 6, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($3, 3), - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $4, - $6 - ); - } - - directive_definition: - description_opt DIRECTIVE DIR_SIGN name arguments_definitions_opt directive_repeatable_opt ON directive_locations { - $$ = MAKE_AST_NODE(DirectiveDefinition, 7, - rb_ary_entry($2, 1), - rb_ary_entry($2, 2), - rb_ary_entry($4, 3), - (RB_TEST($6) ? Qtrue : Qfalse), // repeatable - // TODO see get_description for reading a description from comments - (RB_TEST($1) ? rb_ary_entry($1, 3) : Qnil), - $5, - $8 - ); - } - - directive_repeatable_opt: - /* nothing */ { $$ = Qnil; } - | REPEATABLE { $$ = Qtrue; } - - directive_locations: - name { $$ = rb_ary_new_from_args(1, MAKE_AST_NODE(DirectiveLocation, 3, rb_ary_entry($1, 1), rb_ary_entry($1, 2), rb_ary_entry($1, 3))); } - | directive_locations PIPE name { rb_ary_push($$, MAKE_AST_NODE(DirectiveLocation, 3, rb_ary_entry($3, 1), rb_ary_entry($3, 2), rb_ary_entry($3, 3))); } - - - type_system_extension: - schema_extension - | type_extension - - schema_extension: - EXTEND SCHEMA directives_list_opt LCURLY operation_type_definition_list RCURLY { - $$ = MAKE_AST_NODE(SchemaExtension, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - // TODO use static strings: - rb_hash_aref($5, rb_str_new_cstr("query")), - rb_hash_aref($5, rb_str_new_cstr("mutation")), - rb_hash_aref($5, rb_str_new_cstr("subscription")), - $3 - ); - } - | EXTEND SCHEMA directives_list { - $$ = MAKE_AST_NODE(SchemaExtension, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - Qnil, - Qnil, - Qnil, - $3 - ); - } - - type_extension: - scalar_type_extension - | object_type_extension - | interface_type_extension - | union_type_extension - | enum_type_extension - | input_object_type_extension - - scalar_type_extension: EXTEND SCALAR name directives_list { - $$ = MAKE_AST_NODE(ScalarTypeExtension, 4, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4 - ); - } - - object_type_extension: - EXTEND TYPE_LITERAL name implements_opt directives_list_opt field_definition_list_opt { - $$ = MAKE_AST_NODE(ObjectTypeExtension, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4, // implements - $5, - $6 - ); - } - - interface_type_extension: - EXTEND INTERFACE name implements_opt directives_list_opt field_definition_list_opt { - $$ = MAKE_AST_NODE(InterfaceTypeExtension, 6, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4, - $5, - $6 - ); - } - - union_type_extension: - EXTEND UNION name directives_list_opt EQUALS union_members { - $$ = MAKE_AST_NODE(UnionTypeExtension, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $6, // types - $4 - ); - } - | EXTEND UNION name directives_list { - $$ = MAKE_AST_NODE(UnionTypeExtension, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - GraphQL_Language_Nodes_NONE, // types - $4 - ); - } - - enum_type_extension: - EXTEND ENUM name directives_list_opt LCURLY enum_value_definitions RCURLY { - $$ = MAKE_AST_NODE(EnumTypeExtension, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4, - $6 - ); - } - | EXTEND ENUM name directives_list { - $$ = MAKE_AST_NODE(EnumTypeExtension, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4, - GraphQL_Language_Nodes_NONE - ); - } - - input_object_type_extension: - EXTEND INPUT name directives_list_opt LCURLY input_value_definition_list RCURLY { - $$ = MAKE_AST_NODE(InputObjectTypeExtension, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4, - $6 - ); - } - | EXTEND INPUT name directives_list { - $$ = MAKE_AST_NODE(InputObjectTypeExtension, 5, - rb_ary_entry($1, 1), - rb_ary_entry($1, 2), - rb_ary_entry($3, 3), - $4, - GraphQL_Language_Nodes_NONE - ); - } - -%% - -// Custom functions -int yylex (YYSTYPE *lvalp, VALUE parser, VALUE filename) { - VALUE next_token_idx_rb_int = rb_ivar_get(parser, rb_intern("@next_token_index")); - int next_token_idx = FIX2INT(next_token_idx_rb_int); - VALUE tokens = rb_ivar_get(parser, rb_intern("@tokens")); - VALUE next_token = rb_ary_entry(tokens, next_token_idx); - - if (!RB_TEST(next_token)) { - return YYEOF; - } - rb_ivar_set(parser, rb_intern("@next_token_index"), INT2FIX(next_token_idx + 1)); - VALUE token_type_rb_int = rb_ary_entry(next_token, 4); - int next_token_type = FIX2INT(token_type_rb_int); - if (next_token_type == 241) { // BAD_UNICODE_ESCAPE - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mCParser = rb_const_get_at(mGraphQL, rb_intern("CParser")); - VALUE bad_unicode_error = rb_funcall( - mCParser, rb_intern("prepare_bad_unicode_error"), 1, - parser - ); - rb_exc_raise(bad_unicode_error); - } - *lvalp = next_token; - return next_token_type; -} - -void yyerror(VALUE parser, VALUE filename, const char *msg) { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mCParser = rb_const_get_at(mGraphQL, rb_intern("CParser")); - VALUE rb_message = rb_str_new_cstr(msg); - VALUE exception = rb_funcall( - mCParser, rb_intern("prepare_parse_error"), 2, - rb_message, - parser - ); - rb_exc_raise(exception); -} - -#define INITIALIZE_NODE_CLASS_VARIABLE(node_class_name) \ - rb_global_variable(&GraphQL_Language_Nodes_##node_class_name); \ - GraphQL_Language_Nodes_##node_class_name = rb_const_get_at(mGraphQLLanguageNodes, rb_intern(#node_class_name)); - -void initialize_node_class_variables() { - VALUE mGraphQL = rb_const_get_at(rb_cObject, rb_intern("GraphQL")); - VALUE mGraphQLLanguage = rb_const_get_at(mGraphQL, rb_intern("Language")); - VALUE mGraphQLLanguageNodes = rb_const_get_at(mGraphQLLanguage, rb_intern("Nodes")); - - rb_global_variable(&GraphQL_Language_Nodes_NONE); - GraphQL_Language_Nodes_NONE = rb_ary_new(); - rb_ary_freeze(GraphQL_Language_Nodes_NONE); - - rb_global_variable(&r_string_query); - r_string_query = rb_str_new_cstr("query"); - rb_str_freeze(r_string_query); - - INITIALIZE_NODE_CLASS_VARIABLE(Argument) - INITIALIZE_NODE_CLASS_VARIABLE(Directive) - INITIALIZE_NODE_CLASS_VARIABLE(Document) - INITIALIZE_NODE_CLASS_VARIABLE(Enum) - INITIALIZE_NODE_CLASS_VARIABLE(Field) - INITIALIZE_NODE_CLASS_VARIABLE(FragmentDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(FragmentSpread) - INITIALIZE_NODE_CLASS_VARIABLE(InlineFragment) - INITIALIZE_NODE_CLASS_VARIABLE(InputObject) - INITIALIZE_NODE_CLASS_VARIABLE(ListType) - INITIALIZE_NODE_CLASS_VARIABLE(NonNullType) - INITIALIZE_NODE_CLASS_VARIABLE(NullValue) - INITIALIZE_NODE_CLASS_VARIABLE(OperationDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(TypeName) - INITIALIZE_NODE_CLASS_VARIABLE(VariableDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(VariableIdentifier) - - INITIALIZE_NODE_CLASS_VARIABLE(ScalarTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(ObjectTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(InterfaceTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(UnionTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(EnumTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(InputObjectTypeDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(EnumValueDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(DirectiveDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(DirectiveLocation) - INITIALIZE_NODE_CLASS_VARIABLE(FieldDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(InputValueDefinition) - INITIALIZE_NODE_CLASS_VARIABLE(SchemaDefinition) - - INITIALIZE_NODE_CLASS_VARIABLE(ScalarTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(ObjectTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(InterfaceTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(UnionTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(EnumTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(InputObjectTypeExtension) - INITIALIZE_NODE_CLASS_VARIABLE(SchemaExtension) -} diff --git a/vendor/gems/graphql/graphql-c_parser/graphql-c_parser.gemspec b/vendor/gems/graphql/graphql-c_parser/graphql-c_parser.gemspec deleted file mode 100644 index bd9667e9089..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/graphql-c_parser.gemspec +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -$LOAD_PATH.push File.expand_path("../lib", __FILE__) -require "graphql/c_parser/version" -require "date" - -Gem::Specification.new do |s| - s.name = "graphql-c_parser" - s.version = GraphQL::CParser::VERSION - s.date = Date.today.to_s - s.summary = "A parser for GraphQL, implemented as a C extension" - s.homepage = "https://github.com/rmosolgo/graphql-ruby" - s.authors = ["Robert Mosolgo"] - s.email = ["rdmosolgo@gmail.com"] - s.license = "MIT" - s.required_ruby_version = ">= 3.0.0" - s.metadata = { - "homepage_uri" => "https://graphql-ruby.org", - "changelog_uri" => "https://github.com/rmosolgo/graphql-ruby/blob/master/graphql-c_parser/CHANGELOG.md", - "source_code_uri" => "https://github.com/rmosolgo/graphql-ruby", - "bug_tracker_uri" => "https://github.com/rmosolgo/graphql-ruby/issues", - "mailing_list_uri" => "https://buttondown.email/graphql-ruby", - } - - s.files = Dir["{lib,ext}/**/*.{rb,h,c}"] - s.extensions << "ext/graphql_c_parser_ext/extconf.rb" - s.add_dependency "graphql", ">= 2.2.10" -end diff --git a/vendor/gems/graphql/graphql-c_parser/lib/graphql-c_parser.rb b/vendor/gems/graphql/graphql-c_parser/lib/graphql-c_parser.rb deleted file mode 100644 index 791c0bb19a4..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/lib/graphql-c_parser.rb +++ /dev/null @@ -1,2 +0,0 @@ -# frozen_string_literal: true -require "graphql/c_parser" diff --git a/vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser.rb b/vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser.rb deleted file mode 100644 index 3deec946f18..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: true - -require "graphql" -require "graphql/c_parser/version" -require "graphql/graphql_c_parser_ext" - -module GraphQL - module CParser - def self.parse(query_str, filename: nil, trace: GraphQL::Tracing::NullTrace, max_tokens: nil) - Parser.parse(query_str, filename: filename, trace: trace, max_tokens: max_tokens) - end - - def self.parse_file(filename) - contents = File.read(filename) - parse(contents, filename: filename) - end - - def self.tokenize_with_c(str) - reject_numbers_followed_by_names = GraphQL.respond_to?(:reject_numbers_followed_by_names) && GraphQL.reject_numbers_followed_by_names - tokenize_with_c_internal(str, false, reject_numbers_followed_by_names) - end - - def self.prepare_parse_error(message, parser) - query_str = parser.query_string - filename = parser.filename - if message.start_with?("memory exhausted") - return GraphQL::ParseError.new("This query is too large to execute.", nil, nil, query_str, filename: filename) - end - token = parser.tokens[parser.next_token_index - 1] - if token - # There might not be a token if it's a comments-only string - line = token[1] - col = token[2] - if line && col - location_str = " at [#{line}, #{col}]" - if !message.include?(location_str) - message += location_str - end - end - - if !message.include?("end of file") - message.sub!(/, unexpected ([a-zA-Z ]+)(,| at)/, ", unexpected \\1 (#{token[3].inspect})\\2") - end - end - - GraphQL::ParseError.new(message, line, col, query_str, filename: filename) - end - - def self.prepare_number_name_parse_error(line, col, query_str, number_part, name_part) - raise GraphQL::ParseError.new("Name after number is not allowed (in `#{number_part}#{name_part}`)", line, col, query_str) - end - - def self.prepare_bad_unicode_error(parser) - token = parser.tokens[parser.next_token_index - 1] - line = token[1] - col = token[2] - GraphQL::ParseError.new( - "Parse error on bad Unicode escape sequence: #{token[3].inspect} (error) at [#{line}, #{col}]", - line, - col, - parser.query_string, - filename: parser.filename - ) - end - - module Lexer - def self.tokenize(graphql_string, intern_identifiers: false, max_tokens: nil) - if !(graphql_string.encoding == Encoding::UTF_8 || graphql_string.ascii_only?) - graphql_string = graphql_string.dup.force_encoding(Encoding::UTF_8) - end - if !graphql_string.valid_encoding? - return [ - [ - :BAD_UNICODE_ESCAPE, - 1, - 1, - graphql_string, - 241 # BAD_UNICODE_ESCAPE in lexer.rl - ] - ] - end - reject_numbers_followed_by_names = GraphQL.respond_to?(:reject_numbers_followed_by_names) && GraphQL.reject_numbers_followed_by_names - # -1 indicates that there is no limit - lexer_max_tokens = max_tokens.nil? ? -1 : max_tokens - tokenize_with_c_internal(graphql_string, intern_identifiers, reject_numbers_followed_by_names, lexer_max_tokens) - end - end - - class Parser - def self.parse(query_str, filename: nil, trace: GraphQL::Tracing::NullTrace, max_tokens: nil) - self.new(query_str, filename, trace, max_tokens).result - end - - def self.parse_file(filename) - contents = File.read(filename) - parse(contents, filename: filename) - end - - def initialize(query_string, filename, trace, max_tokens) - if query_string.nil? - raise GraphQL::ParseError.new("No query string was present", nil, nil, query_string) - end - @query_string = query_string - @filename = filename - @tokens = nil - @next_token_index = 0 - @result = nil - @trace = trace - @intern_identifiers = false - @max_tokens = max_tokens - end - - def result - if @result.nil? - @tokens = @trace.lex(query_string: @query_string) do - GraphQL::CParser::Lexer.tokenize(@query_string, intern_identifiers: @intern_identifiers, max_tokens: @max_tokens) - end - @trace.parse(query_string: @query_string) do - c_parse - @result - end - end - @result - end - - def tokens_count - result - @tokens.length - end - - attr_reader :tokens, :next_token_index, :query_string, :filename - end - - class SchemaParser < Parser - def initialize(*args) - super - @intern_identifiers = true - end - end - end - - def self.scan_with_c(graphql_string) - GraphQL::CParser::Lexer.tokenize(graphql_string) - end - - def self.parse_with_c(string, filename: nil, trace: GraphQL::Tracing::NullTrace) - if string.nil? - raise GraphQL::ParseError.new("No query string was present", nil, nil, string) - end - document = GraphQL::CParser.parse(string, filename: filename, trace: trace) - if document.definitions.size == 0 - raise GraphQL::ParseError.new("Unexpected end of document", 1, 1, string) - end - document - end - - self.default_parser = GraphQL::CParser -end diff --git a/vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser/version.rb b/vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser/version.rb deleted file mode 100644 index 7c292f45b5c..00000000000 --- a/vendor/gems/graphql/graphql-c_parser/lib/graphql/c_parser/version.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module CParser - VERSION = "1.1.2" - end -end diff --git a/vendor/gems/graphql/graphql-ruby.png b/vendor/gems/graphql/graphql-ruby.png deleted file mode 100644 index c0376eccb9abd2119529536483e9ddf5ca7c52df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4320 zcmV<65FhV}P)X+U09bxX?{jdp;p65|q$#vu`m zNMrz?X{D=Sz$}MBjqwpOiDpG)mRVzbL6W7xEC+?IhH2l3C{8A7aOq@b@WoU$kM=nT z(p3==r0IIxJs-NNt7xmMZapq^&zk?Yb9$e>clE#SIp^-Z&ndxX1jjaMrlZD8g|Uc!BBr^%DZ1^|O6h-@sm?u~$oEY{&lec3Dj%wv zPtA+Azga08xgd5aR*<#chTBx+7bxyNf8K+^;=tbUv*p-W>zPoe;#jBf?Hu-BG+c>! zO}lqQ%d>IXS*BP+GopS1($$d&{A6Z1e})$O+c?mZIE|M{)Y^kfmus`RK%G&BAHCQuH^k)#E>;qIX&_^7}xf zz0l?)CxYIFNgx+lu(7EW{e@8RX|1Jp;v^LwDl92U6S=VzVgQ^&_TK~gUj@Y8Od8H# zmZCp}8-UKjVsb}I?Zk?&I~65oUCn8yifL@>-F^x&Af7|?5MnNz7Y(g1)mG!KU0tZr zo>xdER8Ax!5NY2DG6UGwk4XC6X0({breN))Q%bd!3l)0_UF(@pXNvPR(66Ps5*S=* z#EmyihdED0PZKpn#e7Rm*365x?8lP-G~u(@^8-1X^c^K1pVIc+Z7QX^ezEEyrFO6(Q%b6DJ<;>^5FS z@Ta;I^x8nA{gF!O?+XP9dVFt)a1MY7>zg{mmjJYl8&@4aJfa=pq#kM6vNIC;=29Ep z-~oajUjSh>y|a*PC1#u-*uQmOxw^}q)>hU{9_on6`Yd8lWrQc>4XR<5jZp?t2K+-S6 zwc>EyW~X0)X!uSMvc8l$Jzk@$mX`59E@_~G#J&EN3Rm`a3c-#@=!bcRm5pHSq*DZL z%JSo9jr-o;pG$UbB>WceXvROed7WqCcsu=@+ZkR0l9|qxrh2dcH>K(;iJaCN-@Twe z$m)8ouCr#rQkxdtUL=$E0Yg%)w>l!>NArpsj@v+b$Wz>=VccVI9jXSvm z@BoO)9Zk{DvSRhLczrJjq^GofE=O!a^5h>8 zm0shSFu{TI8b!}UaDawYHU~?eS-d2{vm}4oBDwR@)p{yv+L+hB=8G1)WW&z!K#6}_d%-Rzso(y2L zjqs0Am4C8gK-FPe2&6B9wZ2ObE=)B|fgee6sr|1Yuz%}5M1PU#(er~|-}lSWSqz+J zGD6R^nhzt)q~mq~BUZ(1;5aMF#W3fV>7ONRtgo-a%!EQj^pVsXf7x(#wm%G#Nh>S& zw2d2AT?yTVkPydwEbWl>QUx&twI0$?`AXHJ9r%2I)y=@EJuTUv5LfB7XMNBJNij1a zAcT_=hes~SGo)e=bfQI3&*~E=jxj_f-yGNnYU|4;Kx@ruwTk{M-4Bl@E=8}ugKJr7n-~qNUv=>oKb#jiygzc1W0|^ zw3>urY27UgqWz`2`=a9q(d`F}u@Ubd6!Pcd)4Ij1cvWLIg2(&6sYq{P%uv0hkx-xqiw)1z0ldi_)Eu$6{k$kSHjMD$?*V;Zj{M9q0X!9oaCW5udbXXM+bmkb5L z&=q7B%opZH+n=|?RvJ#^mq5A{SQK5c)TTnqRin-rWttRBA1M0zs1fmBL&}jrblVn? zDL{_%CX$^mIos`yZE72RO3}5)g|N297XZ#pHREDBZpkNK2{cAR2a(MB2AnzG({N3t z?X-dF;OSCDli;}_5D=;($%u{8y0J>Gw|ILxzD4MQ}!^+0sk>%+Z= z*MMHtE2sR~ojg3FzeHEj&U^jC6R=Iu6MLMJi zm}AJMjDAUq=mH~YY)Lqhvkf)Inquxj^o8cV+m5IlwG{Yl14qo`dsKwxtf56AHw~C&hV{e>m5@+LLBZ*WM%{t(S?D| z_SII#TP}_2hoCn!+5Npz4<`YvuAT5Thv^8D&Ty(M3PeK>m#VK+1U>%Mh^99qaZ{6% z=0&$}FIC^_+S7+Q%*00EJE<;Hk!zYGp~tN1u)O#+Led=#FHof=)2rVSN7ajucqUki zFyw;6H0~vndVbp#4cS&wl@BiACqVj3<}$=ta_+x}nB?w7bhvfhS! zpCBZAYd9s2zR>3N5AScBx|4U3t_eUsF~#|-{$!5@#GyBnIZAk(A;v%UC%dw4^3dUl zuII=iMnrEYml&wfDTCbGY+gR-s-CrI_m)F#AsoOYnKZoEh1&Yfg({s}T^d=>OnMO?sNr~4`~m1U!% zH+b~32IVm>7dJ&hFILirN}|Q%yH$jHEr_a!-q6(9{$M5TS2O~fUV~J0trdC2ytb0| zD-MINTNBiqD@_d8aGJUil0h}zvo=%7Wi1}xt%4k=v>#=|FdZlhtcWT`2}kt{!Ix&l%(NEUO4yDZ6Dg%No`iIA17iht(uYxT* zPFtq6F(YED=>KAcEa-=Eb=zL)UkVNz4h8+dg6LcBNXSy*MzOGkoS7XR-6k>{c&~uy zsZKM#p%TZ!D~YUp%o+J5<%6V=yuz+Dhdoz%0rMiE4o$|+RACvgBXzR*0LmW(eI+fY z$W*5p->|Z7@=&{cDF*52a#q+M;BjDQrYR9Ymb>gH(sr$-~?bWF&Bo(m@8Uh_$iSy|3( zb|N$Lr5o(R%DTxzhsV0sBYd~FQ@J+~4Ih;{>RdQx%0&#win)={fLmkwVn9DRY26kd!EJCyf2 zu{QkfM2$YK%ADSD!pcBrc=13i$LfngK@Q}s-baz=$BgX0;`1+tOi;{=dhV3gU4rq_ zs1b*^_uZ8#{3Z|ymwgk+(ZZl12XY=r0_7uwzv9Txq9`9{?v@7Nw@s1oje~_47-Zx? z?y!C#i0JR@t~Fhq9S}l2k8tkD9JI@D5j*#ap(V&bM)XcySAr(o5u=5OYOdl+$Ic4avJx& zzaN#S3QK}O(ZAO=P%tRT0nnsli54z{eeFP`x+15{js*rSInAHFyGu1W74+Sl0?IS4 z#Nk diff --git a/vendor/gems/graphql/graphql.gemspec b/vendor/gems/graphql/graphql.gemspec deleted file mode 100644 index 7caf4b4f57d..00000000000 --- a/vendor/gems/graphql/graphql.gemspec +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true -$LOAD_PATH.push File.expand_path("../lib", __FILE__) -require "graphql/version" -require "date" - -Gem::Specification.new do |s| - s.name = "graphql" - s.version = GraphQL::VERSION - s.date = Date.today.to_s - s.summary = "A GraphQL language and runtime for Ruby" - s.description = "A plain-Ruby implementation of GraphQL." - s.homepage = "https://github.com/rmosolgo/graphql-ruby" - s.authors = ["Robert Mosolgo"] - s.email = ["rdmosolgo@gmail.com"] - s.license = "MIT" - s.required_ruby_version = ">= 2.7.0" - s.metadata = { - "homepage_uri" => "https://graphql-ruby.org", - "changelog_uri" => "https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md", - "source_code_uri" => "https://github.com/rmosolgo/graphql-ruby", - "bug_tracker_uri" => "https://github.com/rmosolgo/graphql-ruby/issues", - "mailing_list_uri" => "https://buttondown.email/graphql-ruby", - "rubygems_mfa_required" => "true", - } - - s.files = Dir["{lib}/**/*", "MIT-LICENSE", "readme.md", ".yardopts"] - - s.add_runtime_dependency "base64" - s.add_runtime_dependency "fiber-storage" - s.add_runtime_dependency "logger" - - s.add_development_dependency "benchmark-ips" - s.add_development_dependency "concurrent-ruby", "~>1.0" - s.add_development_dependency "google-protobuf" - s.add_development_dependency "graphql-batch" - s.add_development_dependency "memory_profiler" - - s.add_development_dependency "minitest" - s.add_development_dependency "minitest-focus" - s.add_development_dependency "minitest-reporters" - s.add_development_dependency "rake" - s.add_development_dependency 'rake-compiler' - s.add_development_dependency "rubocop" - s.add_development_dependency "simplecov" - s.add_development_dependency "simplecov-lcov" - s.add_development_dependency "undercover" - s.add_development_dependency "pronto" - s.add_development_dependency "pronto-undercover" - # website stuff - s.add_development_dependency "jekyll" - s.add_development_dependency "jekyll-sass-converter", "~>2.2" - s.add_development_dependency "yard" - s.add_development_dependency "jekyll-algolia" - s.add_development_dependency "jekyll-redirect-from" - s.add_development_dependency "m", "~> 1.5.0" - s.add_development_dependency "mutex_m" - s.add_development_dependency "webrick" -end diff --git a/vendor/gems/graphql/guides/CNAME b/vendor/gems/graphql/guides/CNAME deleted file mode 100644 index fbf0dd845de..00000000000 --- a/vendor/gems/graphql/guides/CNAME +++ /dev/null @@ -1 +0,0 @@ -graphql-ruby.org diff --git a/vendor/gems/graphql/guides/_config.yml b/vendor/gems/graphql/guides/_config.yml deleted file mode 100644 index 33a879e261c..00000000000 --- a/vendor/gems/graphql/guides/_config.yml +++ /dev/null @@ -1,42 +0,0 @@ -title: GraphQL Ruby -baseurl: "" -url: "https://graphql-ruby.org" - -exclude: - - .gitignore - -keep_files: ["api-doc", ".git"] -# Build settings -markdown: kramdown -highlighter: rouge - -kramdown: - auto_ids: true - hard_wrap: false - input: GFM - -defaults: - - - scope: - path: "" - values: - layout: "default" - fullwidth: true - -algolia: - application_id: '8VO8708WUV' - index_name: 'prod_graphql_ruby' - settings: - searchableAttributes: - - section - - title - - headings - - content - customRanking: - - desc(title) - - desc(headings) - - desc(content) - -plugins: - - jekyll-algolia - - jekyll-redirect-from diff --git a/vendor/gems/graphql/guides/_layouts/default.html b/vendor/gems/graphql/guides/_layouts/default.html deleted file mode 100644 index e19a20dce56..00000000000 --- a/vendor/gems/graphql/guides/_layouts/default.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - {% if page.section contains "GraphQL" %} - {{ page.section }} - {{ page.title }} - {% else %} - GraphQL - {{ page.title }} - {% endif %} - - - - - - -
- -
-
-
-
-
-
- {{ content }} -
- - - - - diff --git a/vendor/gems/graphql/guides/_layouts/doc_stub.html b/vendor/gems/graphql/guides/_layouts/doc_stub.html deleted file mode 100644 index 1391559ea8a..00000000000 --- a/vendor/gems/graphql/guides/_layouts/doc_stub.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - {{ content }} - - diff --git a/vendor/gems/graphql/guides/_layouts/guide.html b/vendor/gems/graphql/guides/_layouts/guide.html deleted file mode 100644 index d31bf196820..00000000000 --- a/vendor/gems/graphql/guides/_layouts/guide.html +++ /dev/null @@ -1,82 +0,0 @@ ---- -layout: default ---- - -{% if page.experimental %} -
-

- ⚠ Experimental ⚠ -

-

- This feature may get big changes in future releases. - Check the changelog for update notes. -

-
-{% endif %} -{% if page.pro %} -
-

- ⚡️ Pro Feature ⚡️ - - This feature is bundled with GraphQL-Pro. - -

-
-{% endif %} -{% if page.enterprise %} -
-

- 🌟 Enterprise Feature 🌟 - - This feature is bundled with GraphQL-Enterprise. - -

-
-{% endif %} -

{{ page.title }}

-
{% table_of_contents %}
-
- {{ content }} -
- - diff --git a/vendor/gems/graphql/guides/_plugins/api_doc.rb b/vendor/gems/graphql/guides/_plugins/api_doc.rb deleted file mode 100644 index ab927cafe8c..00000000000 --- a/vendor/gems/graphql/guides/_plugins/api_doc.rb +++ /dev/null @@ -1,249 +0,0 @@ -# frozen_string_literal: true -require_relative "../../lib/graphql/version" -require "kramdown" - -module GraphQLSite - API_DOC_ROOT = "/api-doc/#{GraphQL::VERSION}/" - - module APIDoc - def api_doc(input) - if !input.start_with?("GraphQL") - ruby_ident = "GraphQL::#{input}" - else - ruby_ident = input - end - - doc_path = ruby_ident - .gsub("::", "/") # namespaces - .sub(/#(.+)$/, "#\\1-instance_method") # instance methods - .sub(/\.(.+)$/, "#\\1-class_method") # class methods - - %|#{input}| - end - - def link_to_img(img_path, img_title) - full_img_path = "#{@context.registers[:site].baseurl}#{img_path}" - <<-HTML - - #{img_title} - - HTML - end - end - - class APIDocRoot < Liquid::Tag - def render(context) - API_DOC_ROOT - end - end - - class CalloutBlock < Liquid::Block - def initialize(tag_name, callout_class, tokens) - super - @callout_class = callout_class.strip - end - - def render(context) - raw_text = super - - site = context.registers[:site] - converter = site.find_converter_instance(::Jekyll::Converters::Markdown) - rendered_text = converter.convert(raw_text) - - heading = case @callout_class - when "warning" - "⚠ Heads up!" - else - raise ArgumentError, "Unhandled callout class: #{@callout_class.inspect}" - end - %|

#{heading}

#{rendered_text}
| - end - end - - class OpenAnIssue < Liquid::Tag - def initialize(tag_name, issue_info, tokens) - title, body = issue_info.split(",") - # remove whitespace and quotes if value is present - @title = strip_arg(title) - @body = strip_arg(body) - end - - def render(context) - %|open an issue| - end - - private - - def strip_arg(text) - text && text.strip[1..-2] - end - end - - # Build a URL relative to `site.baseurl`, - # asserting that the page exists. - class InternalLink < Liquid::Tag - GUIDES_ROOT = "guides/" - - def initialize(tag_name, guide_info, tokens) - text, path = guide_info.split(",") - # remove whitespace and quotes if value is present - @text = strip_arg(text) - @path = strip_arg(path) - if @path && @path.start_with?("/") - @path = @path[1..-1] - end - if !exist?(@path) - raise "Internal link failed, couldn't find file for: #{path}" - end - end - - def render(context) - <<-HTML.chomp -#{@text} - HTML - end - - private - - def strip_arg(text) - text && text.strip[1..-2] - end - - POSSIBLE_EXTENSIONS = [".html", ".md"] - def exist?(path) - filepath = GUIDES_ROOT + path.split("#").first - filepath = filepath.sub(".html", "") - POSSIBLE_EXTENSIONS.any? { |ext| File.exist?(filepath + ext) } - end - end - - class TableOfContents < Liquid::Tag - def render(context) - headers = context["page"]["content"].scan(/^##+[^\n]+$/m) - section_count = 0 - current_table = header_table = [nil] - prev_depth = nil - headers.each do |h| - header_hashes = h.match(/^#+/)[0] - depth = header_hashes.size - if depth == 2 - section_count += 1 - end - text = h.gsub(/^#+ /, "") - target = text.downcase - .gsub(/[^a-z0-9_]+/, "-") - .sub(/-$/, "") - .sub(/^-/, "") - - rendered_text = Kramdown::Document.new(text, auto_ids: false) - .to_html - .sub("

", "") - .sub("

", "") # remove wrapping added by kramdown - - if prev_depth - if prev_depth > depth - # outdent - current_table = current_table[0] - elsif prev_depth < depth - # indent - new_table = [current_table] - current_table[-1][-1] = new_table - current_table = new_table - else - # same depth - end - end - - current_table << [rendered_text, target, []] - prev_depth = depth - end - - table_html = "".dup - render_table_into_html(table_html, header_table) - - html = <<~HTML -
-

Contents

- #{table_html} -
- HTML - - if section_count == 0 - if headers.any? - full_path = "guides/#{context["page"]["path"]}" - warn("No sections identified for #{full_path} -- make sure it's using `## ...` for section headings.") - end - "" - else - html - end - end - - private - - def render_table_into_html(html_str, table) - html_str << "
    " - table.each_with_index do |entry, idx| - if idx == 0 - next # parent reference - end - rendered_text, target, child_table = *entry - html_str << "
  1. " - html_str << "#{rendered_text}" - if child_table.any? - render_table_into_html(html_str, child_table) - end - html_str << "
  2. " - end - html_str << "
" - end - end -end - - - -Liquid::Template.register_filter(GraphQLSite::APIDoc) -Liquid::Template.register_tag("api_doc_root", GraphQLSite::APIDocRoot) -Liquid::Template.register_tag("open_an_issue", GraphQLSite::OpenAnIssue) -Liquid::Template.register_tag("internal_link", GraphQLSite::InternalLink) -Liquid::Template.register_tag("table_of_contents", GraphQLSite::TableOfContents) -Liquid::Template.register_tag('callout', GraphQLSite::CalloutBlock) -Jekyll::Hooks.register :site, :pre_render do |site| - section_pages = Hash.new { |h, k| h[k] = [] } - section_names = [] - site.pages.each do |page| - this_section = page.data["section"] - if this_section - this_section_pages = section_pages[this_section] - this_section_pages << page - this_section_pages.sort_by! { |page| page.data["index"] || 100 } - page.data["section_pages"] = this_section_pages - section_names << this_section - end - end - section_names.compact! - section_names.uniq! - all_sections = [] - section_names.each do |section_name| - all_sections << { - "name" => section_name, - "overview_page" => section_pages[section_name].first, - } - end - - sorted_section_names = site.pages.find { |p| p.data["title"] == "Guides Index" }.data["sections"].map { |s| s["name"] } - all_sections.sort_by! { |s| sorted_section_names.index(s["name"]) } - site.data["all_sections"] = all_sections -end - -module Jekyll - module Algolia - module Hooks - def self.before_indexing_each(record, node, context) - record = record.dup - record.delete(:section_pages) - record - end - end - end -end diff --git a/vendor/gems/graphql/guides/_sass/reset.scss b/vendor/gems/graphql/guides/_sass/reset.scss deleted file mode 100644 index 47c6f90962b..00000000000 --- a/vendor/gems/graphql/guides/_sass/reset.scss +++ /dev/null @@ -1,48 +0,0 @@ -/* https://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/vendor/gems/graphql/guides/_tasks/site.rb b/vendor/gems/graphql/guides/_tasks/site.rb deleted file mode 100644 index 3c6cfbabdf6..00000000000 --- a/vendor/gems/graphql/guides/_tasks/site.rb +++ /dev/null @@ -1,195 +0,0 @@ -# frozen_string_literal: true -require "yard" -require "webrick" - -namespace :apidocs do - desc "Fetch a gem version from RubyGems, build the docs" - task :gen_version, [:version] do |t, args| - # GITHUB_REF comes from GitHub Actions - version = args[:version] || ENV["GITHUB_REF"] || raise("A version is required") - puts "Building docs for #{version}" - # GitHub Actions gives the full tag name - if version.start_with?("refs/tags/") - version = version[10..-1] - end - if version.start_with?("v") - version = version[1..-1] - end - Dir.mktmpdir do - puts "Fetching graphql-#{version}" - system("gem fetch graphql --version=#{version}") - system("gem unpack graphql-#{version}.gem") - system("rm graphql-#{version}.gem") - - Dir.chdir("graphql-#{version}") do - # Copy it into gh-pages for publishing - # and locally for previewing - push_dest = File.expand_path("../gh-pages/api-doc/#{version}") - local_dest = File.expand_path("../guides/_site/api-doc/#{version}") - puts "Creating directories: #{push_dest.inspect}, #{local_dest.inspect}" - FileUtils.mkdir_p(push_dest) - FileUtils.mkdir_p(local_dest) - system("yardoc") - puts "Copying from #{Dir.pwd}/doc to #{push_dest}" - copy_entry "doc", push_dest - puts "Copying from #{Dir.pwd}/doc to #{local_dest}" - copy_entry "doc", local_dest - end - end - puts "Successfully generated docs for #{version}" - end -end - -namespace :site do - desc "View the documentation site locally" - task serve: [] do # if you need api docs, add `:build_doc` to the list of dependencies - require "jekyll" - options = { - "source" => File.expand_path("guides"), - "destination" => File.expand_path("guides/_site"), - "watch" => true, - "serving" => true - } - # Generate the site in server mode. - puts "Running Jekyll..." - Jekyll::Commands::Build.process(options) - Jekyll::Commands::Serve.process(options) - end - - desc "Get the gh-pages branch locally, make sure it's up-to-date" - task :fetch_latest do - # Ensure the gh-pages dir exists so we can generate into it. - puts "Checking for gh-pages dir..." - unless File.exist?("./gh-pages") - puts "Creating gh-pages dir..." - sh "git clone git@github.com:rmosolgo/graphql-ruby gh-pages" - end - - # Ensure latest gh-pages branch history. - Dir.chdir("gh-pages") do - sh "git checkout gh-pages" - sh "git pull origin gh-pages" - end - end - - desc "Remove all generated HTML (making space to re-generate)" - task :clean_html do - # Proceed to purge all files in case we removed a file in this release. - puts "Cleaning gh-pages directory..." - purge_exclude = [ - 'gh-pages/.', - 'gh-pages/..', - 'gh-pages/.git', - 'gh-pages/.gitignore', - 'gh-pages/api-doc', - ] - - FileList["gh-pages/{*,.*}"].exclude(*purge_exclude).each do |path| - sh "rm -rf #{path}" - end - end - - desc "Build guides/ into gh-pages/ with Jekyll" - task :build_html do - # Copy site to gh-pages dir. - puts "Building site into gh-pages branch..." - ENV['JEKYLL_ENV'] = 'production' - require "jekyll" - Jekyll::Commands::Build.process({ - "source" => File.expand_path("guides"), - "destination" => File.expand_path("gh-pages"), - "sass" => { "style" => "compressed" } - }) - - File.write('gh-pages/.nojekyll', "Prevent GitHub from running Jekyll") - end - - desc "Commit new docs" - task :commit_changes do - puts "Committing and pushing to GitHub Pages..." - sha = `git rev-parse HEAD`.strip - Dir.chdir('gh-pages') do - system "git status" - system "git add ." - system "git status" - system "git commit --allow-empty -m 'Updating to #{sha}.'" - end - end - - desc "Push docs to gh-pages branch" - task :push_commit do - Dir.chdir('gh-pages') do - sh "git push origin gh-pages" - end - end - - desc "Commit the local site to the gh-pages branch and publish to GitHub Pages" - task publish: [:build_doc, :update_search_index, :fetch_latest, :clean_html, :build_html, :commit_changes, :push_commit] - - YARD::Rake::YardocTask.new(:prepare_yardoc) - - task build_doc: :prepare_yardoc do - require_relative "../../lib/graphql/version" - - def to_rubydoc_url(path) - "/api-doc/#{GraphQL::VERSION}/" + path - .gsub("::", "/") # namespaces - .sub(/#(.+)$/, "#\\1-instance_method") # instance methods - .sub(/\.(.+)$/, "#\\1-class_method") # class methods - end - - DOC_TEMPLATE = <<-PAGE ---- -layout: doc_stub -search: true -title: %{title} -url: %{url} -rubydoc_url: %{url} -doc_stub: true ---- - -%{documentation} -PAGE - - puts "Preparing YARD docs @ v#{GraphQL::VERSION} for search index..." - registry = YARD::Registry.load!(".yardoc") - files_target = "guides/yardoc" - FileUtils.rm_rf(files_target) - FileUtils.mkdir_p(files_target) - - # Get docs for all classes and modules - docs = registry.all(:class, :module) - docs.each do |code_object| - begin - # Skip private classes and modules - if code_object.visibility == :private - next - end - rubydoc_url = to_rubydoc_url(code_object.path) - page_content = DOC_TEMPLATE % { - title: code_object.path, - url: rubydoc_url, - documentation: code_object.format.gsub(/-{2,}/, " ").gsub(/^\s+/, ""), - } - - filename = code_object.path.gsub(/\W+/, "_") - filepath = "guides/yardoc/#{filename}.md" - File.write(filepath, page_content) - rescue StandardError => err - puts "Error on: #{code_object.path}" - puts err - puts err.backtrace - end - end - puts "Wrote #{docs.size} YARD docs to #{files_target}." - end - - desc "Update the Algolia search index used for graphql-ruby.org" - task :update_search_index do - if !ENV["ALGOLIA_API_KEY"] - warn("Can't update search index without ALGOLIA_API_KEY; Search will be out-of-date.") - else - system("bundle exec jekyll algolia push --source=./guides") - end - end -end diff --git a/vendor/gems/graphql/guides/authorization/authorization.md b/vendor/gems/graphql/guides/authorization/authorization.md deleted file mode 100644 index 913b38b1bab..00000000000 --- a/vendor/gems/graphql/guides/authorization/authorization.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -layout: guide -search: true -section: Authorization -title: Authorization -desc: During execution, check if the current user has permission to access retrieved objects. -index: 3 ---- - -While a query is running, you can check each object to see whether the current user is authorized to interact with that object. If the user is _not_ authorized, you can handle the case with an error. - -## Adding Authorization Checks - -Schema members have `authorized?` methods which will be called during execution: - -- Type classes have `.authorized?(object, context)` class methods -- Fields have `#authorized?(object, args, context)` instance methods -- Arguments have `#authorized?(object, arg_value, context)` instance methods -- Mutations and Resolvers have `.authorized?(object, context)` class methods and `#authorized?(args)` instance methods -- Enum values have `#authorized?(context)` instance methods - -These methods are called with: - -- `object`: the object from your application which was returned from a field -- `args`/`arg_value`: The arguments for a field, or the value of an argument -- `context`: the query context, based on the hash passed as `context:` - -#### Object Authorization - -When you implement this method to return `false`, the query will be halted, for example: - -```ruby -class Types::Friendship < Types::BaseObject - # You can only see the details on a `Friendship` - # if you're one of the people involved in it. - def self.authorized?(object, context) - super && (object.to_friend == context[:viewer] || object.from_friend == context[:viewer]) - end -end -``` - -(Always call `super` to get the default checks, too.) - -Now, whenever an object of type `Friendship` is going to be returned to the client, it will first go through the `.authorized?` method. If that method returns false, the field will get `nil` instead of the original object, and you may handle that case with an error (see below). - -#### Field Authorization - -Field `#authorized?` methods are called before resolving a field, for example: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - # Pass `field ..., require_admin: true` to reject non-admin users from a given field - def initialize(*args, require_admin: false, **kwargs, &block) - @require_admin = require_admin - super(*args, **kwargs, &block) - end - - def authorized?(obj, args, ctx) - # if `require_admin:` was given, then require the current user to be an admin - super && (@require_admin ? ctx[:viewer]&.admin? : true) - end -end -``` - -For this to work, the base field class must be {% internal_link "configured with other GraphQL types", "/type_definitions/extensions.html#customizing-fields" %}. - -#### Argument Authorization - -Argument `#authorized?` hooks are called before resolving the field that the argument belongs to. For example: - -```ruby -class Types::BaseArgument < GraphQL::Schema::Argument - def initialize(*args, require_logged_in: false, **kwargs, &block) - @require_logged_in = require_logged_in - super(*args, **kwargs, &block) - end - - def authorized?(obj, arg_value, ctx) - super && if @require_logged_in - ctx[:viewer].present? - else - true - end - end -end -``` - -For this to work, the base argument class must be {% internal_link "configured with other GraphQL types", "/type_definitions/extensions.html#customizing-arguments" %}. - -## Mutation Authorization - -See mutations/mutation_authorization.html#can-this-user-perform-this-action {% internal_link "Mutation Authorization", "/mutations/mutation_authorization.html#can-this-user-perform-this-action" %}) in the Mutation Guides. - -## Enum Value Authorization - -{{ "GraphQL::Schema::EnumValue#authorized?" | api_doc }} is called when client input is received and when the schema returns values to the client. - -For authorizing input, if a value's `#authorized?` method returns false, then a {{ "GraphQL::UnauthorizedEnumValueError" | api_doc }} is raised. It passed to your schema's `.unauthorized_object` hook, where you can handle it another way if you want. - -For authorizing return values, if an outgoing value's `#authorized?` method returns false, then a {{ "GraphQL::Schema::Enum::UnresolvedValueError" | api_doc }} is raised, which crashes the query. In this case, you should modify your field or resolver to _not_ return this value to an unauthorized viewer. (In this case, the error isn't returned to the viewer because the viewer can't do anything about it -- it's a developer-facing issue instead.) - -## Handling Unauthorized Objects - -By default, GraphQL-Ruby silently replaces unauthorized objects with `nil`, as if they didn't exist. You can customize this behavior by implementing {{ "Schema.unauthorized_object" | api_doc }} in your schema class, for example: - -```ruby -class MySchema < GraphQL::Schema - # Override this hook to handle cases when `authorized?` returns false for an object: - def self.unauthorized_object(error) - # Add a top-level error to the response instead of returning nil: - raise GraphQL::ExecutionError, "An object of type #{error.type.graphql_name} was hidden due to permissions" - end -end -``` - -Now, the custom hook will be called instead of the default one. - -If `.unauthorized_object` returns a non-`nil` object (and doesn't `raise` an error), then that object will be used in place of the unauthorized object. - -A similar hook is available for unauthorized fields: - -```ruby -class MySchema < GraphQL::Schema - # Override this hook to handle cases when `authorized?` returns false for a field: - def self.unauthorized_field(error) - # Add a top-level error to the response instead of returning nil: - raise GraphQL::ExecutionError, "The field #{error.field.graphql_name} on an object of type #{error.type.graphql_name} was hidden due to permissions" - end -end -``` diff --git a/vendor/gems/graphql/guides/authorization/can_can_integration.md b/vendor/gems/graphql/guides/authorization/can_can_integration.md deleted file mode 100644 index 09c3113eb82..00000000000 --- a/vendor/gems/graphql/guides/authorization/can_can_integration.md +++ /dev/null @@ -1,387 +0,0 @@ ---- -layout: guide -search: true -section: Authorization -title: CanCan Integration -desc: Hook up GraphQL to CanCan abilities -index: 4 -pro: true ---- - - -[GraphQL::Pro](https://graphql.pro) includes an integration for powering GraphQL authorization with [CanCan](https://github.com/CanCanCommunity/cancancan). - -__Why bother?__ You _could_ put your authorization code in your GraphQL types themselves, but writing a separate authorization layer gives you a few advantages: - -- Since the authorization code isn't embedded in GraphQL, you can use the same logic in non-GraphQL (or legacy) parts of the app. -- The authorization logic can be tested in isolation, so your end-to-end GraphQL tests don't have to cover as many possibilities. - -## Getting Started - -__NOTE__: Requires the latest gems, so make sure your `Gemfile` has: - -```ruby -# For CanCanIntegration: -gem "graphql-pro", ">=1.7.11" -# For list scoping: -gem "graphql", ">=1.8.7" -``` - -Then, `bundle install`. - -Whenever you run queries, include `:current_user` in the context: - -```ruby -context = { - current_user: current_user, - # ... -} -MySchema.execute(..., context: context) -``` - -### Rails Generator - -If your schema files follow the same convention as `rails generate graphql:install`, then you can install the CanCan integration with a Rails generator: - -```bash -$ rails generate graphql:cancan:install -``` - -This will insert all the necessary `include ...`s described below. Alternatively, check the docs below to mix in `CanCanIntegration`'s modules. - -## Authorizing Objects - -For each object type, you can assign a required action for Ruby objects of that type. To get started, include the `ObjectIntegration` in your base object class: - -```ruby -# app/graphql/types/base_object.rb -class Types::BaseObject < GraphQL::Schema::Object - # Add the CanCan integration: - include GraphQL::Pro::CanCanIntegration::ObjectIntegration - # By default, require `can :read, ...` - can_can_action(:read) - # Or, to require no permissions by default: - # can_can_action(nil) -end -``` - -Now, anyone fetching an object will need `can :read, ...` for that object. - -CanCan configurations are inherited, and can be overridden in subclasses. For example, to allow _all_ viewers to see the `Query` root type: - -```ruby -class Types::Query < Types::BaseObject - # Allow anyone to see the query root - can_can_action nil -end -``` - -### Bypassing CanCan - -`can_can_action(nil)` will override any inherited configuration and skip CanCan checks for an object, field, argument or mutation. - -### Handling Unauthorized Objects - -When any CanCan check returns `false`, the unauthorized object is passed to {{ "Schema.unauthorized_object" | api_doc }}, as described in {% internal_link "Handling unauthorized objects", "/authorization/authorization#handling-unauthorized-objects" %}. - -## Scopes - -#### ActiveRecord::Relation - -The CanCan integration adds [CanCan's `.accessible_by`](https://github.com/cancancommunity/cancancan/wiki/Fetching-Records) to GraphQL-Ruby's {% internal_link "list scoping", "/authorization/scoping" %} - -To scope lists of interface or union type, include the integration in your base union class and base interface module _and_ set a base `can_can_action`, if desired: - -```ruby -class BaseUnion < GraphQL::Schema::Union - include GraphQL::Pro::CanCanIntegration::UnionIntegration - # To provide a default action for scoping lists: - can_can_action :read -end - -module BaseInterface - include GraphQL::Schema::Interface - include GraphQL::Pro::CanCanIntegration::InterfaceIntegration - # To provide a default action for scoping lists: - can_can_action :read -end -``` - -#### Array - -For Arrays, the CanCan integration will use `.select { ... }` to filter items using the `can_can_action` from the lists's type. - -#### Bypassing scopes - -To allow an unscoped relation to be returned from a field, disable scoping with `scope: false`, for example: - -```ruby -# Allow anyone to browse the job postings -field :job_postings, [Types::JobPosting], null: false, - scope: false -``` - -## Authorizing Fields - -You can also require certain checks on a field-by-field basis. First, include the integration in your base field class: - -```ruby -# app/graphql/types/base_field.rb -class Types::BaseField < GraphQL::Schema::Field - # Add the CanCan integration: - include GraphQL::Pro::CanCanIntegration::FieldIntegration - # By default, don't require a role at field-level: - can_can_action nil -end -``` - -If you haven't already done so, you should also hook up your base field class to your base object and base interface: - -```ruby -# app/graphql/types/base_object.rb -class Types::BaseObject < GraphQL::Schema::Object - field_class Types::BaseField -end -# app/graphql/types/base_interface.rb -module Types::BaseInterface - # ... - field_class Types::BaseField -end -# app/graphql/mutations/base_mutation.rb -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - field_class Types::BaseField -end -``` - -Then, you can add `can_can_action:` options to your fields: - -```ruby -class Types::JobPosting < Types::BaseObject - # Only allow `can :review_applications, JobPosting` users - # to see who has applied - field :applicants, [Types::User], - can_can_action: :review_applicants -end -``` - -It will require the named action (`:review_applicants`) for the object being viewed (a `JobPosting`). - -### Authorizing by attribute - -CanCan 3.0 added attribute-level authorization ([pull request](https://github.com/CanCanCommunity/cancancan/pull/474)). You can leverage this in your field definitions with the `can_can_attribute:` configuration: - -```ruby -# This will call `.can?(:read, user, :email_address)` -field :email_address, String, - can_can_action: :read, - can_can_attribute: :email_address -``` - -You could also provide a _default value_ for `can_can_attribute` in your base field class: - -```ruby -class Types::BaseField - def initialize(*args, **kwargs, &block) - # pass all configs to the super class: - super - # Then set a new `can_can_attribute` value, if applicable - if can_can_attribute.nil? && can_can_action.present? - # `method_sym` is the thing GraphQL-Ruby will use to resolve this field - can_can_attribute(method_sym) - end - end -end -``` - -(See {{ "GraphQL::Schema::Field" | api_doc }} for the different values available for defaults.) - -### Providing a Custom CanCan Subject - -Authorization checks are _skipped_ whenever the underlying `object` is `nil`. This can happen in root query fields, for example, when no `root_value: ...` is given. To provide a `can_can_subject` in this case, you can add it as a field configuration: - -```ruby -field :users, Types::User.connection_type, null: false, - can_can_action: :manage, - # `:all` will be used instead of `object` (which is `nil`) - can_can_subject: :all -``` - -The configuration above will call `can?(:manage, :all)` whenever that field is requested. - -## Authorizing Arguments - -Similar to field-level checks, you can require certain permissions to _use_ certain arguments. To do this, add the integration to your base argument class: - -```ruby -class Types::BaseArgument < GraphQL::Schema::Argument - # Include the integration and default to no permissions required - include GraphQL::Pro::CanCanIntegration::ArgumentIntegration - can_can_action nil -end -``` - -Then, make sure your base argument is hooked up to your base field and base input object: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - argument_class Types::BaseArgument - # PS: see "Authorizing Fields" to make sure your base field is hooked up to objects, interfaces and mutations -end - -class Types::BaseInputObject < GraphQL::Schema::InputObject - argument_class Types::BaseArgument -end - -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - argument_class Types::BaseArgument -end -``` - -Now, arguments accept a `can_can_action:` option, for example: - -```ruby -class Types::Company < Types::BaseObject - field :employees, Types::Employee.connection_type do - # Only admins can filter employees by email: - argument :email, String, required: false, can_can_action: :admin - end -end -``` - -This will check for `can :admin, Company` (or a similar rule for the `company` being queried) for the current user. - -## Authorizing Mutations - -There are a few ways to authorize GraphQL mutations with the CanCan integration: - -- Add a [mutation-level roles](#mutation-level-roles) -- Run checks on [objects loaded by ID](#authorizing-loaded-objects) - -Also, you can configure [unauthorized object handling](#unauthorized-mutations) - -#### Setup - -Add `MutationIntegration` to your base mutation, for example: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - include GraphQL::Pro::CanCanIntegration::MutationIntegration - - # Also, to use argument-level authorization: - argument_class Types::BaseArgument -end -``` - -Also, you'll probably want a `BaseMutationPayload` where you can set a default role: - -```ruby -class Types::BaseMutationPayload < Types::BaseObject - # If `BaseObject` requires some permissions, override that for mutation results. - # Assume that anyone who can run a mutation can read their generated result types. - can_can_action nil -end -``` - -And hook it up to your base mutation: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - object_class Types::BaseMutationPayload - field_class Types::BaseField -end -``` - -#### Mutation-level roles - -Each mutation can have a class-level `can_can_action` which will be checked before loading objects or resolving, for example: - -```ruby -class Mutations::PromoteEmployee < Mutations::BaseMutation - can_can_action :run_mutation -end -``` - -In the example above, `can :run_mutation, Mutations::PromoteEmployee` will be checked before running the mutation. (The currently-running instance of `Mutations::PromoteEmployee` is passed to the ability checker.) - -#### Authorizing Loaded Objects - -Mutations can automatically load and authorize objects by ID using the `loads:` option. - -Beyond the normal [object reading permissions](#authorizing-objects), you can add an additional role for the specific mutation input using a `can_can_action:` option: - -```ruby -class Mutations::FireEmployee < Mutations::BaseMutation - argument :employee_id, ID, - loads: Types::Employee, - can_can_action: :supervise, -end -``` - -In the case above, the mutation will halt unless the `can :supervise, ...` check returns true. (The fetched instance of `Employee` is passed to the ability checker.) - -#### Unauthorized Mutations - -By default, an authorization failure in a mutation will raise a Ruby exception. You can customize this by implementing `#unauthorized_by_can_can(owner, value)` in your base mutation, for example: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - def unauthorized_by_can_can(owner, value) - # No error, just return nil: - nil - end -end -``` - -The method is called with: - -- `owner`: the `GraphQL::Schema::Argument` instance or mutation class whose role was not satisfied -- `value`: the object which didn't pass for `context[:current_user]` - -Since it's a mutation method, you can also access `context` in that method. - -Whatever that method returns will be treated as an early return value for the mutation, so for example, you could return {% internal_link "errors as data", "/mutations/mutation_errors" %}: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - field :errors, [String] - - def unauthorized_by_can_can(owner, value) - # Return errors as data: - { errors: ["Missing required permission: #{owner.can_can_action}, can't access #{value.inspect}"] } - end -end -``` - -## Authorizing Resolvers - -Resolvers are authorized just like [mutations](#authorizing-mutations), and require similar setup: - -```ruby -# app/graphql/resolvers/base_resolver.rb -class Resolvers::BaseResolver < GraphQL::Schema::Resolver - include GraphQL::Pro::CanCanIntegration::ResolverIntegration - argument_class BaseArgument - # can_can_action(nil) # to disable authorization by default -end -``` - -Beyond that, see [Authorizing Mutations](#authorizing-mutations) above for further details. - -## Custom Abilities Class - -By default, the integration will look for a top-level `::Ability` class. - -If you're using a different class, provide an instance ahead-of-time as `context[:can_can_ability]` - -For example, you could _always_ add one in your schema's `#execute` method: - -```ruby -class MySchema < GraphQL::Schema - # Override `execute` to provide a custom Abilities instance for the CanCan integration - def self.execute(*args, context: {}, **kwargs) - # Assign `context[:can_can_ability]` to an instance of our custom class - context[:can_can_ability] = MyAuthorization::CustomAbilitiesClass.new(context[:current_user]) - super - end -end -``` diff --git a/vendor/gems/graphql/guides/authorization/overview.md b/vendor/gems/graphql/guides/authorization/overview.md deleted file mode 100644 index 9e2258707ef..00000000000 --- a/vendor/gems/graphql/guides/authorization/overview.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -layout: guide -search: true -section: Authorization -title: Overview -desc: Overview of GraphQL authorization in general and an intro to the built-in framework. -index: 0 ---- - -Here's a conceptual approach to GraphQL authorization, followed by an introduction to the built-in authorization framework. Each part of the framework is described in detail in its own guide. - -## Authorization: GraphQL vs REST - -In a REST API, the common authorization pattern is fairly simple. Before performing the requested action, the server asserts that the current client has the required permissions for that action. For example: - -```ruby -class PostsController < ApiController - def create - # First, check the client's permission level: - if current_user.can?(:create_posts) - # If the user is permitted, then perform the action: - post = Post.create(params) - render json: post - else - # Otherwise, return an error: - render nothing: true, status: 403 - end - end -end -``` - -However, this request-by-request mindset doesn't map well to GraphQL because there's only one controller and the requests that come to it may be _very_ different. To illustrate the problem: - -```ruby -class GraphqlController < ApplicationController - def execute - # What permission is required for `query_str`? - # It depends on the string! So, you can't generalize at this level. - if current_user.can?(:"???") - MySchema.execute(query_str, context: ctx, variables: variables) - end - end -end -``` - -So, what new mindset will work with a GraphQL API? - -For __mutations__, remember that each mutation is like an API request in itself. For example, `Posts#create` above would map to the `createPost(...)` mutation in GraphQL. So, each mutation should be authorized in its own right. - -For __queries__, you can think of each individual _object_ like a `GET` request to a REST API. So, each object should be authorized for reading in its own right. - -By applying this mindset, each part of the GraphQL query will be properly authorized before it is executed. Also, since the different units of code are each authorized on their own, you can be sure that each incoming query will be properly authorized, even if it's a brand new query that the server has never seen before. - -## What About Authentication? - -As a reminder: - -- _Authentication_ is the process of determining what user is making the current request, for example, accepting a username and password, or finding a `User` in the database from `session[:current_user_id]`. -- _Authorization_ is the process of verifying that the current user has permission to do something (or see something), for example, checking `admin?` status or looking up permission groups from the database. - -In general, authentication is _not_ addressed in GraphQL at all. Instead, your controller should get the current user based on the HTTP request (eg, an HTTP header or a cookie) and provide that information to the GraphQL query. For example: - -```ruby -class GraphqlController < ApplicationController - def execute - # Somehow get the current `User` from this HTTP request. - current_user = get_logged_in_user(request) - # Provide the current user in `context` for use during the query - context = { current_user: current_user } - MySchema.execute(query_str, context: context, ...) - end -end -``` - -After your HTTP handler has loaded the current user, you can access it via `context[:current_user]` in your GraphQL code. - -## Authorization in Your Business Logic - -Before introducing GraphQL-specific authorization, consider the advantages of application-level authorization. (See the [GraphQL.org post](https://graphql.org/learn/authorization/) on the same topic.) For example, here's authorization mixed into the GraphQL API layer: - -```ruby -field :posts, [Types::Post], null: false - -def posts - # Perform an auth check in the GraphQL field code: - if context[:current_user].admin? - Post.all - else - Post.published - end -end -``` - -The downside of this is that, when `Types::Post` is queried in other contexts, the same authorization check may not be applied. Additionally, since the authorization code is coupled with the GraphQL API, the only way to test it is via GraphQL queries, which adds some complexity to tests. - -Alternatively, you could move the authorization to your business logic, the `Post` class: - -```ruby -class Post < ActiveRecord::Base - # Return the list of posts which `user` may see - def self.posts_for(user) - if user.admin? - self.all - else - self.published - end - end -end -``` - -Then, use this application method in your GraphQL code: - -```ruby -field :posts, [Types::Post], null: false - -def posts - # Fetch the posts this user can see: - Post.posts_for(context[:current_user]) -end -``` - -In this case, `Post.posts_for(user)` could be tested _independently_ from GraphQL. Then, you have less to worry about in your GraphQL tests. As a bonus, you can use `Post.posts_for(user)` in _other_ parts of the app, too, such as the web UI or REST API. - -## GraphQL-Ruby's Authorization Framework - -Despite the advantages of authorization at the application layer, as described above, there might be some reasons to authorize in the API layer: - -- Have an extra assurance that your API layer is secure -- Authorize the API request _before_ running it (see "visibility" below) -- Integrate with code that doesn't have authorization built-in - -To accomplish these, you can use GraphQL-Ruby's authorization framework. The framework has three levels, each of which is described in its own guide: - -- {% internal_link "Visibility", "/authorization/visibility" %} hides parts of the GraphQL schema from users who don't have full permission. -- {% internal_link "Authorization", "/authorization/authorization" %} checks application objects during execution to be sure the user has permission to access them. - -Also, [GraphQL::Pro](https://graphql.pro) has integrations for {% internal_link "CanCan", "/authorization/can_can_integration" %} and {% internal_link "Pundit", "/authorization/pundit_integration" %}. diff --git a/vendor/gems/graphql/guides/authorization/pundit_integration.md b/vendor/gems/graphql/guides/authorization/pundit_integration.md deleted file mode 100644 index 0900df6611c..00000000000 --- a/vendor/gems/graphql/guides/authorization/pundit_integration.md +++ /dev/null @@ -1,517 +0,0 @@ ---- -layout: guide -search: true -section: Authorization -title: Pundit Integration -desc: Hook up GraphQL to Pundit policies -index: 4 -pro: true ---- - -[GraphQL::Pro](https://graphql.pro) includes an integration for powering GraphQL authorization with [Pundit](https://github.com/varvet/pundit) policies. - -__Why bother?__ You _could_ put your authorization code in your GraphQL types themselves, but writing a separate authorization layer gives you a few advantages: - -- Since the authorization code isn't embedded in GraphQL, you can use the same logic in non-GraphQL (or legacy) parts of the app. -- The authorization logic can be tested in isolation, so your end-to-end GraphQL tests don't have to cover as many possibilities. - -## Getting Started - -__NOTE__: Requires the latest gems, so make sure your `Gemfile` has: - -```ruby -# For PunditIntegration: -gem "graphql-pro", ">=1.7.9" -# For list scoping: -gem "graphql", ">=1.8.7" -``` - -Then, `bundle install`. - -Whenever you run queries, include `:current_user` in the context: - -```ruby -context = { - current_user: current_user, - # ... -} -MySchema.execute(..., context: context) -``` - -### Rails Generator - -If your schema files follow the same convention as `rails generate graphql:install`, then you can install the Pundit integration with a Rails generator: - -```bash -$ rails generate graphql:pundit:install -``` - -This will insert all the necessary `include ...`s described below. Alternatively, check the docs below to mix in `PunditIntegration`'s modules. - -## Authorizing Objects - -You can specify Pundit roles that must be satisfied in order for viewers to see objects of a certain type. To get started, include the `ObjectIntegration` in your base object class: - -```ruby -# app/graphql/types/base_object.rb -class Types::BaseObject < GraphQL::Schema::Object - # Add the Pundit integration: - include GraphQL::Pro::PunditIntegration::ObjectIntegration - # By default, require staff: - pundit_role :staff - # Or, to require no permissions by default: - # pundit_role nil -end -``` - -Now, anyone trying to read a GraphQL object will have to pass the `#staff?` check on that object's policy. - -Then, each child class can override that parent configuration. For example, allow _all_ viewers to read the `Query` root: - -```ruby -class Types::Query < Types::BaseObject - # Allow anyone to see the query root - pundit_role nil -end -``` - -#### Policies and Methods - -For each object returned by GraphQL, the integration matches it to a policy and method. - -The policy is found using [`Pundit.policy!`](https://www.rubydoc.info/gems/pundit/Pundit%2Epolicy!), which looks up a policy using the object's class name. (This can be customized, see below.) - -Then, GraphQL will call a method on the policy to see whether the object is permitted or not. This method is assigned in the object class, for example: - -```ruby -class Types::Employee < Types::BaseObject - # Only show employee objects to their bosses, - # or when that employee is the current viewer - pundit_role :employer_or_self - # ... -end -``` - -That configuration will call `#employer_or_self?` on the corresponding Pundit policy. - -#### Custom Policy Class - -By default, the integration uses `Pundit.policy!(current_user, object)` to find a policy. You can specify a policy class using `pundit_policy_class(...)`: - -```ruby -class Types::Employee < Types::BaseObject - pundit_policy_class(Policies::CustomEmployeePolicy) - # Or, you could use a string: - # pundit_policy_class("Policies::CustomEmployeePolicy") -end -``` - -For really custom policy lookup, see [Custom Policy Lookup](#custom-policy-lookup) below. - -#### Bypassing Policies - -The integration requires that every object with a `pundit_role` has a corresponding policy class. To allow objects to _skip_ authorization, you can pass `nil` as the role: - -```ruby -class Types::PublicProfile < Types::BaseObject - # Anyone can see this - pundit_role nil -end -``` - -#### Handling Unauthorized Objects - -When any Policy method returns `false`, the unauthorized object is passed to {{ "Schema.unauthorized_object" | api_doc }}, as described in {% internal_link "Handling unauthorized objects", "/authorization/authorization#handling-unauthorized-objects" %}. - -## Scopes - -The Pundit integration adds [Pundit scopes](https://github.com/varvet/pundit#scopes) to GraphQL-Ruby's {% internal_link "list scoping", "/authorization/scoping" %} feature. Any list or connection will be scoped. If a scope is missing, the query will crash rather than risk leaking unfiltered data. - -To scope lists of interface or union type, include the integration in your base union class and base interface module: - -```ruby -class BaseUnion < GraphQL::Schema::Union - include GraphQL::Pro::PunditIntegration::UnionIntegration -end - -module BaseInterface - include GraphQL::Schema::Interface - include GraphQL::Pro::PunditIntegration::InterfaceIntegration -end -``` - -#### Bypassing scopes - -To allow an unscoped relation to be returned from a field, disable scoping with `scope: false`, for example: - -```ruby -# Allow anyone to browse the job postings -field :job_postings, [Types::JobPosting], null: false, - scope: false -``` - -## Authorizing Fields - -You can also require certain checks on a field-by-field basis. First, include the integration in your base field class: - -```ruby -# app/graphql/types/base_field.rb -class Types::BaseField < GraphQL::Schema::Field - # Add the Pundit integration: - include GraphQL::Pro::PunditIntegration::FieldIntegration - # By default, don't require a role at field-level: - pundit_role nil -end -``` - -If you haven't already done so, you should also hook up your base field class to your base object and base interface: - -```ruby -# app/graphql/types/base_object.rb -class Types::BaseObject < GraphQL::Schema::Object - field_class Types::BaseField -end -# app/graphql/types/base_interface.rb -module Types::BaseInterface - # ... - field_class Types::BaseField -end -# app/graphql/mutations/base_mutation.rb -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - field_class Types::BaseField -end -``` - -Then, you can add `pundit_role:` options to your fields: - -```ruby -class Types::JobPosting < Types::BaseObject - # Allow signed-in users to browse listings - pundit_role :signed_in - - # But, only allow `JobPostingPolicy#staff?` users to see - # who has applied - field :applicants, [Types::User], - pundit_role: :staff -end -``` - -It will call the named role (eg, `#staff?`) on the parent object's policy (eg `JobPostingPolicy`). - -#### Custom Policy Class - -You can override the policy class for a field using `pundit_policy_class:`, for example: - -```ruby -class Types::JobPosting < Types::BaseObject - # Only allow `ApplicantsPolicy#staff?` users to see - # who has applied - field :applicants, [Types::User], - pundit_role: :staff, - pundit_policy_class: ApplicantsPolicy - # Or with a string: - # pundit_policy_class: "ApplicantsPolicy" -end -``` - -This will initialize an `ApplicantsPolicy` with the parent object (a `Job`) and call `#staff?` on it. - -For really custom policy lookup, see [Custom Policy Lookup](#custom-policy-lookup) below. - -## Authorizing Arguments - -Similar to field-level checks, you can require certain permissions to _use_ certain arguments. To do this, add the integration to your base argument class: - -```ruby -class Types::BaseArgument < GraphQL::Schema::Argument - # Include the integration and default to no permissions required - include GraphQL::Pro::PunditIntegration::ArgumentIntegration - pundit_role nil -end -``` - -Then, make sure your base argument is hooked up to your base field and base input object: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - argument_class Types::BaseArgument - # PS: see "Authorizing Fields" to make sure your base field is hooked up to objects, interfaces and mutations -end - -class Types::BaseInputObject < GraphQL::Schema::InputObject - argument_class Types::BaseArgument -end - -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - argument_class Types::BaseArgument -end -``` - -Now, arguments accept a `pundit_role:` option, for example: - -```ruby -class Types::Company < Types::BaseObject - field :employees, Types::Employee.connection_type do - # Only admins can filter employees by email: - argument :email, String, required: false, pundit_role: :admin - end -end -``` - -The role will be called on the parent object's policy, for example `CompanyPolicy#admin?` in the case above. - -## Authorizing Mutations - -There are a few ways to authorize GraphQL mutations with the Pundit integration: - -- Add a [mutation-level roles](#mutation-level-roles) -- Run checks on [objects loaded by ID](#authorizing-loaded-objects) - -Also, you can configure [unauthorized object handling](#unauthorized-mutations) - -#### Setup - -Add `MutationIntegration` to your base mutation, for example: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - include GraphQL::Pro::PunditIntegration::MutationIntegration - - # Also, to use argument-level authorization: - argument_class Types::BaseArgument -end -``` - -Also, you'll probably want a `BaseMutationPayload` where you can set a default role: - -```ruby -class Types::BaseMutationPayload < Types::BaseObject - # If `BaseObject` requires some permissions, override that for mutation results. - # Assume that anyone who can run a mutation can read their generated result types. - pundit_role nil -end -``` - -And hook it up to your base mutation: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - object_class Types::BaseMutationPayload - field_class Types::BaseField -end -``` - -#### Mutation-level roles - -Each mutation can have a class-level `pundit_role` which will be checked before loading objects or resolving, for example: - -```ruby -class Mutations::PromoteEmployee < Mutations::BaseMutation - pundit_role :admin -end -``` - -In the example above, `PromoteEmployeePolicy#admin?` will be checked before running the mutation. - -#### Custom Policy Class - -By default, Pundit uses the mutation's class name to look up a policy. You can override this by defining `pundit_policy_class` on your mutation: - -```ruby -class Mutations::PromoteEmployee < Mutations::BaseMutation - pundit_policy_class ::UserPolicy - pundit_role :admin -end -``` - -Now, the mutation will check `UserPolicy#admin?` before running. - -For really custom policy lookup, see [Custom Policy Lookup](#custom-policy-lookup) below. - -#### Authorizing Loaded Objects - -Mutations can automatically load and authorize objects by ID using the `loads:` option. - -Beyond the normal [object reading permissions](#authorizing-objects), you can add an additional role for the specific mutation input using a `pundit_role:` option: - -```ruby -class Mutations::FireEmployee < Mutations::BaseMutation - argument :employee_id, ID, - loads: Types::Employee, - pundit_role: :supervisor, -end -``` - -In the case above, the mutation will halt unless the `EmployeePolicy#supervisor?` method returns true. - -#### Unauthorized Mutations - -By default, an authorization failure in a mutation will raise a Ruby exception. You can customize this by implementing `#unauthorized_by_pundit(owner, value)` in your base mutation, for example: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - def unauthorized_by_pundit(owner, value) - # No error, just return nil: - nil - end -end -``` - -The method is called with: - -- `owner`: the `GraphQL::Schema::Argument` or mutation class whose role was not satisfied -- `value`: the object which didn't pass for `context[:current_user]` - -Since it's a mutation method, you can also access `context` in that method. - -Whatever that method returns will be treated as an early return value for the mutation, so for example, you could return {% internal_link "errors as data", "/mutations/mutation_errors" %}: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - field :errors, [String] - - def unauthorized_by_pundit(owner, value) - # Return errors as data: - { errors: ["Missing required permission: #{owner.pundit_role}, can't access #{value.inspect}"] } - end -end -``` - -## Authorizing Resolvers - -Resolvers are authorized just like [mutations](#authorizing-mutations), and require similar setup: - -```ruby -# app/graphql/resolvers/base_resolver.rb -class Resolvers::BaseResolver < GraphQL::Schema::Resolver - include GraphQL::Pro::PunditIntegration::ResolverIntegration - argument_class BaseArgument - # pundit_role nil # to disable authorization by default -end -``` - -Beyond that, see [Authorizing Mutations](#authorizing-mutations) above for further details. - -## Custom Policy Lookup - -By default, the integration uses `Pundit`'s top-level methods to interact with policies: - -- `Pundit.policy!(context[:current_user], object)` is called to find a policy instance -- `Pundit.policy_scope!(context[:current_user], items)` is called to filter `items` - -### Custom Policy Methods - -You can implement a custom lookup by defining the following methods in your schema: - -- `pundit_policy_class_for(object, context)` to return a policy class (or raise an error if one isn't found) -- `pundit_role_for(object, context)` to return a role method (Symbol), or `nil` to bypass authorization -- `scope_by_pundit_policy(context, items)` to apply a scope to `items` (or raise an error if one isn't found) - -Since different objects have different lifecycles, the hooks are installed slightly different ways: - -- Your base argument, field, and mutation classes should have _instance methods_ with those names -- Your base type classes should have _class methods_ with that name - -Here's an example of how the custom hooks can be installed: - -```ruby -module CustomPolicyLookup - # Lookup policies in the `SystemAdmin::` namespace for system_admin users - # @return [Class] - def pundit_policy_class_for(object, context) - current_user = context[:current_user] - if current_user.system_admin? - SystemAdmin.const_get("#{object.class.name}Policy") - else - super - end - end - - # Require admin permissions if the object is pending_approval - def pundit_role_for(object, context) - if object.pending_approval? - :admin - else - super # fall back to the normally-configured role - end - end -end - -# Add policy hooks as class methods -class Types::BaseObject < GraphQL::Schema::Object - extend CustomPolicyLookup -end -class Types::BaseUnion < GraphQL::Schema::Union - extend CustomPolicyLookup -end -module Types::BaseInterface - include GraphQL::Schema::Interface - # Add this as a class method that will be "inherited" by other interfaces: - definition_methods do - include CustomPolicyLookup - end -end - -# Add policy hooks as instance methods -class Types::BaseField < GraphQL::Schema::Field - include CustomPolicyLookup -end -class Types::BaseArgument < GraphQL::Schema::Argument - include CustomPolicyLookup -end -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - include CustomPolicyLookup -end -``` - -### One Policy Per Class - -Another good approach is to have one policy per class. You can implement `policy_class_for(object, context)` to look up a policy _within_ the class, for example: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - def policy_class_for(_object, _context) - # Look up a nested `Policy` constant: - self.class.const_get(:Policy) - end -end -``` - -Then, each mutation can define its policy inline, for example: - -```ruby -class Mutations::PromoteEmployee < Mutations::BaseMutation - # This will be found by `BaseMutation.policy_class`, defined above: - class Policy - # ... - end - - pundit_role :admin -end -``` - -Now, `Mutations::PromoteEmployee::Policy#admin?` will be checked before running the mutation. - -## Custom User Lookup - -By default, the Pundit integration looks for the current user in `context[:current_user]`. You can override this by implementing `#pundit_user` on your custom query context class. For example: - -```ruby -# app/graphql/query_context.rb -class QueryContext < GraphQL::Query::Context - def pundit_user - # Lookup `context[:viewer]` instead: - self[:viewer] - end -end -``` - -Then be sure to hook up your custom class in the schema: - -```ruby -class MySchema < GraphQL::Schema - context_class(QueryContext) -end -``` - -Then, the Pundit integration will use your `def pundit_user` to get the current user at runtime. diff --git a/vendor/gems/graphql/guides/authorization/scoping.md b/vendor/gems/graphql/guides/authorization/scoping.md deleted file mode 100644 index 5d8a8e4e54a..00000000000 --- a/vendor/gems/graphql/guides/authorization/scoping.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: guide -search: true -section: Authorization -title: Scoping -desc: Filter lists to match the current viewer and context -index: 4 ---- - - -_Scoping_ is a complementary consideration to authorization. Rather than checking "can this user see this thing?", scoping takes a list of items filters it to the subset which is appropriate for the current viewer and context. - -For similar features, see [Pundit scopes](https://github.com/varvet/pundit#scopes) and [Cancan's `.accessible_by`](https://github.com/cancancommunity/cancancan/wiki/Fetching-Records). - -## `scope:` option - -Fields accept a `scope:` option to enable (or disable) scoping, for example: - -```ruby -field :products, [Types::Product], scope: true -# Or -field :all_products, [Types::Product], scope: false -``` - -For __list__ and __connection__ fields, `scope: true` is the default. For all other fields, `scope: false` is the default. You can override this by using the `scope:` option. - -## `.scope_items(items, ctx)` method - -Type classes may implement `.scope_items(items, ctx)`. This method is called when a field has `scope: true`. For example, - -```ruby -field :products, [Types::Product] # has `scope: true` by default -``` - -Will call: - -```ruby -class Types::Product < Types::BaseObject - def self.scope_items(items, context) - # filter items here - end -end -``` - -The method should return a new list with only the appropriate items for the current `context`. - -## Bypassing object-level authorization - -If you know that any items returned from `.scope_items` should be visible to the current client, you can skip the normal `.authorized?(obj, ctx)` checks by configuring `reauthorize_scoped_objects(false)` in your type definition. For example: - -```ruby -class Types::Product < Types::BaseObject - # Check that singly-loaded objects are visible to the current viewer - def self.authorized?(object, context) - super && object.visible_to?(context[:viewer]) - end - - # Filter any list to only include objects that are visible to the current viewer - def self.scope_items(items, context) - items = super(items, context) - items.visible_for(context[:viewer]) - end - - # If an object of this type was returned from `.scope_items`, - # don't call `.authorized?` with it. - reauthorize_scoped_objects(false) -end -``` diff --git a/vendor/gems/graphql/guides/authorization/visibility.md b/vendor/gems/graphql/guides/authorization/visibility.md deleted file mode 100644 index 4b38bd9e794..00000000000 --- a/vendor/gems/graphql/guides/authorization/visibility.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -layout: guide -search: true -section: Authorization -title: Visibility -desc: Programmatically hide parts of the GraphQL schema from some users. -index: 1 -redirect_from: -- /schema/limiting_visibility ---- - -With GraphQL-Ruby, it's possible to _hide_ parts of your schema from some users. This isn't exactly part of the GraphQL spec, but it's roughly within the bounds of the spec. - -Here are some reasons you might want to hide parts of your schema: - -- You don't want non-admin users to know about administration functions of the schema. -- You're developing a new feature and want to make a gradual release to only a few users first. - -## Hiding Parts of the Schema - -To start limiting visibility of your schema, add the plugin: - -```ruby -class MySchema < GraphQL::Schema - # ... - use GraphQL::Schema::Visibility # see below for options -end -``` - -Then, you can customize the visibility of parts of your schema by reimplementing various `visible?` methods: - -- Type classes have a `.visible?(context)` class method -- Fields and arguments have a `#visible?(context)` instance method -- Enum values have `#visible?(context)` instance method -- Mutation classes have a `.visible?(context)` class method - -These methods are called with the query context, based on the hash you pass as `context:`. If the method returns false, then that member of the schema will be treated as though it doesn't exist for the entirety of the query. That is: - -- In introspection, the member will _not_ be included in the result -- In normal queries, if a query references that member, it will return a validation error, since that member doesn't exist - -## Visibility Profiles - -You can use named profiles to cache your schema's visibility modes. For example: - -```ruby -use GraphQL::Schema::Visibility, profiles: { - # mode_name => example_context_hash - public: { public: true }, - beta: { public: true, beta: true }, - internal_admin: { internal_admin: true } -} -``` - -Then, you can run queries with `context[:visibility_profile]` equal to one of the pre-defined profiles. When you do, GraphQL-Ruby will create a cached set of types for named profile. `.visible?` will only be called with the context hash passed to `profiles: ...`. - -The profile contexts passed to `profiles` will have `visibility_profile: ...` added to them, then they're frozen by GraphQL-Ruby. - -### Preloading profiles - -By default, GraphQL-Ruby will preload all named visibility profiles when `Rails.env.production?` is present and true. You can manually set this option by passing `use ... preload: true` (or `false`). Enable preloading in production to reduce latency of the first request to each visibility profile. Disable preloading in development to speed up application boot. - -### Dynamic profiles - -When you provide named visibility profiles, `context[:visibility_profile]` is required for query execution. You can also permit dynamic visibility for queries which _don't_ have that key set by passing `use ..., dynamic: true`. You could use this to support backwards compatibility or when visibility calculations are too complex to predefine. - -When no named profiles are defined, all queries use dynamic visibility. - -## Object Visibility - -Let's say you're working on a new feature which should remain secret for a while. You can implement `.visible?` in a type: - -```ruby -class Types::SecretFeature < Types::BaseObject - def self.visible?(context) - # only show it to users with the secret_feature enabled - super && context[:viewer].feature_enabled?(:secret_feature) - end -end -``` - -(Always call `super` to inherit the default behavior.) - -Now, the following bits of GraphQL will return validation errors: - -- Fields that return `SecretFeature`, eg `query { findSecretFeature { ... } }` -- Fragments on `SecretFeature`, eg `Fragment SF on SecretFeature` - -And in introspection: - -- `__schema { types { ... } }` will not include `SecretFeature` -- `__type(name: "SecretFeature")` will return `nil` -- Any interfaces or unions which normally include `SecretFeature` will _not_ include it -- Any fields that return `SecretFeature` will be excluded from introspection - -## Field Visibility - -```ruby -class Types::BaseField < GraphQL::Schema::Field - # Pass `field ..., require_admin: true` to hide this field from non-admin users - def initialize(*args, require_admin: false, **kwargs, &block) - @require_admin = require_admin - super(*args, **kwargs, &block) - end - - def visible?(ctx) - # if `require_admin:` was given, then require the current user to be an admin - super && (@require_admin ? ctx[:viewer]&.admin? : true) - end -end -``` - -For this to work, the base field class must be {% internal_link "configured with other GraphQL types", "/type_definitions/extensions.html#customizing-fields" %}. - -## Argument Visibility - -```ruby -class Types::BaseArgument < GraphQL::Schema::Argument - # If `require_logged_in: true` is given, then this argument will be hidden from logged-out viewers - def initialize(*args, require_logged_in: false, **kwargs, &block) - @require_logged_in = require_logged_in - super(*args, **kwargs, &block) - end - - def visible?(ctx) - super && (@require_logged_in ? ctx[:viewer].present? : true) - end -end -``` - -For this to work, the base argument class must be {% internal_link "configured with other GraphQL types", "/type_definitions/extensions.html#customizing-arguments" %}. - -## Opting Out - -By default, GraphQL-Ruby always runs visibility checks. You can opt out of this by adding to your schema class: - -```ruby -class MySchema < GraphQL::Schema - # ... - # Opt out of GraphQL-Ruby's visibility feature: - use GraphQL::Schema::AlwaysVisible -end -``` - -For big schemas, this can be a worthwhile speed-up. - -## Migration Notes - -{{ "GraphQL::Schema::Visibility" | api_doc }} is a _new_ implementation of visibility in GraphQL-Ruby. It has some slight differences from the previous implementation ({{ "GraphQL::Schema::Warden" | api_doc }}): - -- `Visibility` speeds up Rails app boot because it doesn't require all types to be loaded during boot and only loads types as they are used by queries. -- `Visibility` supports predefined, reusable visibility profiles which speeds up queries using complicated `visible?` checks. -- `Visibility` hides types differently in a few edge cases: - - Previously, `Warden` hide interface and union types which had no possible types. `Visibility` doesn't check possible types (in order to support performance improvements), so those types must return `false` for `visible?` in the same cases where all possible types were hidden. Otherwise, that interface or union type will be visible but have no possible types. - - Some other thing, see TODO -- When `Visibility` is used, several (Ruby-level) Schema introspection methods don't work because the caches they draw on haven't been calculated (`Schema.references_to`, `Schema.union_memberships`). If you're using these, please get in touch so that we can find a way forward. - -### Migration Mode - -You can use `use GraphQL::Schema::Visibility, ... migration_errors: true` to enable migration mode. In this mode, GraphQL-Ruby will make visibility checks with _both_ `Visibility` and `Warden` and compare the result, raising a descriptive error when the two systems return different results. As you migrate to `Visibility`, enable this mode in test to find any unexpected discrepancies. - -Sometimes, there's a discrepancy that is hard to resolve but doesn't make any _real_ difference in application behavior. To address these cases, you can use these flags in `context`: - -- `context[:visibility_migration_running] = true` is set in the main query context. -- `context[:visibility_migration_warden_running] = true` is set in the _duplicate_ context which is passed to a `Warden` instance. -- If you set `context[:skip_migration_error] = true`, then no migration error will be raised for that query. - -You can use these flags to conditionally handle edge cases that should be ignored in testing. diff --git a/vendor/gems/graphql/guides/changesets/definition.md b/vendor/gems/graphql/guides/changesets/definition.md deleted file mode 100644 index f46c5eda054..00000000000 --- a/vendor/gems/graphql/guides/changesets/definition.md +++ /dev/null @@ -1,300 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Changesets -title: Defining Changesets -desc: Creating a set of modifications to release in an API version -index: 2 ---- - -After {% internal_link "installing Changeset integrations", "/changesets/installation" %} in your schema, you can create Changesets which modify parts of the schema. Changesets extend `GraphQL::Enterprise::Changeset` and include a `release` string. Once a Changeset class is defined, it can be referenced with `added_in: ...` or `removed_in: ...` configurations in the schema. - -__Note:__ Before GraphQL-Enterprise 1.3.0, Changesets were configured with `modifies ...` blocks. These blocks are still supported and you can find the documentation for that API [on GitHub](https://github.com/rmosolgo/graphql-ruby/blob/v2.0.22/guides/changesets/definition.md). - - -## Changeset Classes - -This Changeset will be available to any client whose `context[:changeset_version]` is on or after `2020-12-01`: - -```ruby -# app/graphql/changesets/deprecate_recipe_flag.rb -class Changesets::DeprecateRecipeTags < GraphQL::Enterprise::Changeset - release "2020-12-01" -end -``` - -Additionally, Changesets must be {% internal_link "released", "/changesets/releases" %} for their changes to be published. - -## Publishing with `added_in:` - -New things can be published in a changeset by adding `added_in: SomeChangeset` to their configuration. For example, to add a new argument to a field: - -```ruby -field :search_recipes, [Types::Recipe] do - argument :query, String - argument :tags, [Types::RecipeTag], required: false, added_in: Changesets::AddRecipeTags -end -``` - -You can also provide a _replacement_ implementation by using `added_in:`. When a new definition has the same name as an existing definition, it implicitly replaces the previous definition in new versions of the API. For example: - -```ruby -field :rating, Integer, "A 1-5 score for this recipe" # This definition will be superseded by the following one -field :rating, Float, "A 1.0-5.0 score for this recipe", added_in: Changesets::FloatingPointRatings -``` - -Here, a new implementation for `rating` will be used when clients requests an API version that includes `Changesets::FloatingPointRatings`. (If the client requests a version _before_ that changeset, then the preceding implementation would be used instead.) - -## Removing with `removed_in:` - -A `removed_in:` configuration removes something in the named changeset. For example, these enum values are replaced with more clearly-named ones: - -```ruby -class Types::RecipeTag < Types::BaseEnum - # These are replaced by *_HEAT below: - value :SPICY, removed_in: Changesets::ClarifyHeatTags - value :MEDIUM, removed_in: Changesets::ClarifyHeatTags - value :MILD, removed_in: Changesets::ClarifyHeatTags - # These new tags are more clear: - value :SPICY_HEAT, added_in: Changesets::ClarifyHeatTags - value :MEDIUM_HEAT, added_in: Changesets::ClarifyHeatTags - value :MILD_HEAT, added_in: Changesets::ClarifyHeatTags -end -``` - -If something has been defined several times, a `removed_in:` configuration removes _all_ definitions: - -```ruby -class Mutations::SubmitRecipeRating < Mutations::BaseMutation - # This is replaced in future API versions by the following argument - argument :rating, Integer - # This replaces the previous, but in another future version, - # it is removed completely (and so is the previous one) - argument :rating, Float, added_in: Changesets::FloatingPointRatings, removed_in: Changesets::RemoveRatingsCompletely -end -``` - -## Examples - -See below for the different kind of modifications you can make in a changeset: - -- [Fields](#fields): adding, modifying, and removing fields -- [Arguments](#arguments): adding, modifying, and removing arguments -- [Enum values](#enum-values): adding, modifying, and removing arguments -- [Unions](#unions): adding or removing object types from a union -- [Interfaces](#interfaces): adding or removing interface implementations from object types -- [Types](#types): changing one type definition for another -- [Runtime](#runtime): choosing a behavior at runtime based on the current request and changeset - -### Fields - -To add or redefine a field, use `field(..., added_in: ...)`, including all configuration values for the new implementation (see {{ "GraphQL::Schema::Field#initialize" | api_doc }}). The definition given here will override the previous definition (if there was one) whenever this Changeset applies. - -```ruby -class Types::Recipe < Types::BaseObject - # This new field is available when `context[:changeset_version]` - # is on or after the release date of `AddRecipeTags` - field :tags, [Types::RecipeTag], added_in: Changeset::AddRecipeTags -end -``` - -To remove a field, add a `removed_in: ...` configuration to the last definition of the field: - -```ruby -class Types::Recipe < Types::BaseObject - # Even after migrating to floating point values, - # the "rating" feature never took off, - # so we removed it entirely eventually. - field :rating, Integer - field :rating, Float, added_in: Changeset::FloatingPointRatings, - removed_in: Changeset::RemoveRatings -end -``` - -When a field is removed, queries that request that field will be invalid, unless the client has requested a previous API version where the field is still available. - -### Arguments - -You can add, redefine, or remove arguments that belong to fields, input objects, or resolvers. Use `added_in: ...` to provide a new (or updated) definition for an argument, for example: - -```ruby -class Types::RecipesFilter < Types::BaseInputObject - argument :rating, Integer - # This new definition is available when - # the client's `context[:changeset_version]` includes `FloatingPointRatings` - argument :rating, Float, added_in: Changesets::FloatingPointRatings -end -``` - -To remove an argument entirely, add a `removed_in: ...` configuration to the last definition. It will remove _all_ implementations for that argument. For example: - -```ruby -class Mutations::SubmitRating < Mutations::BaseMutation - # Remove this because it's irrelevant: - argument :phone_number, String, removed_in: Changesets::StopCollectingPersonalInformation -end -``` - -When arguments are removed, the schema will reject any queries which use them unless the client has requested a previous API version where the argument is still allowed. - -### Enum Values - -With Changesets, you can add, redefine, or remove enum values. To add a new value (or provide a new implementation for a value), include `added_in:` in the `value(...)` configuration: - -```ruby -class Types::RecipeTag < Types::BaseEnum - # This enum will accept and return `KETO` only when the client's API version - # includes `AddKetoDietSupport`'s release date. - value :KETO, added_in: Changesets::AddKetoDietSupport -end -``` - -Values can be removed with `removed_in:`, for example: - -```ruby -class Types::RecipeTag < Types::BaseEnum - # Old API versions will serve this value; - # new versions won't accept it or return it. - value :GRAPEFRUIT_DIET, removed_in: Changesets::RemoveLegacyDiets -end -``` - -When enum values are removed, they won't be accepted as input and they won't be allowed as return values from fields unless the client has requested a previous API version where those values are still allowed. - -### Unions - -You can add to or remove from a union's possible types. To release a new union member, include `added_in:` in the `possible_types` configuration: - -```ruby -class Types::Cookable < Types::BaseUnion - possible_types Types::Recipe, Types::Ingredient - # Add this to the union when clients opt in to our new feature: - possible_types Types::Cuisine, added_in: Changeset::ReleaseCuisines -``` - -To remove a member from a union, move it to a `possible_types` call with `removed_in: ...`: - -```ruby -# Stop including this in the union in new API versions: -possible_types Types::Chef, removed_in: Changeset::LessChefHype -``` - -When a possible type is removed, it will not be associated with the union type in introspection queries or schema dumps. - -### Interfaces - -You can add to or remove from an object type's interface definitions. To add one or more interface implementations, use `implements(..., added_in:)`. This will add the interface and its fields to the object whenever this Changeset is active, for example: - -```ruby -class Types::Recipe < Types::BaseObject - # Add this new implementation in new API versions only: - implements Types::RssSubject, added_in: Changesets::AddRssSupport -end -``` - - -To remove one or more more interface implementations, add `removed_in:` to the `implements ...` configuration, for example: - -```ruby - implements Types::RssSubject, - added_in: Changesets::AddRssSupport, - # Sadly, nobody seems to want to use this, - # so we removed it all: - removed_in: Changesets::RemoveRssSupport -``` - -When an interface implementation is removed, then the interface will not be associated with the object in introspection queries or schema dumps. Also, any fields inherited from the interface will be hidden from clients. (If the object defines the field itself, it will still be visible.) - -### Types - -Using Changesets, it's possible to define a new type using the same name as an old type. (Only one type per name is allowed for each query, but different queries can use different types for the same name.) - -First, to define two types with the same name, make two different type definitions. One of them will have to use `graphql_name(...)` to specify the conflicting type name. For example, to migrate an enum type to an object type, define two types: - -```ruby -# app/graphql/types/legacy_recipe_flag.rb - -# In the old version of the schema, "recipe tags" were limited to defined set of values. -# This enum was renamed from `Types::RecipeTag`, then `graphql_name("RecipeTag")` -# was added for GraphQL. -class Types::LegacyRecipeTag < Types::BaseEnum - graphql_name "RecipeTag" - # ... -end -``` - -```ruby -# app/graphql/types/recipe_flag.rb - -# But in the new schema, each tag is a full-fledged object with fields of its own -class Types::RecipeTag < Types::BaseObject - field :name, String, null: false - field :is_vegetarian, Boolean, null: false - # ... -end -``` - -Then, add or update fields or arguments to use the _new_ type instead of the old one. For example: - -```diff - class Types::Recipe < Types::BaseObject - -# Change this definition to point at the newly-renamed _legacy_ type -# (It's the same type definition, but the Ruby class has a new name) -- field :tags, [Types::RecipeTag] -+ field :tags, [Types::LegacyRecipeTag] - -# And add a new field for the new type: -+ field :tags, [Types::RecipeTag], added_in: Changesets::MigrateRecipeTagToObject - end -``` - -With that Changeset, `Recipe.tags` will return an object type instead of an enum type. Clients requesting older versions will still receive enum values from that field. - -The resolver will probably need an update, too, for example: - -```ruby -class Types::Recipe < Types::BaseObject - # Here's the original definition which returns enum values: - field :tags, [Types::LegacyRecipeTag], null: false - # Here's the new definition which replaces the previous one on new API versions: - field :tags, [Types::RecipeTag], null: false, added_in: Changesets::MigrateRecipeTagToObject - - def flags - all_flag_objects = object.flag_objects - if Changesets::MigrateRecipeTagToObject.active?(context) - # Here's the new behavior, returning full objects: - all_flag_objects - else - # Convert this to enum values, for legacy behavior: - all_flag_objects.map { |f| f.name.upcase } - end - end -end -``` - -That way, legacy clients will continue to receive enum values while new clients will receive objects. - -## Runtime - -While a query is running, you can check if a changeset applies by using its `.active?(context)` method. For example: - -```ruby -class Types::Recipe - field :flag, Types::RecipeFlag, null: true - - def flag - # Check if this changeset applies to the current request: - if Changesets::DeprecateRecipeFlag.active?(context) - Stats.count(:deprecated_recipe_flag, context[:viewer]) - end - # ... - end -end -``` - -Besides observability, you can use a runtime check when a resolver needs to pick a different behavior depending on the API version. - -After defining a changeset, add it to the schema to {% internal_link "release it", "/changesets/releases" %}. diff --git a/vendor/gems/graphql/guides/changesets/installation.md b/vendor/gems/graphql/guides/changesets/installation.md deleted file mode 100644 index a0a8ecb2dcd..00000000000 --- a/vendor/gems/graphql/guides/changesets/installation.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Changesets -title: Installing Changesets -desc: Adding Changesets to your schema -index: 1 ---- - -Changesets require some updates to the schema (to define changesets) and some updates to your controller (to receive version headers from clients). - -## Schema Setup - -To get started with [GraphQL-Enterprise](https://graphql.pro/enterprise) Changesets, you have to add them to your schema. They're added in several places: - -- To support versioning arguments, add the `ArgumentIntegration` to your base argument: - - ```ruby - # app/graphql/types/base_argument.rb - class Types::BaseArgument < GraphQL::Schema::Argument - include GraphQL::Enterprise::Changeset::ArgumentIntegration - end - ``` - - Also, make sure that your `BaseField`, `BaseInputObject`, `BaseResolver`, and `BaseMutation` have `argument_class(Types::BaseArgument)` configured in them. - -- To support versioning fields, add the `FieldIntegration` to your base field: - - ```ruby - # app/graphql/types/base_field.rb - class Types::BaseField < GraphQL::Schema::Field - include GraphQL::Enterprise::Changeset::FieldIntegration - argument_class(Types::BaseArgument) - end - ``` - - Also, make sure that your `BaseObject`, `BaseInterface`, and `BaseMutation` have `field_class(Types::BaseField)` configured in them. - -- To support versioning enum values, add the `EnumValueIntegration` to your base enum value: - - ```ruby - # app/graphql/types/base_enum_value.rb - class Types::BaseEnumValue < GraphQL::Schema::EnumValue - include GraphQL::Enterprise::Changeset::EnumValueIntegration - end - ``` - - Also, make sure that your `BaseEnum` has `enum_value_class(Types::BaseEnumValue)` configured in it. - -- To support versioning union memberships and interface implementations, add the `TypeMembershipIntegration` to your base type membership: - - ```ruby - # app/graphql/types/base_type_membership.rb - class Types::BaseTypeMembership < GraphQL::Schema::TypeMembership - include GraphQL::Enterprise::Changeset::TypeMembershipIntegration - end - ``` - - Also, make sure that your `BaseUnion` and `BaseInterface` have `type_membership_class(Types::BaseTypeMembership)` configured in it. (`TypeMembership`s are used by GraphQL-Ruby to link object types to the union types they belong to and the interfaces they implement. By using a custom type membership class, you can make objects belong (or _not_ belong) to unions or interfaces, depending on the API version.) - -Once those integrations are set up, you're ready to {% internal_link "write a changeset", "/changesets/definition" %} and start {% internal_link "releasing API versions", "/changesets/releases" %}! - -## Controller Setup - -Additionally, your controller must pass `context[:changeset_version]` when running queries. To provide this, update your controller: - -```ruby -class GraphqlController < ApplicationController - def execute - context = { - # ... - changeset_version: headers["API-Version"], # <- Your header here. Choose something for API clients to pass. - } - result = MyAppSchema.execute(..., context: context) - # ... - end -end -``` - -In the example above, `API-Version: ...` will be parsed from the incoming request and used as `context[:changeset_version]`. - -If `context[:changeset_version]` is `nil`, then _no_ changesets will apply to that request. - -Now that Changesets are installed, read on to {% internal_link "define some changesets", "/changesets/definition" %}. diff --git a/vendor/gems/graphql/guides/changesets/overview.md b/vendor/gems/graphql/guides/changesets/overview.md deleted file mode 100644 index 715dfe38045..00000000000 --- a/vendor/gems/graphql/guides/changesets/overview.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Changesets -title: API Versioning for GraphQL-Ruby -desc: Evolve your schema over time, feature-by-feature -index: 0 ---- - - -Out-of-the-box, GraphQL is [versionless by design](https://graphql.org/learn/best-practices/#versioning). GraphQL's openness to extension paves the way for continuously expanding and improving an API. You can _always_ add new fields, new arguments, and new types to implement new features and customize existing behavior. - -However, sometimes a business case may call for a different versioning scheme. [GraphQL-Enterprise](https://graphql.pro/enterprise)'s "Changesets" enable schemas to release _any_ change -- even breaking changes -- to clients, depending on what version of the schema they're using. With Changesets, you can redefine existing fields, define new types using old names, add or remove enum values -- anything, really -- while maintaining compatibility for existing clients. - -## Why Changesets? - -Changesets are a _complementary_ evolution technique to continuous additions. In general, additive changes (new fields, new arguments, new types) are best added right to the existing schema. But if you need to _remove_ something from the schema or redefine existing parts of the schema in non-backwards-compatible ways, Changesets provide a handy way of doing so. - -For example, if you add a values to an Enum, you can just add it to the existing schema: - -```diff - class Types::RecipeTag < Types::BaseEnum - value "LOW_FAT" - value "LOW_CARB" -+ value "VEGAN" -+ value "KETO" -+ value "GRAPEFRUIT_DIET" - end -``` - -However, if you want to change the schema in ways that would _break_ previous queries, you can do that with a Changeset: - -```ruby -class Types::RecipeTag < Types::BaseEnum - # Turns out this makes you sick: - value "GRAPEFRUIT_DIET", removed_in: Changesets::RemoveLegacyDiets -end -``` - -Then, only clients requesting API versions _before_ this changeset would be able to use `GRAPEFRUIT_DIET`; clients requesting newer versions could not send it as input and would not receive it in responses. - -(Changesets _also_ support additive changes, if you prefer to make them that way.) - -## Getting Started - -To start using Changesets, read on: - -- {% internal_link "Installing Changesets", "/changesets/installation" %} -- {% internal_link "Writing Changesets", "/changesets/definition" %} -- {% internal_link "Releasing Changesets", "/changesets/releases" %} diff --git a/vendor/gems/graphql/guides/changesets/releases.md b/vendor/gems/graphql/guides/changesets/releases.md deleted file mode 100644 index f36de20d886..00000000000 --- a/vendor/gems/graphql/guides/changesets/releases.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Changesets -title: Releasing Changesets -desc: Associating changes to version numbers -index: 3 ---- - -To be available to clients, Changesets added to the schema with `use GraphQL::Enterprise::Changeset::Release changeset_dir: "..."`: - -```ruby -class MyAppSchema < GraphQL::Schema - # Add this before root types so that newly-added types are also added to the schema - use GraphQL::Enterprise::Changeset::Release, changeset_dir: "app/graphql/changesets" - - query(...) - mutation(...) - subscription(...) -end -``` - -This attaches each Changeset defined in `app/graphql/changesets/*.rb` to the schema. (It assumes Rails conventions, where an underscored file like `app/graphql/changesets/add_some_feature.rb` contains a class like `Changesets::AddSomeFeature`.) - -{% callout warning %} - -Add `GraphQL::Enterprise::Changeset::Release` _before_ hooking up your root `query(...)`, `mutation(...)`, and `subscription(...)` types. Otherwise, the schema may not find links to types in new schema versions. - -{% endcallout %} - -Alternatively, Changesets can be explicitly attached using `changesets: [...]`, for example: - -```ruby -class MyAppSchema < GraphQL::Schema - use GraphQL::Enterprise::Changeset::Release, changesets: [ - Changesets::DeprecateRecipeFlag, - Changesets::RemoveRecipeFlag, - ] - # ... -end -``` - -Only changesets in the directory (or in the array) will be shown to clients. The `release ...` configuration in the changeset will be compared to `context[:changeset_version]` to determine if the changeset applies to the current request. - -## Inspecting Releases - -To preview releases, you can create schema dumps by passing `context: { changeset_version: ... }` to {{ "Schema.to_definition" | api_doc }}. - -For example, to see how the schema looks with `API-Version: 2021-06-01`: - -```ruby -schema_sdl = MyAppSchema.to_definition(context: { changeset_version: "2021-06-01"}) -# The GraphQL schema definition for the schema at version "2021-06-01": -puts schema_sdl -``` - -To make sure schema versions don't change unexpectedly, use the techniques described in the {% internal_link "Schema structure guide", "/testing/schema_structure" %}. - -### Introspection Methods - -You can also inspect a schema's changesets programmatically. `GraphQL::Enterprise` adds a `Schema.changesets` method which returns a `Set` of changeset classes: - -```ruby -MySchema.changesets -# # -``` - -Additionally, each changeset has a `.changes` method describing its modifications: - -```ruby -AddNewFeature.changes -# [ -# #, -# #, -# #, -# ... -# ] -``` - -Each `Change` object responds to: - -- `.member`, the part of the schema that was modified -- `.type`, the kind of modification (`:addition` when something new is added, `:removal` when a member is removed or replaced with a new definition) diff --git a/vendor/gems/graphql/guides/css/main.scss b/vendor/gems/graphql/guides/css/main.scss deleted file mode 100644 index 663df7c2166..00000000000 --- a/vendor/gems/graphql/guides/css/main.scss +++ /dev/null @@ -1,746 +0,0 @@ ---- ---- - -@use "reset"; - -$brand-color: #a5152a; -$dark-theme-brand-color: #e5534b; -$brand-color-light: #ed8090; -$brand-color-extralight: #f9e8ee; -$dark-theme-brand-color-extralight: #262324; - -$experimental-background: #fff7cf; -$experimental-color: hsla(50, 100%, 32%, 0.15); - -$pro-color: #406db5; -$pro-background: hsla(217, 100%, 29%, 0.15); - -$enterprise-color: #238c44; -$enterprise-background: hsla(135, 97%, 25%, 0.15); - -$dark-theme-code-border: #aaaaaa; -$code-border: #d6d6d6; -$dark-theme-code-background: #1e1b1b; -$code-background: #fafafa; -$dark-theme-code-color: #b5b5b5; -$code-color: #777777; -$code-border-radius: 2px; - -$muted-color: #777777; -$subtle-color: #aaaaaa; -$font: 'Rubik', sans-serif; -$code-font: 'Monaco', monospace; - -$faint-color: #f0f0f0; -$dark-theme-faint-color: #6a6969; - -$font-color: black; -$dark-theme-font-color: #dbdbdb; - -$background-color: #fafafa; -$dark-theme-background-color: #422e2e; - -$container-color: white; -$dark-theme-container-color: #424242; - -body { - font-family: $font; - background: $background-color; - .dark-theme-button::after { - content: "☀" - } -} - -body.dark-theme { - background: $dark-theme-code-background; - color: $dark-theme-font-color; - .dark-theme-button::after { - content: "☽" - } -} - -strong, b { - font-weight: bold; -} - -// Algolia highlights: -.ais-Highlight { - font-style: normal; - font-weight: bold; -} - -.dark-theme { - .header { - background: $dark-theme-container-color; - box-shadow: 0px 0px 10px 0px black; - .nav a:hover { - color: $font-color; - background-color: $dark-theme-brand-color; - } - } -} -.header { - box-shadow: 0px 0px 10px 0px #d6d6d6; - z-index: 1; - position: relative; - background: $container-color; - .nav { - $height: 30px; - $margin: 10px; - display: flex; - flex-wrap: wrap; - align-items: center; - $fade-time: 0.2s; - - .nav-links { - margin-left: auto; - display: flex; - } - - .img-link { - transition: background $fade-time; - - img { - transition: filter $fade-time; - transition: -webkit-filter $fade-time; - filter: brightness(1) invert(0); - -webkit-filter: brightness(1) invert(0); - } - - &:hover { - img { - -webkit-filter: brightness(0) invert(1); - filter: brightness(0) invert(1); - } - } - } - - img { - height: $height; - width: auto; - margin: $margin; - } - - a, span { - transition: background-color $fade-time; - transition: color $fade-time; - padding: $margin; - height: $height; - display: flex; - align-items: center; - text-decoration: none; - &:hover { - background-color: $brand-color; - color: white; - } - } - } -} - -.header-container { - margin: 0px 20px 0px 20px; -} - -.container { - max-width: 1200px; - margin: 0px auto; - padding: 10px 20px; - background: $container-color; - &.fullwidth { - max-width: 100%; - margin: 0px 20px 0px 20px; - } -} - -.dark-theme { - .container { - background: $dark-theme-container-color; - } -} - -.callout { - padding: 20px 20px 10px 20px; - margin: 20px; - border: 2px; - border-radius: 10px; - .heading { - font-size: 20px; - font-weight: bold; - margin-bottom: 20px; - } - - &.callout-warning { - background-color: rgba(255, 217, 0, 0.2); - border-color: rgba(255, 217, 0, 0.5); - } -} - -pre { - font-family: $code-font; - padding: 0.5rem; - border: 1px solid $code-border; - border-radius: $code-border-radius; - background-color: $code-background; - margin: 10px 0px; - overflow-x: auto; - line-height: 1.4rem; -} - -.dark-theme { - pre { - background-color: $dark-theme-code-background; - border: 1px solid $dark-theme-code-border; - } -} - -p, li { - line-height: 1.3rem; -} - -li { - margin-left: 15px; - margin-top: 5px -} - -p, ul { - margin-bottom: 20px; -} - -ul { - list-style-type: disc; - list-style-position: outside; -} - -ol { - list-style: decimal; - margin-left: 5px; -} - -code { - font-family: $code-font; - color: $code-color; - font-weight: 400; -} -.dark-theme code { - color: $dark-theme-code-color; -} -.code .line-numbers { - display: none; -} - - -.dark-theme a { - color: $dark-theme-brand-color; - border-color: $dark-theme-brand-color; - code { - color: $dark-theme-brand-color; - } -} - -a { - color: $brand-color; - border-color: $brand-color; - text-decoration: none; - code { - color: $brand-color; - } -} - -a:hover, a:hover code { - text-decoration: underline; -} - -#readme img { - display: none; -} - -.guide-container { - a.img-link { - background: none; - &:hover { - background: none; - } - img { - box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); - transition: all 0.3s cubic-bezier(.25,.8,.25,1); - max-height: 300px; - max-width: 100%; - &:hover { - box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); - } - } - } -} - -.cell { - overflow-x: scroll; -} - -.guides-toc { - ul { - list-style: none; - display: flex; - flex-wrap: wrap; - } - - li { - padding-bottom: 10px; - padding-right: 10px; - } -} - - -.guides { - .guide-desc { - color: $muted-color; - margin-left: 5px; - } - - ul { - margin-left: 10px; - list-style: none; - } -} - -@mixin doc-header($color, $background-color) { - background-color: $background-color; - color: $color; - border-radius: $code-border-radius; - padding: 10px 10px 10px 10px; - margin-bottom: 10px; - border: 1px solid $color; - p { - margin: 0px; - padding: 0px; - } - a { - color: $color; - &:hover { - background-color: $color; - color: $background-color; - } - } -} - -.experimental-header { - @include doc-header($experimental-color, $experimental-background); -} - -.pro-header { - @include doc-header($pro-color, $pro-background); -} - -.enterprise-header { - @include doc-header($enterprise-color, $enterprise-background); -} - -.dark-theme .guide-footer { - background-color: $dark-theme-brand-color-extralight; -} - -.guide-footer { - background: $brand-color-extralight; - margin: 25px 0px 0px 0px; - padding: 10px; - border-radius: $code-border-radius; -} - - -.dark-theme { - .hero { - .hero-part { - &.shaded { - background: $dark-theme-faint-color; - } - - h2 { - color: $dark-theme-brand-color; - text-shadow: $dark-theme-background-color 1px 1px 1px; - } - } - } -} - -.hero { - display: flex; - flex-direction: column; - justify-content: space-around; - .hero-title { - display: flex; - justify-content: center; - align-items: center; - - img, h1 { - margin: 20px 10px 30px 10px; - } - } - - .hero-subtitle { - padding: 10px 0px; - p { - margin: 5px auto; - text-align: center; - } - } - - .hero-part { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - - &.shaded { - background: $faint-color; - } - - h2 { - color: $brand-color; - text-shadow: #cccccc 1px 1px 1px; - font-size: 1.4em; - } - - .hero-feature { - padding: 15px; - flex-basis: calc(50% - 60px); - flex-grow: 1; - } - } -} - -h1, h2, h3, h4, h5 { - margin: 25px 0px 15px 0px; - a { - text-decoration: none; - } -} - -.guide-header { - margin-bottom: 15px; -} - -h1 { font-size: 1.5rem; } -h2 { font-size: 1.3rem; } -h3 { font-size: 1.2rem; } -h4 { font-size: 1.1rem; } -h5 { font-size: 1.05rem; } -em { font-style: italic; } - -table { - width: 100%; - margin: 0px 0px 15px 0px; - thead { - text-align: left; - border-bottom: 1px solid $subtle-color; - } - td, th { - padding: 5px 10px 5px 0px; - } -} - -.dark-theme { - .search-input { - background: $dark-theme-code-background; - color: $dark-theme-font-color; - } - .search-results-container { - background-color: $dark-theme-background-color; - #search-results { - .search-result { - &:focus, &:hover { - background-color: $dark-theme-container-color; - border-bottom-color: $dark-theme-brand-color; - .search-title { - color: $dark-theme-brand-color; - } - } - .search-category { - border: 1px solid $dark-theme-brand-color; - color: $dark-theme-brand-color; - } - } - } - } -} -.search-input { - font-size: 1em; - padding: 5px; - margin: 10px; - border: 1px solid $subtle-color; - border-radius: 3px; -} - -.search-results-container { - $bg: #eaeaea; - $bg-highlight: #f9f9f9; - background-color: $bg; - - #search-results { - $fade-time: 0.1s; - display: flex; - flex-direction: column; - max-width: 1040px; - margin: 0 auto; - .search-result { - text-decoration: none; - color: $font-color; - padding: 6px 10px 0px 6px; - line-height: 18px; - border-bottom: 2px solid transparent; - transition: border-bottom-color $fade-time; - - .search-title { - font-weight: bold; - margin-right: 8px; - transition: color $fade-time; - } - - .search-preview { - color: $subtle-color; - } - .search-category { - border: 1px solid $brand-color; - border-radius: 3px; - margin: 0 8px 0 0; - padding: 3px; - font-size: 0.7em; - color: $brand-color; - position: relative; - top: -3px; - } - - &:focus, &:hover { - outline: none; - background-color: $bg-highlight; - border-bottom-color: $brand-color; - .search-title { - color: $brand-color; - } - } - } - } -} - -.dark-theme ul.breadcrumb .jump-to-select { - color: $dark-theme-brand-color; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='292.4' height='292.4'%3E%3Cpath fill='%23e5534b' d='M287 69.4a17.6 17.6 0 0 0-13-5.4H18.4c-5 0-9.3 1.8-12.9 5.4A17.6 17.6 0 0 0 0 82.2c0 5 1.8 9.3 5.4 12.9l128 127.9c3.6 3.6 7.8 5.4 12.8 5.4s9.2-1.8 12.8-5.4L287 95c3.5-3.5 5.4-7.8 5.4-12.8 0-5-1.9-9.2-5.5-12.8z'/%3E%3C/svg%3E"); -} - -ul.breadcrumb { - color: $muted-color; - - li { - display: inline; - list-style: none; - margin: 0; - } - li:before { - content: "»"; - margin: 0px 4px 0px 2px; - } - li:first-child:before { - content: ""; - margin: 0; - } - - .jump-to-select { - box-sizing: border-box; - -moz-appearance: none; - -webkit-appearance: none; - appearance: none; - padding: 5px 20px 5px 5px; - border: 1px solid $code-border; - border-radius: 5px; - background-color: transparent; - color: $brand-color; - font-size: 16px; - font-weight: 500; - line-height: 1.3; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='292.4' height='292.4'%3E%3Cpath fill='%23a5152a' d='M287 69.4a17.6 17.6 0 0 0-13-5.4H18.4c-5 0-9.3 1.8-12.9 5.4A17.6 17.6 0 0 0 0 82.2c0 5 1.8 9.3 5.4 12.9l128 127.9c3.6 3.6 7.8 5.4 12.8 5.4s9.2-1.8 12.8-5.4L287 95c3.5-3.5 5.4-7.8 5.4-12.8 0-5-1.9-9.2-5.5-12.8z'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 8px center; - background-size: 9px; - - cursor: default; - - &:hover { - border-color: #777; - } - - &:focus { - border-color: #999; - box-shadow: 0 0 1px 2px #6db4ff; - outline: none; - } - - - option { - color: black; - } - } -} - - -.dark-theme { - .table-of-contents { - background: $dark-theme-code-background; - } -} - -.table-of-contents { - float: right; - border: 1px solid $subtle-color; - border-radius: 3px; - padding: 15px; - margin: 0 10px 10px 10px; - width: 300px; - background: $code-background; - .contents-header { - margin: 0 0 5px 20px; - } - .contents-list { - margin: 0; - list-style: decimal; - padding-left: 5px; - .contents-entry { - &::marker { - color: $muted-color; - } - - .contents-entry { - list-style: none; - } - } - } -} - -/* pygments CSS, github theme */ -.highlight .hll { background-color: #ffffcc } -.highlight .c { color: #999988; font-style: italic } /* Comment */ -.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ -.highlight .k { color: #000000; font-weight: bold } /* Keyword */ -.highlight .o { color: #000000; font-weight: bold } /* Operator */ -.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */ -.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ -.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #aa0000 } /* Generic.Error */ -.highlight .gh { color: #999999 } /* Generic.Heading */ -.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ -.highlight .go { color: #888888 } /* Generic.Output */ -.highlight .gp { color: #555555 } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ -.highlight .gt { color: #aa0000 } /* Generic.Traceback */ -.highlight .kc { color: #000000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ -.highlight .m { color: #009999 } /* Literal.Number */ -.highlight .s { color: #d01040 } /* Literal.String */ -.highlight .na { color: #008080 } /* Name.Attribute */ -.highlight .nb { color: #0086B3 } /* Name.Builtin */ -.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ -.highlight .no { color: #008080 } /* Name.Constant */ -.highlight .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */ -.highlight .ni { color: #800080 } /* Name.Entity */ -.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ -.highlight .nl { color: #990000; font-weight: bold } /* Name.Label */ -.highlight .nn { color: #555555 } /* Name.Namespace */ -.highlight .nt { color: #000080 } /* Name.Tag */ -.highlight .nv { color: #008080 } /* Name.Variable */ -.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #009999 } /* Literal.Number.Float */ -.highlight .mh { color: #009999 } /* Literal.Number.Hex */ -.highlight .mi { color: #009999 } /* Literal.Number.Integer */ -.highlight .mo { color: #009999 } /* Literal.Number.Oct */ -.highlight .sb { color: #d01040 } /* Literal.String.Backtick */ -.highlight .sc { color: #d01040 } /* Literal.String.Char */ -.highlight .sd { color: #d01040 } /* Literal.String.Doc */ -.highlight .s2 { color: #d01040 } /* Literal.String.Double */ -.highlight .se { color: #d01040 } /* Literal.String.Escape */ -.highlight .sh { color: #d01040 } /* Literal.String.Heredoc */ -.highlight .si { color: #d01040 } /* Literal.String.Interpol */ -.highlight .sx { color: #d01040 } /* Literal.String.Other */ -.highlight .sr { color: #009926 } /* Literal.String.Regex */ -.highlight .s1 { color: #d01040 } /* Literal.String.Single */ -.highlight .ss { color: #990073 } /* Literal.String.Symbol */ -.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #008080 } /* Name.Variable.Class */ -.highlight .vg { color: #008080 } /* Name.Variable.Global */ -.highlight .vi { color: #008080 } /* Name.Variable.Instance */ -.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ - - -.dark-theme { - .highlight .hll { background-color: #49483e } - pre.highlight { background: #272822; color: #f8f8f2 } - .highlight .c { color: #75715e } /* Comment */ - .highlight .err { color: #960050; background-color: #1e0010 } /* Error */ - .highlight .k { color: #66d9ef } /* Keyword */ - .highlight .l { color: #ae81ff } /* Literal */ - .highlight .n { color: #f8f8f2 } /* Name */ - .highlight .o { color: #f92672 } /* Operator */ - .highlight .p { color: #f8f8f2 } /* Punctuation */ - .highlight .ch { color: #75715e } /* Comment.Hashbang */ - .highlight .cm { color: #75715e } /* Comment.Multiline */ - .highlight .cp { color: #75715e } /* Comment.Preproc */ - .highlight .cpf { color: #75715e } /* Comment.PreprocFile */ - .highlight .c1 { color: #75715e } /* Comment.Single */ - .highlight .cs { color: #75715e } /* Comment.Special */ - .highlight .gd { color: #f92672; background-color: #5e4343; } /* Generic.Deleted */ - .highlight .ge { font-style: italic } /* Generic.Emph */ - .highlight .gi { color: #a6e22e; background-color: #475547; } /* Generic.Inserted */ - .highlight .gs { font-weight: bold } /* Generic.Strong */ - .highlight .gu { color: #75715e } /* Generic.Subheading */ - .highlight .kc { color: #66d9ef } /* Keyword.Constant */ - .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ - .highlight .kn { color: #f92672 } /* Keyword.Namespace */ - .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ - .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ - .highlight .kt { color: #66d9ef } /* Keyword.Type */ - .highlight .ld { color: #e6db74 } /* Literal.Date */ - .highlight .m { color: #ae81ff } /* Literal.Number */ - .highlight .s { color: #e6db74 } /* Literal.String */ - .highlight .na { color: #a6e22e } /* Name.Attribute */ - .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ - .highlight .nc { color: #a6e22e } /* Name.Class */ - .highlight .no { color: #66d9ef } /* Name.Constant */ - .highlight .nd { color: #a6e22e } /* Name.Decorator */ - .highlight .ni { color: #f8f8f2 } /* Name.Entity */ - .highlight .ne { color: #a6e22e } /* Name.Exception */ - .highlight .nf { color: #a6e22e } /* Name.Function */ - .highlight .nl { color: #f8f8f2 } /* Name.Label */ - .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ - .highlight .nx { color: #a6e22e } /* Name.Other */ - .highlight .py { color: #f8f8f2 } /* Name.Property */ - .highlight .nt { color: #f92672 } /* Name.Tag */ - .highlight .nv { color: #f8f8f2 } /* Name.Variable */ - .highlight .ow { color: #f92672 } /* Operator.Word */ - .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ - .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ - .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ - .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ - .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ - .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ - .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ - .highlight .sc { color: #e6db74 } /* Literal.String.Char */ - .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ - .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ - .highlight .se { color: #ae81ff } /* Literal.String.Escape */ - .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ - .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ - .highlight .sx { color: #e6db74 } /* Literal.String.Other */ - .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ - .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ - .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ - .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ - .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ - .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ - .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ - .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ -} diff --git a/vendor/gems/graphql/guides/dataloader/adopting.md b/vendor/gems/graphql/guides/dataloader/adopting.md deleted file mode 100644 index c7f78d0ec24..00000000000 --- a/vendor/gems/graphql/guides/dataloader/adopting.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Dataloader vs. GraphQL-Batch -desc: Comparing and Contrasting Batch Loading Options -index: 3 ---- - -{{ "GraphQL::Dataloader" | api_doc }} solves the same problem as [`GraphQL::Batch`](https://github.com/shopify/graphql-batch). There are a few major differences between the modules: - - -- __Concurrency Primitive:__ GraphQL-Batch uses `Promise`s from [`promise.rb`](https://github.com/lgierth/promise.rb); GraphQL::Dataloader uses Ruby's [`Fiber` API](https://ruby-doc.org/core-3.0.0/Fiber.html). These primitives dictate how batch loading code is written (see below for comparisons). -- __Maturity:__ Frankly, GraphQL-Batch is about as old as GraphQL-Ruby, and it's been in production at Shopify, GitHub, and others for many years. GraphQL::Dataloader is new, and although Ruby has supported `Fiber`s since 1.9, they still aren't widely used. -- __Scope:__ It's not currently possible to use `GraphQL::Dataloader` _outside_ GraphQL. - -The incentive in writing `GraphQL::Dataloader` was to leverage `Fiber`'s ability to _transparently_ pause and resume work, which removes the need for `Promise`s (and removes the resulting complexity in the code). Additionally, `GraphQL::Dataloader` should _eventually_ support Ruby 3.0's `Fiber.scheduler` API, which runs I/O in the background by default. - -## Comparison: Fetching a single object - -In this example, a single object is batch-loaded to satisfy a GraphQL field. - -- With __GraphQL-Batch__, you call a loader, which returns a `Promise`: - - ```ruby - record_promise = Loaders::Record.load(1) - ``` - - Then, under the hood, GraphQL-Ruby manages the promise (using its `lazy_resolve` feature, upstreamed from GraphQL-Batch many years ago). GraphQL-Ruby will call `.sync` on it when no further execution is possible; `promise.rb` implements `Promise#sync` to execute the pending work. - -- With __GraphQL::Dataloader__, you get a source, then call `.load` on it, which may pause the current Fiber, but it returns the requested object. - - ```ruby - dataloader.with(Sources::Record).load(1) - ``` - - Since the requested object is (eventually) returned from `.load`, Nothing else is required. - -## Comparison: Fetching objects in sequence (dependent) - -In this example, one object is loaded, then another object is loaded _based on_ the first one. - -- With __GraphQL-Batch__, `.then { ... }` is used to join dependent code blocks: - - ```ruby - Loaders::Record.load(1).then do |record| - Loaders::OtherRecord.load(record.other_record_id) - end - ``` - - That call returns a `Promise`, which is stored by GraphQL-Ruby, and finally `.sync`ed. - -- With __GraphQL-Dataloader__, `.load(...)` returns the requested object (after a potential `Fiber` pause), so no other method calls are necessary: - - ```ruby - record = dataloader.with(Sources::Record).load(1) - dataloader.with(Sources::OtherRecord).load(record.other_record_id) - ``` - -## Comparison: Fetching objects concurrently (independent) - -Sometimes, you need multiple _independent_ records to perform a calculation. Each record is loaded, then they're combined in some bit of work. - -- With __GraphQL-Batch__, `Promise.all(...)` is used to to wait for several pending loads: - - ```ruby - promise_1 = Loaders::Record.load(1) - promise_2 = Loaders::OtherRecord.load(2) - Promise.all([promise_1, promise_2]).then do |record, other_record| - do_something(record, other_record) - end - ``` - - If the objects are loaded from the same loader, then `.load_many` also works: - - ```ruby - Loaders::Record.load_many([1, 2]).then do |record, other_record| - do_something(record, other_record) - end - ``` - -- With __GraphQL::Dataloader__, each request is registered with `.request(...)` (which never pauses the Fiber), then data is loaded with `.load` (which will pause the Fiber as needed): - - ```ruby - # first, make some requests - request_1 = dataloader.with(Sources::Record).request(1) - request_2 = dataloader.with(Sources::OtherRecord).request(2) - # then, load the objects and do something - record = request_1.load - other_record = request_2.load - do_something(record, other_record) - ``` - - If the objects come from the same `Source`, then `.load_all` will return the objects directly: - - ```ruby - record, other_record = dataloader.with(Sources::Record).load_all([1, 2]) - do_something(record, other_record) - ``` diff --git a/vendor/gems/graphql/guides/dataloader/async_dataloader.md b/vendor/gems/graphql/guides/dataloader/async_dataloader.md deleted file mode 100644 index f725602c9a2..00000000000 --- a/vendor/gems/graphql/guides/dataloader/async_dataloader.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Async Source Execution -desc: Using AsyncDataloader to fetch external data in parallel -index: 5 ---- - -`AsyncDataloader` will run {{ "GraphQL::Dataloader::Source#fetch" | api_doc }} calls in parallel, so that external service calls (like database queries or network calls) don't have to wait in a queue. - -To use `AsyncDataloader`, hook it up in your schema _instead of_ `GraphQL::Dataloader`: - -```diff -- use GraphQL::Dataloader -+ use GraphQL::Dataloader::AsyncDataloader -``` - -__Also__, add [the `async` gem](https://github.com/socketry/async) to your project, for example: - -``` -bundle add async -``` - -Now, {{ "GraphQL::Dataloader::AsyncDataloader" | api_doc }} will create `Async::Task` instances instead of plain `Fiber`s and the `async` gem will manage parallelism. - -For a demonstration of this behavior, see: [https://github.com/rmosolgo/rails-graphql-async-demo](https://github.com/rmosolgo/rails-graphql-async-demo) - -_You can also implement {% internal_link "manual parallelism", "/dataloader/parallelism" %} using `dataloader.yield`._ - -## Rails - -For Rails, you'll need **Rails 7.1**, which properly supports fiber-based concurrency, and you'll also want to configure Rails to use Fibers for isolation: - -```ruby -class Application < Rails::Application - # ... - config.active_support.isolation_level = :fiber -end -``` -### ActiveRecord Connections - -You can use Dataloader's {% internal_link "Fiber lifecycle hooks", "/dataloader/dataloader#fiber-lifecycle-hooks" %} to improve ActiveRecord connection handling: - -- In Rails < 7.2, connections are not reused when a Fiber exits; instead, they're only reused when a request or background job finishes. You can add manual `release_connection` calls to improve this. -- With `isolation_level = :fiber`, new Fibers don't inherit `connected_to ...` settings from their parent fibers. - -Altogether, it can be improved like this: - -```ruby -def get_fiber_variables - vars = super - # Collect the current connection config to pass on: - vars[:connected_to] = { - role: ActiveRecord::Base.current_role, - shard: ActiveRecord::Base.current_shard, - prevent_writes: ActiveRecord::Base.current_preventing_writes - } - vars -end - -def set_fiber_variables(vars) - connection_config = vars.delete(:connected_to) - # Reset connection config from the parent fiber: - ActiveRecord::Base.connecting_to(**connection_config) - super(vars) -end - -def cleanup_fiber - super - # Release the current connection - ActiveRecord::Base.connection_pool.release_connection -end -``` - -Modify the example according to your database configuration and abstract class hierarchy. - -## Other Options - -You can also manually implement parallelism with Dataloader. See the {% internal_link "Dataloader Parallelism", "/dataloader/parallelism" %} guide for details. diff --git a/vendor/gems/graphql/guides/dataloader/dataloader.md b/vendor/gems/graphql/guides/dataloader/dataloader.md deleted file mode 100644 index bde06aba0f4..00000000000 --- a/vendor/gems/graphql/guides/dataloader/dataloader.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Dataloader -desc: The Dataloader orchestrates Fibers and Sources -index: 2 ---- - -{{ "GraphQL::Dataloader" | api_doc }} instances are created for each query (or multiplex) and they: - -- Cache {% internal_link "Source", "/dataloader/sources" %} instances for the duration of GraphQL execution -- Run pending Fibers to resolve data requirements and continue GraphQL execution - -During a query, you can access the dataloader instance with: - -- {{ "GraphQL::Query::Context#dataloader" | api_doc }} (`context.dataloader`, anywhere that query context is available) -- {{ "GraphQL::Schema::Object#dataloader" | api_doc }} (`dataloader` inside a resolver method) -- {{ "GraphQL::Schema::Resolver#dataloader" | api_doc }} (`dataloader` inside `def resolve` of a Resolver, Mutation, or Subscription class.) - -## Fiber Lifecycle Hooks - -Under the hood, `Dataloader` creates Fibers as-needed and uses them to run GraphQL and load data from `Source` classes. You can hook into these Fibers through several lifecycle hooks. To implement these hooks, create a custom subclass and provide new implementation for these methods: - -```ruby -class MyDataloader < GraphQL::Dataloader # or GraphQL::Dataloader::AsyncDataloader - # ... -end -``` - -Then, use your customized dataloader instead of the built-in one: - -```diff - class MySchema < GraphQL::Schema -- use GraphQL::Dataloader -+ use MyDataloader - end -``` - -- __{{ "GraphQL::Dataloader#get_fiber_variables" | api_doc }}__ is called before creating a Fiber. By default, it returns a hash containing the parent Fiber's variables (from `Thread.current[...]`). You can add to this hash in your own implementation of this method. -- __{{ "GraphQL::Dataloader#set_fiber_variables" | api_doc }}__ is called inside the new Fiber. It's passed the hash returned from `get_fiber_variables`. You can use this method to initialize "global" state inside the new Fiber. -- __{{ "GraphQL::Dataloader#cleanup_fiber" | api_doc }}__ is called just before a Dataloader Fiber exits. You can use this methods to teardown any state that you prepared in `set_fiber_variables`. diff --git a/vendor/gems/graphql/guides/dataloader/overview.md b/vendor/gems/graphql/guides/dataloader/overview.md deleted file mode 100644 index e842ae94220..00000000000 --- a/vendor/gems/graphql/guides/dataloader/overview.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Overview -desc: Getting started with the Fiber-based Dataloader -index: 0 ---- - - {{ "GraphQL::Dataloader" | api_doc }} provides efficient, batched access to external services, backed by Ruby's `Fiber` concurrency primitive. It has a per-query result cache and {% internal_link "AsyncDataloader", "/dataloader/async_dataloader" %} supports truly parallel execution out-of-the-box. - -`GraphQL::Dataloader` is inspired by [`@bessey`'s proof-of-concept](https://github.com/bessey/graphql-fiber-test/tree/no-gem-changes) and [shopify/graphql-batch](https://github.com/shopify/graphql-batch). - -## Batch Loading - -`GraphQL::Dataloader` facilitates a two-stage approach to fetching data from external sources (like databases or APIs): - -- First, GraphQL fields register their data requirements (eg, object IDs or query parameters) -- Then, after as many requirements have been gathered as possible, `GraphQL::Dataloader` initiates _actual_ fetches to external services - -That cycle is repeated during execution: data requirements are gathered until no further GraphQL fields can be executed, then `GraphQL::Dataloader` triggers external calls based on those requirements and GraphQL execution resumes. - -## Fibers - -`GraphQL::Dataloader` uses Ruby's `Fiber`, a lightweight concurrency primitive which supports application-level scheduling _within_ a `Thread`. By using `Fiber`, `GraphQL::Dataloader` can pause GraphQL execution when data is requested, then resume execution after the data is fetched. - -At a high level, `GraphQL::Dataloader`'s usage of `Fiber` looks like this: - -- GraphQL execution is run inside a Fiber. -- When that Fiber returns, if the Fiber was paused to wait for data, then GraphQL execution resumes with the _next_ (sibling) GraphQL field inside a new Fiber. -- That cycle continues until no further sibling fields are available and all known Fibers are paused. -- `GraphQL::Dataloader` takes the first paused Fiber and resumes it, causing the `GraphQL::Dataloader::Source` to execute its `#fetch(...)` call. That Fiber continues execution as far as it can. -- Likewise, paused Fibers are resumed, causing GraphQL execution to continue, until all paused Fibers are evaluated completely. - -Whenever `GraphQL::Dataloader` creates a new `Fiber`, it copies each pair from `Thread.current[...]` and reassigns them inside the new `Fiber`. - -`AsyncDataloader`, built on top of the [`async` gem](https://github.com/socketry/async), supports parallel I/O operations (like network and database communication) via Ruby's non-blocking `Fiber.schedule` API. {% internal_link "Learn more →", "/dataloader/async_dataloader" %}. - -## Getting Started - -To install {{ "GraphQL::Dataloader" | api_doc }}, add it to your schema with `use ...`, for example: - -```ruby -class MySchema < GraphQL::Schema - # ... - use GraphQL::Dataloader -end -``` - -Then, inside your schema, you can request batch-loaded objects by their lookup key with `dataloader.with(...).load(...)`: - -```ruby -field :user, Types::User do - argument :handle, String -end - -def user(handle:) - dataloader.with(Sources::UserByHandle).load(handle) -end -``` - -Or, load several objects by passing an array of lookup keys to `.load_all(...)`: - -```ruby -field :is_following, Boolean, null: false do - argument :follower_handle, String - argument :followed_handle, String -end - -def is_following(follower_handle:, followed_handle:) - follower, followed = dataloader - .with(Sources::UserByHandle) - .load_all([follower_handle, followed_handle]) - - followed && follower && follower.follows?(followed) -end -``` - -To prepare requests from several sources, use `.request(...)`, then call `.load` after all requests are registered: - -```ruby -class AddToList < GraphQL::Schema::Mutation - argument :handle, String - argument :list, String, as: :list_name - - field :list, Types::UserList - - def resolve(handle:, list_name:) - # first, register the requests: - user_request = dataloader.with(Sources::UserByHandle).request(handle) - list_request = dataloader.with(Sources::ListByName, context[:viewer]).request(list_name) - # then, use `.load` to wait for the external call and return the object: - user = user_request.load - list = list_request.load - # Now, all objects are ready. - list.add_user!(user) - { list: list } - end -end -``` - -### `loads:` and `object_from_id` - -`dataloader` is also available as `context.dataloader`, so you can use it to implement `MySchema.object_from_id`. For example: - -```ruby -class MySchema < GraphQL::Schema - def self.object_from_id(id, ctx) - model_class, database_id = IdDecoder.decode(id) - ctx.dataloader.with(Sources::RecordById, model_class).load(database_id) - end -end -``` - -Then, any arguments with `loads:` will use that method to fetch objects. For example: - -```ruby -class FollowUser < GraphQL::Schema::Mutation - argument :follow_id, ID, loads: Types::User - - field :followed, Types::User - - def resolve(follow:) - # `follow` was fetched using the Schema's `object_from_id` hook - context[:viewer].follow!(follow) - { followed: follow } - end -end -``` - -## Data Sources - -To implement batch-loading data sources, see the {% internal_link "Sources guide", "/dataloader/sources" %}. - -## Parallelism - -You can run I/O operations in parallel with GraphQL::Dataloader. There are two approaches: - -- `AsyncDataloader` uses the `async` gem to automatically background I/O from `Dataloader::Source#fetch` calls. {% internal_link "Read More", "/dataloader/async_dataloader" %} -- You can manually call `dataloader.yield` after starting work in the background. {% internal_link "Read More", "/dataloader/parallelism" %} diff --git a/vendor/gems/graphql/guides/dataloader/parallelism.md b/vendor/gems/graphql/guides/dataloader/parallelism.md deleted file mode 100644 index a0634bb7bf4..00000000000 --- a/vendor/gems/graphql/guides/dataloader/parallelism.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Manual Parallelism -desc: Yield to Dataloader after starting work -index: 7 ---- - -You can coordinate with {{ "GraphQL::Dataloader" | api_doc }} to run tasks in the background. To do this, call `dataloader.yield` inside `Source#fetch` after kicking off your task. For example: - -```ruby -def fetch(ids) - # somehow queue up a background query, - # see examples below - future_result = async_query_for(ids) - # return control to the dataloader - dataloader.yield - # dataloader will come back here - # after calling other sources, - # now wait for the value - future_result.value -end -``` - -_Alternatively, you can use {% internal_link "AsyncDataloader", "/dataloader/async_dataloader" %} to automatically background I/O inside `Source#fetch` calls._ - -## Example: Rails load_async - -You can use Rails's `load_async` method to load `ActiveRecord::Relation`s in the background. For example: - -```ruby -class Sources::AsyncRelationSource < GraphQL::Dataloader::Source - def fetch(relations) - relations.each(&:load_async) # start loading them in the background - dataloader.yield # hand back to GraphQL::Dataloader - relations.each(&:load) # now, wait for the result, returning the now-loaded relation - end -end -``` - -You could call that source from a GraphQL field method: - -```ruby -field :direct_reports, [Person] - -def direct_reports - # prepare an ActiveRecord::Relation: - direct_reports = Person.where(manager: object) - # pass it off to the source: - dataloader - .with(Sources::AsyncRelationSource) - .load(direct_reports) -end -``` - -## Example: Rails async calculations - -In a Dataloader source, you can run Rails async calculations in the background while other work continues. For example: - -```ruby -class Sources::DirectReportsCount < GraphQL::Dataloader::Source - def fetch(users) - # Start the queries in the background: - promises = users.map { |u| u.direct_reports.async_count } - # Return to GraphQL::Dataloader: - dataloader.yield - # Now return the results, waiting if necessary: - promises.map(&:value) - end -end -``` - -Which could be used in a GraphQL field: - -```ruby -field :direct_reports_count, Int - -def direct_reports_count - dataloader.with(Sources::DirectReportsCount).load(object) -end -``` - -## Example: Concurrent::Future - -You could use `concurrent-ruby` to put work in a background thread. For example, using `Concurrent::Future`: - -```ruby -class Sources::ExternalDataSource < GraphQL::Dataloader::Source - def fetch(urls) - # Start some I/O-intensive work: - futures = urls.map do |url| - Concurrent::Future.execute { - # Somehow fetch and parse data: - get_remote_json(url) - } - end - # Yield back to GraphQL::Dataloader: - dataloader.yield - # Dataloader has done what it can, - # so now return the value, waiting if necessary: - futures.map(&:value) - end -end -``` diff --git a/vendor/gems/graphql/guides/dataloader/sources.md b/vendor/gems/graphql/guides/dataloader/sources.md deleted file mode 100644 index 0dc4b03ddb8..00000000000 --- a/vendor/gems/graphql/guides/dataloader/sources.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Sources -desc: Batch-loading objects for GraphQL::Dataloader -index: 1 ---- - -_Sources_ are what {{ "GraphQL::Dataloader" | api_doc }} uses to fetch data from external services. - -## Source Concepts - -Sources are classes that inherit from `GraphQL::Dataloader::Source`. A Source _must_ implement `def fetch(keys)` to return a list of objects, one for each of the given keys. A source _may_ implement `def initialize(...)` to accept other batching parameters. - -Sources will receive two kinds of inputs from `GraphQL::Dataloader`: - -- _keys_, which correspond to objects requested by the application. - - Keys are passed to `def fetch(keys)`, which must return an object (or `nil`) for each of `keys`, in the same order as `keys`. - - Under the hood, each Source instance maintains a `key => object` cache. - -- _batch parameters_, which are the basis of batched groups. For example, if you're loading records from different database tables, the table name would be a batch parameter. - - Batch parameters are given to `dataloader.with(source_class, *batch_parameters)`, and the default is _no batch parameters_. When you define a source, you should add the batch parameters to `def initialize(...)` and store them in instance variables. - - (`dataloader.with(source_class, *batch_parameters)` returns an instance of `source_class` with the given batch parameters -- but it might be an instance which was cached by `dataloader`.) - - Additionally, batch parameters are used to de-duplicate Source initializations during a query run. `.with(...)` calls that have the same batch parameters will use the same Source instance under the hood. To customize how Sources are de-duplicated, see {{ "GraphQL::Dataloader::Source.batch_key_for" | api_doc }}. - -## Example: Loading Strings from Redis by Key - -The simplest source might fetch values based on their keys. For example: - -```ruby -# app/graphql/sources/redis_string.rb -class Sources::RedisString < GraphQL::Dataloader::Source - REDIS = Redis.new - def fetch(keys) - # Redis's `mget` will return a value for each key with a `nil` for any not-found key. - REDIS.mget(*keys) - end -end -``` - -This loader could be used in GraphQL like this: - -```ruby -some_string = dataloader.with(Sources::RedisString).load("some_key") -``` - -Calls to `.load(key)` will be batched, and when `GraphQL::Dataloader` can't go any further, it will dispatch a call to `def fetch(keys)` above. - -## Example: Loading ActiveRecord Objects by ID - -To fetch ActiveRecord objects by ID, the source should also accept the _model class_ as a batching parameter. For example: - -```ruby -# app/graphql/sources/active_record_object.rb -class Sources::ActiveRecordObject < GraphQL::Dataloader::Source - def initialize(model_class) - @model_class = model_class - end - - def fetch(ids) - records = @model_class.where(id: ids) - # return a list with `nil` for any ID that wasn't found - ids.map { |id| records.find { |r| r.id == id.to_i } } - end -end -``` - -This source could be used for any `model_class`, for example: - -```ruby -author = dataloader.with(Sources::ActiveRecordObject, ::User).load(1) -post = dataloader.with(Sources::ActiveRecordObject, ::Post).load(1) -``` - -## Example: Batched Calculations - -Besides fetching objects, Sources can return values from batched calculations. For example, a system could batch up checks for who a user follows: - -```ruby -# for a given user, batch checks to see whether this user follows another user. -# (The default `user.followings.where(followed_user_id: followed).exists?` would cause N+1 queries.) -class Sources::UserFollowingExists < GraphQL::Dataloader::Source - def initialize(user) - @user = user - end - - def fetch(handles) - # Prepare a `SELECT id FROM users WHERE handle IN(...)` statement - user_ids = ::User.where(handle: handles).select(:id) - # And use it to filter this user's followings: - followings = @user.followings.where(followed_user_id: user_ids) - # Now, for followings that _actually_ hit a user, get the handles for those users: - followed_users = ::User.where(id: followings.select(:followed_user_id)) - # Finally, return a result set, with one entry (true or false) for each of the given `handles` - handles.map { |h| !!followed_users.find { |u| u.handle == h }} - end -end -``` - -It could be used like this: - -```ruby -is_following = dataloader.with(Sources::UserFollowingExists, context[:viewer]).load(handle) -``` - -After all requests were batched, `#fetch` will return a Boolean result to `is_following`. - -## Example: Loading in a background thread - -Inside `Source#fetch(keys)`, you can call `dataloader.yield` to return control to the Dataloader. This way, it will proceed loading other Sources (if there are any), then return the source that yielded. - -A simple example, spinning up a new Thread: - -```ruby -def fetch(keys) - # spin up some work in a background thread - thread = Thread.new { - fetch_external_data(keys) - } - # return control to the dataloader - dataloader.yield - # at this point, - # the dataloader has tried everything else and come back to this source, - # so block if necessary: - thread.value -end -``` - -See the {% internal_link "parallelism guide", "/dataloader/parallelism" %} for details about this approach. - -## Filling the Dataloader Cache - -If you load records from the database, you can use them to populate a source's cache by using {{ "Dataloader::Source#merge" | api_doc }}. For example: - -```ruby -# Build a `{ key => value }` map to populate the cache -comments_by_id = post.comments.each_with_object({}) { |comment, hash| hash[comment.id] = comment } -# Merge the map into the source's cache -dataloader.with(Sources::ActiveRecordObject, Comment).merge(comments_by_id) -``` - -After that, any calls to `.load(id)` will use those already-loaded records if they're available. - -## De-duplicating equivalent objects - -Sometimes, _different_ objects in the application should load the same object from `fetch`. You can customize this behavior by implementing `def result_key_for(key)` in your application. For example, to map records from your ORM to their database ID: - -```ruby -# Load the `created_by` person for a record from our database -class CreatedBySource < GraphQL::Dataloader::Source - def result_key_for(key) - key.id # Use the record's ID to deduplicate different `.load` calls - end - - # Fetch a `person` for each of `records`, based on their created_by_id - def fetch(records) - PersonService.find_each(records.map(&:created_by_id)) - end -end -``` - -In this case, `records` will include the _first_ object for each unique `record.id` -- subsequent records with the same `.id` will be assumed to be duplicates. Under the hood, the `Source` will cache the result based on the record's `id`. - -Alternatively, you could use this to make the `Source` retain each incoming object, even when they would _otherwise_ be treated as duplicates. (This would come in handy when you need `def fetch` to mutate each object). For example, to treat _every_ incoming object as distinct: - -```ruby -def result_key_for(record) - record.object_id # even if the records are equivalent, handle each distinct Ruby object separately -end -``` diff --git a/vendor/gems/graphql/guides/dataloader/testing.md b/vendor/gems/graphql/guides/dataloader/testing.md deleted file mode 100644 index 2f5bd1a6f96..00000000000 --- a/vendor/gems/graphql/guides/dataloader/testing.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -layout: guide -search: true -section: Dataloader -title: Testing -desc: Tips for testing Dataloader implementation -index: 4 ---- - -There are a few techniques for testing your {{ "GraphQL::Dataloader" | api_doc }} setup. - -## Integration Tests - -One important feature of `Dataloader` is how it manages database access while GraphQL runs queries. You can test that by listening for database queries while running queries, for example, with ActiveRecord: - - -```ruby -def test_active_record_queries_are_batched_and_cached - # set up a listener function - database_queries = 0 - callback = lambda {|_name, _started, _finished, _unique_id, _payload| database_queries += 1 } - - query_str = <<-GRAPHQL - { - a1: author(id: 1) { name } - a2: author(id: 2) { name } - b1: book(id: 1) { author { name } } - b2: book(id: 2) { author { name } } - } - GRAPHQL - - # Run the query with the listener - ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do - MySchema.execute(query_str) - end - - # One query for authors, one query for books - assert_equal 2, database_queries -end -``` - -You could also make specific assertions on the queries that are run (see the [`sql.active_record` docs](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#active-record)). For other frameworks and databases, check your ORM or library for instrumentation options. - -## Testing Dataloader Sources - -You can also test `Dataloader` behavior outside of GraphQL using {{ "GraphQL::Dataloader.with_dataloading" | api_doc }}. For example, let's if you have a `Sources::ActiveRecord` source defined like so: - -```ruby - -module Sources - class User < GraphQL::Dataloader::Source - def fetch(ids) - records = User.where(id: ids) - # return a list with `nil` for any ID that wasn't found, so the shape matches - ids.map { |id| records.find { |r| r.id == id.to_i } } - end - end -end -``` - -You can test it like so: - -```ruby -def test_it_fetches_objects_by_id - user_1, user_2, user_3 = 3.times.map { User.create! } - - database_queries = 0 - callback = lambda {|_name, _started, _finished, _unique_id, _payload| database_queries += 1 } - - ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do - GraphQL::Dataloader.with_dataloading do |dataloader| - req1 = dataloader.with(Sources::ActiveRecord).request(user_1.id) - req2 = dataloader.with(Sources::ActiveRecord).request(user_2.id) - req3 = dataloader.with(Sources::ActiveRecord).request(user_3.id) - req4 = dataloader.with(Sources::ActiveRecord).request(-1) - - # Validate source's matching up of records - expect(req1.load).to eq(user_1) - expect(req2.load).to eq(user_2) - expect(req3.load).to eq(user_3) - expect(req4.load).to be_nil - end - end - - assert_equal 1, database_queries, "All users were looked up at once" -end -``` diff --git a/vendor/gems/graphql/guides/defer/defer-graphiql-gif.gif b/vendor/gems/graphql/guides/defer/defer-graphiql-gif.gif deleted file mode 100644 index db542e19dbaf22e06a92fdce97d1f097b16d3314..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82127 zcmd3t_ct8Q*T;9)F1uD)ooFliuCjU;J$mm%5JX8tuOX|iUL!;oz4uP^7DNw%=n*Aa z2xED=ghh1%*>DPGjs3zzUHBZnxvFnAvho84*;;?;kv5EM#_r%s=@*o0wCbO zr8_Wy<-f@H-vs>cSj>NsnM;ffB_kmr!78M}BB8-7t<5Z_&%v+AiBaMbRY!}dqD3@$ zB((*^4bgJOLekozvWCJ^mf~`@QV$#%l}s2_%^B3K8FcOFEZrIO9q5f+XstbIZT)DU z1W-GN3Mg5ND;P>C8Oy7g%d1-ms@MuWv=`QJ5Ycj$Rebh9#Y0TrUCPu?$}B*}@}-fe zzLM4>6&+heH6K-tU^RU^O(REbQ)gWZHv_BZ#*aKqZM`h)eXX4WY@CDbTtmo%)5%|D zQM+I%eB$M7!sKluG<70$_2UeUQ%%jYt*wg{?4lGLVwIeeRNT@Zdc4)}&eilS)Cw%s z4XLuREq8FoI(bAmdquhV;@kt`J%f{cLR0PRY8@S$pFZtye%j&Y+WXvXz{7pi%VW~d zXD+~ZIV5oN)yo~S@Emx22|THSIIV^#x1Bh<1(x4QSkwc3*GE`30I3=U*G&>MPJ>$K zL7mIM-Zen~CSY(&4_jv#*5rQyu2`RaN5t$XKhkDh-el8;LzZL6f8w9C78Ja8*j z^%zn18qf-!R`Z`x3z&TvxUBwiMKdHUWIyuNw{r!PzK&x*q0^COaqqEg=B(#qm9E0eNnQr_03=QX}AY{@R^EKhq|m7WuYJ&p-K zkBz)cjQN#>`;!ufFKZl~dR@7m-FTSWjt{HHC$!-+drn&?@P$MFswOrEr_M*0@cp0g zOM9oMr}(4G>+9=3mpAwyxBveA`=9*&e+(!d0D&^8Hxv#;K&epH;|)baF$i|eT=mA{ zk$8F$*Y)wncVo#&jM+Vur}Kg$ko;o9V7=g@uPyd|5i(ZKt0*ypK1hyW7vc^hdy1b$dE4cE{7vk7s&1 zufEQ{(<;#I?Ycf*X?FWE)7$NT0+S!av!H%=-(4LpR6qXM*ZceD`R<1Tz4v{8e=iKZ zU?Qh}kH-TDdA6`%TIa1WC|AW+c)QDRfgXt(&vqoWsq=Oey;H?@^tI!ULM6$5;SV^D zOi3-YP{8nZ9ADevPCRCmXE#9< zAv(Opu`iRLifX;zzOEHUrkQ;$!g0B7f&%%`mX`<_MivFa9f~20o`rQBQhQ zb<)K1_vobAZgUM$1DYTvK(5%w6DW@AmM}JNMWZQO_A-whR2GSmXs~3<=Q`-Rrlo=V z&a&H?+QC8fi^N0XV?-mdFC#@$-KN|50L5wb`5>QWw|*CZn**cLDV1=XZHg-p#W+58W*b zNe4g@OnKsb9J2>RYZ!n0Fa7b9G`_h{c65Dwt2i`D>2pg$)(y>c>tAtq)7hO#5C)Sk z&flnl`MlZkUQ_g(uVT09oAQv zF~C~mVkTrwhz}96+hVTx^Ka(Y=dX4X(xaAW`#XM57rRUC)%4-rc~p zXN3eUq#ojO3XCLpTI9fKs(_me?-!ysxJ){0CF%-XF?G562+Gia`b_{(Rb^b2B-KNz z*wQ(WcM30gvQd$K_OY9bLXbmYDwYtjb z-89=b=~&9_arH*)!MKzJ4h}(sCIL%BMbU8%Qu6^~oq2$i-8%46JcdAOzL=&ZtRLzM zAYhzIrY)7V{7s7pJElmXZ6vm}QcRHoD6WaH_%KRCQLpXv%<0-k@#;C#NL0XKKB%iSk=ik9^M zFwy8ugwX#CI+T)GGftvaMenec`%YzXEEHgTC*fdCBeW~UQ^M(Q*uIY3#CdyM^PC;y zK;X>i2HA54h|TAQ@|ol&qb#l#>o3SzahtW~EIw8Hs#}JIIweS<7%3XX6Kj<1#+U90Pa&>_8Uon#~sq^h;m*a7aTU#{zQ#Ool{pO}S zSGbK26I#8R7DWmMHFh+Rk(o)0-z6qe+)-?NakQTxJ(SVEa@efC-hA@QU-VuKK0dH5 zYInn1es3IIta9jln|@D!fcQ13fD$Htb=vQy<05s#+wYucw0DO%M8=BMa6-)zP#jja z$Fyb(Ts)M*R7qtOn1nrrtQM1oEp0HN!qWBfwc9vfod8K9wH}8f%xXqe>q3R7!@ppV zV5$sfhE#$u1c356r;cv$&+M8c6ud3 zRQpNT8vU5Qp8-t+a`HAKpq-m=v7o}%A)M!9z~~byC8P&rfPb}mmU}=eX&Vg*IG-D*Oe;r84*5m5~ zB8gd1DK!+%%~>V5%PdZf>xIP5VYq(KNT9enNM-h^mJ#iOXjpXFh1Ig&=U-+;e%~s@ z43EN%w~5nCq1n?XN1=m3yt5d!Xt$7Kr+U;Uj9rULkPbq#{2rV7jd!D6&`F(W1ik2K zOY_(xZs+$ZYc}x%k@W}yN~V$zut3j*_S28<$E6HI(29?Ble} zr+$dvh+GezJ1Tg8`I~h6=HEsdwqP$=w!?3b!R0LaLGCIgd1nsK3}t*l3E_ME=B=9|1w@#3Q|Bq zFMat|%j~Bae}>jxi|**|ExP+Zbr(0j&*?|;#&!Sflj&1^W#+I&q!$=L{qG!SCR(|p!Dj*K|%!4l3P>-{cyWNL~jFRWsIst0)T6$ zF4ceOLKljtKXpy2tvS8ZJ{Oe@LB(GUP%OW7T(za z!E`{{(_WEZzzDJskN~WVr$vebar9IF@j`6-IXq1tmOG1`EwQMk3)9B2Qb8$cco1TU zC(7b4*up?s2v{T*#DzhGAzz@X2ph2884DJQuQQJi3y9#u#Sh_dQbI1mp?sO#y+h098c%C4Kl! z7NSQX0c4Q;Gb?$*9PxM-4z!4deoGK9!Wo18=Ja7%SXjRY-0uxZ2~z*fVd9o~+Sx#? ze7NAAImN3M0%m%cW`9zpB;sY6L)8LY7_X18?}roE5ssTD+iU>{8v&9UDV;bl#4`ol zm@XHa0Aj;|2jE>8M2vc>#GH1DC~@8v>>Y+U%QkTiLk#590XwESxj3r%Q?ThnxIj=| zB@|kUg2WbLQW(mz9VQfrI5kJBGQ1YZf_HAk5nQD7V&SS=jv;|D))-*IY7D3-gJ1yu ziXp3aHpPDhVl0(v%0NZSt6El=Shf|>kD=;EW_}b+(+bF}T4kPmO3*w@^rBKg=KSqf zNgWG99ur%LsU&3pZ#s8YIyfv|7?V5nEn38uu&Y8imKX8fIfaThcNmtdSDAocfm^3! z=iDXeacGnj!d~0La`?gTe~?17cvVh!kD~xkh1_Bq5{`Uf7>&lAWha0l0S$uosih z@{74%f|G*)C?JJ^1EA+l`5NUE??(v|9n(1mpotA&Q4|8E09H{5SLp|VFOtM?aA9N! zh%LbpQBr_OcGNEsK@xtrgDId&mO2ZkvOzNs35}&odJ9V)Y9ciHUp?bNJeq_E>l4z& z6}`A9a-MwaB1KULcmWXMy#AKRtmuI@CEyAJ;S|E7kO)3Z9EUnW$`(?yf}q3@*6|Sj zG9&CzCse}o0#FESS3g_>kr2HG$NkE>Qb!DC#p`B+;@02^M=`IZD?!_k&VD#~Wwuin zt;<@~DlkaeD~V!(sNY0Gk&DJJ*1iixiRh~G->&MnWPUZqB?*9nFfS;ca9UBn>07H| zqOO_!#q?YP)PMlV+d|j?@KXjMAprbCP|nP~x^&9gy-B85PazGqkkFL6M*I6|UefUG z*qx)Aeam{eOsmv=garoPXj{WY%<-(armd^^%Cg~$Ydus;(M?n6d;$@YQmpi}fik!O zX4TjW3~IKMmQ-nM5o%tLzsa3Rn$sYk7YHR4tcQdTXZ@%0a)DU2@P4m;(vYx2yrhZD1fhyZXzb|DO& zgiKoqnsWkYB)C($#oWF{3Y`R>Z4qHCDDeuF$H;IGgt}G`#bTjBh&Gb#EZVSkkmsx0 z@>dg;2s|Bj(FNh?iXg-idSHm$W`R&oBF|Zbq6blcEl~gxY!451h;L?>X_L01Fhf=- zJ2my(g@NY)$>-3Qw$RiD=u6&KtH^NhUAT~B1OoUPYR~}&g8e;+d=-e&atMjGJ4l6y z{A~ds+nzU{gR?(3X5ITVN$Iy04#h9n~!-BTkQg>*K zmn34#Eru3VsyrD(co9Qr*9}(05mq6*F+_CzxjtB;dl&@mED(n50dE7xwGe(7BB)|N z*Z@G|0RYVbGu_IvOL{&qHcfx#QbJRRdO&sl*~Vu_rp>`hV# zdQqcK5*i$3>poghymuND%zi@{21@NGf*7RRH0Of)GeFzvuvNICRL0DD_`~cBD%dm# zIZeR!9)x_olKr2WhFxumlC_BZ6ef=O5FqWIN7@savIrt;#5o`0_0({B_2kvF*X)I1 zVhsZ(3Iu^l1fEANz0d03le`&_6-1SJBIj}l=Kx8wP`_$~3U6MrRCf5c!aC~`TlI!&x7cvR*%1JKlE9@Y{>iW z>W>c^s`$GA(0&3mZM`|PhoN^pbLV4 zlt~-eUKxzT7l&dq+j2SmVpF-LYs;mNzgq4Iy?EtA!0B0Vx4M+D1+|ky{8=3!4l4w8 zeP}a(U2_@@)EuX}D}-LaC^Zq(u0;a;h(LaX|KHDk`a}d5Grl2+Yi-1EJR)v-#Yq~3 zR-46ILZDcH31E#Radp3H)#U@lEBqb-#)H5V@Pv!)ooE*rGzYaD2cP8ig0|n0ZI>uY zC#$%Ys8^L}SIu3LmQFK-8&ASbkCH+9Uql*83Fb;g6#DF>%aUX#{ShpF4a4Pp01qsY zOA64de*&~U;nfdhK@ce_tbhl$S;%%ko&Xmt(E}{dbhTPuVFRq_1nwOmB5hhr%wG>_ zdRR??!Auz=mhOj@2cZxtTje@{itt~EsGy42u8PE86^6W(8IyZinnOJ+l?C^z+9az8 z?5Zl(wm|*B0FOyMZGb;w7>pS{n)>9NGVF&KxMf-d&mEZlIRK@O_`1KAw=Eay%aKvo zSk5DGdbzvzb{Cr1#EjowUJ*@8Aom^~%d8)=Q705yC$H2b%+9|OFvs673_~vyfX+we+c8un!Nl*zkSzRK0U6eJ0YAS82jGyBfsqHKWuK^nXq3{ zZ)kHT-#G!k2%;x{n{@ujm*CPJ;mY@o+^!v#8ciTbg{jsQZc;bc$eJ5%!RO`kIr z>*e1cU+;)E((r~GAVnuJmQ&srnJme3{AZVm9J}~K0?sZ1HNuTq_g$%vkun8{%OiRN z#*hWGkWVobmLFPe;BCC{tH1xaUu8U%1AM>A|54rf13LGEVv8UsKIyERC^iKegd7W$ zc||nPenS`k`%NW`q64Ik?d|=BtL{i(?0H#>NR{b){kzdY{4v~PLy`QZoaM&;%LQ7{ z70F6*5J1TS4F>~ZNk~XI0+v*G;Yi+%qVE>siEuaXj<7=5)^+zBchd^o4q5e#%@PuhQ$?UMGvJhEkeWL|IEBkcF%U2pEq7#kcCUbE`g2$rwlaKH{pF z@72+Jc=#s*#rL+M@9)sVe~#Y+`1%Qo`lI;2LW=%@2KveP0EqRSD^omQz*Wq@-&^W8 z5}&VNK!kXm3POK&OED5gBOppYu{{Kay-`7VdW_L9k&xf)tWbn0VwfK=I22Ytsb_%b zdHvx5C&|mVWElxGD9iCR}Gy{Vn}mH zbqaQ3EoXHM$*f>tPOIe;ix!uqI>*n;r&b-FO9F*fpT0lp3B3Nk|M}C|=@0gBSWDI z-n@j45-c2~!y8ayM*K$Ak~k~xa!ZSYBydU8nX%n{lhFVEpLwkg_X85CsYa2n5daw< za0{EIA}LE5D6;U2hmFpFxKGA>(NNLWFUix39yVJT=ZN4Ol90qJ8{qF zToKBi;h{u~ZaG&X5gD#-`oL%9QP(3Ku}w@5UMY%!Fyd5TQS_eb#F0uXY~W_vLsi1a z;jIy*h*#4zoQSfCVrAmFHWAP>}~2-n!DqE#8WxSBu_y^1pvv=_wHOrVNptk;IAMrz;wY z3t9Ay^;KzUG!>H7qixUCA_Km0(&!KH}ftk>Xi zFeIR<@J&aD$vesX(`z#ssKSetA156{q)8jL%Bxa{`Hm;a{}sK~8AhgojxB?}caB+4 zG6S979^d;kltD2~LYV)M!$6Vnz9)zcL1M|4#zPi|BnqF@1aKV%G3XZISDS)^Y%Pnp zvL=tpmR(39{eV&1ykcoXT8|-;j6KpAG9&w_OfmJTsL=jhhs>+x;4?S3o8K?`3FuyW z47n$%Ip%3x4H!TeTpVEI5Ct|$m1yi>s(PIGVe87|FEAa06UMzvSP4JeA*@-TNTOXz z@W`jBu}mzAI)E+WCR|-&qa!>`L4yz$^uQ!qc+#mNQRaRVJL(r0OOPxooU&_w-SMB6 zB*Os=Mb~>B^1s8$)CrPVFffy(Z7by|nE2fCy z070M=VKl<_w6KIQc6*FE@uV%KFjxbCx&;gR48(IFIl|zgP`qHk)0FJo5)HD*LNI&~ z0J*sz0l82T2ryu_W{V^UDAY57dc+hXws=IR(iCSKXk?&v!O%N4!|+3zk`9d!EeCrl z2I>bqY6fveB|CNpZ<>Ee%^@ki#X9vYXA1gpB|N1FXWG;U%5ZR^AwmieS}&kPJ|xMq zMJnpRAUSyN-S0YF)9{8t%6wHnISEExh08GY*|#U`%y;VQuE)vy#C!Mo12wdr z4ATkY9JuA~H1x%t(?UM%aOnhU8kbSNE^=^0Kf0UjO_;ZQZS2VZJW$IzpFFdB&ry(- z_k-)4@S7nHC!xen`Nv#F*&lxFT1ZEVC}dt{EtEKk)#5GKot%tvKEGtZRB}$aMH}Vr zPH{?12kQF#n#frIaZ7F7=?0*L^R7ys%CWWRy0nVq|K5B0z}H0&OK4me<$WOXt)1=F z{R%%6PyC@1LN=Ny4CEV+;GKpQM#SYm(b~W%|Cy1Y4gV>gGWs<&TyU`P_CegGHxU`Z7H_p*kt2V zXEXBUU(Y^qG9M~fchBnz&aAf?AeJu-)6z>vJmgRU7}7_&**?kKSv(N$T-A9IN|n1k z+@b!^P(0?=CcgEi|oFXUeNhF)_=oK<*z%*gV&7^c!0VB-9eL+ z_#a2iRpXG)uLg_zvk^>-SYk7zq6NDnf-cJ!@mB_y;B1_UWpL1de49%S26C{q3b=7^ zdn&%JoXBblsMVL>Jna@+cIoaX_BD8cMb_>}`eknTWPNZM3QEu1EBkpL6 zrRwM}SznD0TKKkzr9DKHC@*l~)ZsI3I#~egrAnKZaqPwSUl&e9qf8o!HF7|0&*L4c zAG)cS^=L9S@GZaAep+K;AI=ycA5!+5+ih`A*|&3J($%M2#-lEis%F6p!nzm~PZkE| zIILaHWUvC1;SA%=k z!hY8c^Byf}n|vYjdfrQX2VNn&FW#iMhIGrxTI1Vj6D*B!ho^r2eB>vQ94|7=!QDMx zuOF3O5WMg9w3|aiv(7o+Siqz~iPS?s3f~Ru&yTLr{FTzs`csT~yVOT|u8?jssU-ca7e4JPw%D>?YP5B=3 z@p^~7;BL@;I=ouqKQlv2Mxu~ekG%*GSNjCcix+<}R#esG`^O-Z_vW*yUsB0V4OUPm|u z500WE{9Xf&&)i_g!lb62x3r{Oc)Ze#yyp0O7DV;O)1N@Wb*k8Nn|K1Bn%1fl}N2CE6WZO zgaJ0#6BVAR2r&&ms8LYKRna^lQH@l3IHhtgQUw${sQE`le|QjeKBT*-Vic*M>oBZD zqH6kVB*J#6*=l%7Qq_{A&v>}O#7Fg!=&&HQD%;**%du)lg{r-Ex7Cj(>vdH}?IBib zERsnH$TNuM!#Nob+V_n5$c$QOk1|GPK4{)NZ~i53GyqjJkGp%qUSfzU zqpZObEI=!~sAK{CKI2?Ied;Vd2DEd60KiU^u*sdurFisJ-1 zCu5zq)pm;$_fyebP3V^K>PXypUsuHYpg7Tn@g^QV%9C;9Nx9~&UXGVzX*HUo(i3)j zqs_=LbOTVTu|~b0ZyJSrH=xmWTVn&QEnrWYdG~rjlOJJUJ69|ih2vkV&tF zvN(qPD6k35NPgEr@Jo_`aD$xhH~Ev*rf&)5r|YxHN4ix^l~ek3thg{n06I_vN67=0 za|Ws6z_b<343!iiS}m%-DQ*bpLxN#SyC1^~4gOJT!h`6&d(v3e(gZvifzVc6qbX4a zD&2T0LnCsd5Bgp^`e+Cbxg$rdDBO27S2gerP3)V%$~W3};^wfnZ~zV7{&ND8+-u`( zY`AR8W0{2rc1C9^EH1aLs=4he2W_Ght-FyxEP=7-XGyPj^C4Y^s(IynkKlU7I25iu z+K~BP~$QgPkF9 zS5if1QFYqH-PIUzr`y(n%M54)!`>E2;pX|E2pm{W|6Z98{GAN0F7RVYNZHn`?@i`;f^X@WGt6r0Uc@w{BE}O(we*XCSBOrZ1qKgV*ER5-f z8ldJ8$&~`4V;JwJGW{6HJftkUXplF$0E?jM$S-96JdpQ6*&ILpn;G}3XO>Zao`8J8 zo8>SyAL1x?p1Op(%3p>TT=25`hLrm~Uv&ujlo|V}sSt=pG#)7<{%*56=tKrtf$5M) z0_pf;_V=g9eXy!Rc+mTGhWBK`?>|r#5gI;tC&%_v1a}+3mRRxNuH!w^8r#1sHYBLO z4vhomq6mzOU)LJGd1PfCzSW6AkUfG&F-l#LT6Umi zpSw+N-PFtK+2zt;FORb8x}1vj#C}721ea!-#rX8Pa<27E6vxPj$kdYcoXOA(|Azc8 z>n7~yxkrz{qK`gJvoFeJ%sM_=d%p3hcY`PHk?h-zwbKo}*U+P_=dF{iLmflPU*v4$ zNNn~USvMS^OR6@9rB(ZRo4$;j5yG2?)0<2sHs5Qr4<6yZwqjmzz+;HVBIzG?RPZ!Q zKI~fBv^%o7b(A{pRi0fWVKnD`jS_+gX+>Lrl=Z>#&X1cU)!UF;k?WO&lX_7e>Ne+s zkvMhA-0@P^sv&D!LI&z*YuW9_mdBk<6!bB25lCS(QOS{#r43c`kmofA_wtK(bY;*b z$rj|I3z=kzHgQ1y$#*0*k(jzD+sWZ6+b27=_rKGU(^77d#!+>I z#aJtHTnb-tNp#_u0+;3@oD9h;N5h3!}PB%!Hs7QMt=pj zRN}We8cIA+Ux(W&D~JzNbf5huhNaKQjB?1?>_3pQj|6N-+*Sq4rCk1yncOa@q>x=5 z*F)y$0nZbE^?an7)Z3%)h&bHGGc~!<%oeUBeaGluDCk2T_5N|z=Z(-uRp+~w&Hihc z1u&!-n>w1ZJeBiwG}N$(+nU0@iD-~eL5B0t8$~pTFr139UW+IM?~893a1^Dz z?At?{NN#h?Ys$>m1m}newKkd<@|fJ0ggwQ3ay^Y>p_cEn9(9CozU!ohEkt-gBNV_7 z`xnWC7tARDV$3w+r>)U}3zE-i($$O{+l<&ALFH=OYzNxRI2YX+KR@uLbxw7bt#d9e zLwJk7NwA4$>Ht~rA^gRmYR-w_>UL=YP#qn5@S!o*Z9)IK)j*Ygth+Y4(k05=B&M0( zY)RZQYB#k=GM(Z`8|B)Wl=6;{-bWYb!|>6rwKHNXLfsjruHTsNW?J4k?d%AB&!OYm zJN>F}S!jN_e0TZHxoJyLmWPhBw<9iE{!@GOvDRC+S=!QguF?`+cv$}oWa3j_POcJt zxH2x1OA`7>U}aF|K^zTs#ESVzuq=MJVtE(4GTD4OC4!qimQCPiNz`FUI(@zH+k6Ra zu^O?vB4Yt8lzV-BqDuVyG&-ScG_%_;5mlI;>nk5&4w~|eEXnRGR7I98$ydHEJkzqg z*hMOPul|D@V+`xy|$I@&%y(iqxTI_Pk=mD9MTFW`BdNv;IYc-$8~q& z_Tstx(zCkE5QneL|3PKv9;80-KGFdS@OmU-y-DGMP$DC~0@P&mb!uvcxWIGp(A_+L zhS}OvYwa*9MRU5u6N!g2e)M1#yp-9NgFS$zW?Ud&T$*>C+urwLFYuDu?ky8frSvoA z{&LAJ5ii|i^PtR2=nEtIDHQL&Xegj}h2f4DEPsq(_LeN?fZpPSpHB0gnTVNPu?e0^ zo_RwoT+3*&(h~JDF`r~VdGp}8cz=4U>A~-1Nl_GDDYRd)KKQ})#rx#Bj}AVCIb|`N z{zs&wH-Ed2z}OG5FFwW+DNcL67V=QKdLP4bPoq~?#`V7Q6{7|X; zzw{=A-?kI8MPQ>|t^+0PvuiTNz0<6MnQAt5Y$ZneO-p*)eDxJ@@_JtHVUPdWK~I75 zm_S4QF|Q}Wok8*ew|HtUF#YBPeP}F0UG*+hGl1K9okyCL?)QN59sB)O=GACi@qqW_V#@N z39_9=F{o7Nl=Q)P+C!yi1*}x^a7Mw!>n}h3JOh*D5sG~Zs`M`L{m@S}IxFjft0z$P z8)%FiRI?4HmG2S_Yyi^40nsy{tG!yl{=QE`_^m?(W*;^bL0lbF{TO$K8mavns>SB zNM`7F++W!iY+LYS?-t`FsK&b%n?_OX1yODqF)m2!kH_;oiHDmHareRKCb*sw$G@HA zmvw2Euc?8A04TFr0F=l1961`1kQ`7U~o96=*G6(ae!B2qeQSh6~)8n6lTm9ig zR07ZNx0s#bI64vclUt#^@icCux@UL72UA%e)s9c31f3(ol({>KhisG4FeO1Bs8CV9 zI{CW&Vt~Gx8bjA}y8qBc?Rth{gQFA2NVfU{i09dIh)B8OYwIAC8T}Z_;GNeAPY9~t zWuU@+wA;>G?089L@58H+p)j#Ox0hcR>R&wWJNWbK=hfL6{=c>eu_Q7K@^2*jY2HTp zvU4H7hblRtfhWY~S_lzK1>;0$uo0`KD1#k2ZJCtV+4Lz0FkJeMBf~&bYG0MmGLSRV!nKq0&7&YZ zFESiv=wc92c;}%?CUoKvMGhC9AE)%Z_d|sP&EvyG;}jAePa*>4#)5k@=Du>j$`m>z zu}7HA=7;>NoR6O?O#ulyXj|$R;%I~PDF-Hyii&w&Y|0EW0*j-})vE*4%sx;65sQlh zl88kQ)<{jnGE6ZX2;D$Etm~%rQMhMkhv90PKy~vqKbHQ@*D|Lm!{53@Mtksd$~4f{ z8CnxKLL(y?3%Z}`;f%PSz?Tt0BbmiHK#z>I(lRGz~GyUx?VO6QYq2*Fn@ z)I93Q5T*Lw-mPE+!Nm>8L(yh$@Pqmi=P;0^c5`5Z`8iYpz#QQ7EXLn?9dFmftn?!; zWs5F@{R+%*;QRunm{_C%KoZB_9|PYZWSWknQ$wWk*v&SL(1RUS9ee_C=`lQDwDz zz*p%nkEBl;Kgi3RlGTOEeD6H2>#%@vo&NgT3V$GbF|yDnb2)*2|1z}p>z}TRIjskB z*G?j@?Ftx+af-{-n(-Wbc18rs zgS4gy!YwYlS12C!VnSX!F6}2!6LaDCm7M7Cy=QOC*7J|0Gf2 zPL+6M3&^#?MA4llhV+jKDa3`~{@H957c8?O08;}X_K2>XW+Mw&;dOfVV`17W6RVh) zCi~H=jIn=9`*~Bc8ztpKZ{I^C+lnG|(vUF{7e+@Y6rt376!o(9xq&pw+0xN4Z*9}G z=sz0+@Jlr>IaHgJhg~z}1lxQ@&C9#(`C2i>C#7!Bxc(HDhG@uHy;v)6K9D=RlSh}% zzI6mr-FwpUJ$)?#ai5aMS>XX}AaixHgq;wl5&LtOaB(Fy;Lr#6h+NG3=$b5zs)17mx!V4((6j)?*LXw#jVaAk~YM zG1*m8p_os*jzA3Wh*Q2^{6dY>QZK#ci7K2VDX0MM=&bq`rGEcEhm?HDi;pk-n3Za0 ziUuMw;riM@5OiwG$P9ic{Z=@ohRH>SMB~Gf9Ay@yup~0$ZYrN_`mHj*>xb4mjTZuN z*o<)$Z5K?}i&7!~S|L(F1w=tqE{E)`dFrz8M9JqAM@N5#9 z_ZBFus#*nO1)Egazpl3BL|d*Bhjv;{Hk|>yn%6V`<*0-+P(st-7DR5_;cPK{gu~0=EOK$^U_`FCAuV zI_atUE|Q3tci08*hI#Vkt?;j84@AgsC%pTv&VKwp%O4AvPVf7%d?kA%^Egm6dGklz ztG{$cg1^V>`(B2{$bI)dJ6OHSKWk0?xbG|YXZv%{&(T-E&$g-l=!wjRg~n|uahVUm zEjc0>IYOtC>qDBj3&aMJ`^lbRQ=sQqV%fvykdmMjPHGTE5Fm{7%1}Gv{69WZ*h@81 zT}4sp?Ke$_Vk-VLf8$8%4HA}Is++*I8-Cj*U)GA&d{v|Z8TNw<&2W#!KzoDbJ=MW` z%qrBPG^~^%LU+jGpT6qtPw>vgJb?nngh@EDB>+r|JJXBr?aie2atXr77S#UhJ4eMzhM1BijQCH%?mK7 z2)Yy%!Kj2__k`sblrRg0Lc_-D3+4vtVs`1`Fo2|sY8k^YGi{_~YP!_v7R_BDGVUJY zU}TwJ0d5dRVW~(1?eCdj2supetQn00WB?SD+-M>dQXwjw_x^Hb2zh3t`WCCud>$s9 zSG^ARWH!^p+2h&3>ao-sCQ<&w#+o$4+u+D0 z?~dZ|C$b*^a%-TWYPg~5SRR{#(Z(9h9?T+lNwI;(>^j^G1~8AMvB;+R_f6enVBFHh zL^6pTegCcX2;Nq4g~le3?s03_V@Hc8G|P6J%k~h{T1i?54O&MFTA`p>ApwpcPWB~O zkp(p$HI%x6FbPa3ehor&OysS}A7#2&h;t^Tu(=rU6`l8jfXD)6otF+<;jM8@) zGvFBEcc(W_$TEwx3i23voMvIpQ%WPoX@9e5)HWh4aK8hG1?Y#-*p@~k0lfT*uxQ70 zsfRw)`0Rm-J@f2hj}0|0iL7Gy>7w& zyP0v)h%s@0yQKZgRiZq86-J$H#q(@j)=mOOg^{?wpmV$^Hj*2qlDMv(6v! zcRRuuaj}dIFF!X>e;{Xy4?1uPo;if}az_l8E2RKBPAWuc!T)_cq2E{u-};=V0cID) zhbe*AyJeY%ICA^WX*e1gpcV-c)^GM_K3})sa<$epNL#S>$~u&!K;n6 zF5%&yQID0d1-GZ4Xodkj|ImBW)%{&My+^8bYGdH0SY3`9in5WO4eow5zC9AYk$XA= zsNrRbqCrCHx6e*2U(VBN){GzMjAYi#a31G{ZkBKrQ{1MB)ImcKmCj3(IxpP(W z*gvOjd~TM9Jjpi|-!3aY{&4%+R<|4`aN=k1DM)W?nTSN?U3XXAchuvPn>r_4o%5nV z$gEYz_qq%D!ZWTHj)1dN*x7~r*)*#DhbG|_F}6!d@FwW=hr8et;b!0{2b@fj<-Q zESS9+f!~WSfD)JlHV=H4;5TUuq*@TXP!GnxfV?;ldW0cFH>?O@h>{zA2A=WTARxe1*eNH^qu;1DMk7$7CHIRJ8T+U;D_MHQc|HDc-$Y0cx6X6>v;1>w37Zj{Q zR9~Da_0A|I8mV`WsL+k1@%4myjkH(KXwWaHUkK3?xYKc;(Zw_}BsbDMZj_E~U^vFy zOK-ea&Cl50NbNC%in?_a>dUlDvt6PWX^T_&!17{Z|6) z5?*8vE_c+LIL0uX=sM2FO~g;VzPJl>CpU0k@$d~W>wpPK${Ud|sh2@DAeqH6{5!oLW;XUIf^BI<>9`GmPeZUmYq#G6GP2#UU` z5!GwHF!dg>5fMMG6F1_M@b^9rz3Pu{mh={og4akDTy2+K_0-`-WWLtQd_c>NiLA{u zcP=-}J?58xQ!RgXHFx9P_D@9NQ;otCv?5*0IE!ctSBug^KII41$`5|LSNqYZE2{dw zT9py4_OzwLy`|o-<)Il`{b!YWvS@RrPi?-aCV92y0!pjBrLIr3dbCA*45d?FrSnC! z>flGkcTv4pReJF#eInm?WUXbitp;N}hEJ;u1;yOO#Y*JGjM1gW4^Sp%VnR08xlXO7 zXGpUTm1eKRZlc5zlf*2@3oJhJSeCbbuk{UXYqi>Cv2HH6{&;=7bnU(=W|Na|bIkSl zr`Wf@zV?J|woh5?*vsr#+p4(4jReG>JgIsj!r}1nW=luhaSZRd+Umpl#ryHkY{zy#qxQ-oasL;s{y#VU zk|p9^w+BW@^u$U8aTf+%Z3e#gcN+P5GA$AExH_bqCG@Mm^r^(F=Jv0a?XL*#W8c}l zq6^?)?)b{t5zfjL;hi7x;MP#>c27q#s=ho*izWJL2mYC43|taU*B&#(i(AOYyMp0e^R%`jZ$T{ot99N_sgplag~w9oo$ZCy%tgD_h1|am`F>4{ zb-jxrE5R2QU44J6*Hvuxt4pJ+OpmPm|JZxasHVSe-!};fy(E;-A@nZ2x6nI;UPOA6 z8mfSl(0lK_iZqcfN|CBaQHs)(4k`+Y3J8McM19V)|L5#I?zrRLaqrmUjvL;PSA<{I zoa?jZcdqZe_2W(vx*=L_IpIfR*rO&R!RDmW=KGI!yC1b={U8sNYL)J7<>0tSz*e;x zUiMBdZSc{3ELr=<9|hQov$Y@X_8}bvmu-yfwZi=+lKow*x8KkEHtF5IWqMmpsb5U9 zzc-Ke!L9zDF`v%6=sx+6ePu=Mcd?hNt@8c1`*)cKZdvp%%m3QAEx+GCj0Vek=E+2)bRlQH6 z2SzX5Mv@fbvj(0NDMVB%Oj-6#SyE0ujtG4^aJzGW>bu;mU;iu~&2uCEvCk3MldIXH z%OPz0oR;*w-~0@ZVzzK(#;=IG9|jh)XkU66FIxVrv{xi_9b9sjTfQ~;vaESJ`e#N` zBw^U#>NmMHitM%fKOa0$tY8=fj48gD9s~>yt_v${y!*NG*mL!BEPB~ zh3)064IZUAVWnNhsCN9pT`h{e*I1)nOQkJ)r5`RsZ;h1R-5T0664;OabsQh{BXsEC zuE2-8%kKjPcJ2>dbq{@9R2-gGlABj*8dCa5p!A8h@{@bk2ka0X0rr$s`3u4QFRaR+ zUwa+!L>~x7w@5u6`Jr^8_4q`b;;UNpnV0gZ^Wiu9=z);O_o9?(pAMaK$X;-~IODZhXPqQY|CX1eM8myPaz3Y5y_10Z{h82#_3MFsDeQbKXR(pfJ^1N&x z1|BT++@|&0XtjH+dD{zpXg?B1L@O0R=fBexPA?sDMHk@mEKl_7PfwY4r`a;uTW44F z62?{E48b3g3r8rQGckmCuXf#BCU0T5<-6V=_8tB#ocnERZwjq+6jPYb=5&el ztzYG$=*J26JP)FnZ-*S|)ZF_1i#Z}RcOl`{dK61!_~}PT@+~Y&)YaR)CJOgx*6668 z<@?{iV_Aovybqim`bnw)9RZgSXOG}BM;tc+30e$Ch=m5AW95i9xHeD4dXbu;29CJ_ zQUe8;2A;BQ&IBBrhZ2FBmghXdGNc8}J{g;Xlb; z5kV=$d8a&Aw?989O^3JVO|IVE<}1n>k-A5n_XPVDsAeTtDf4Ef)~|%PVbf*WvvNlR zUDeX3Z}N=fb`QM_Q;wPP=dN8UP#cRrbD5q|WsW=BN8{U&I? z;?~`eqe0r1TYemc8&N+Y#G68lzIfsF4{ zAzHDoGpFTOJWqtN5z4a~?sH(2jVX$}sG!tmt23dwx@c7awvGST4!#xQC)Y4%!P}9$N~@$mBzQ zvP8a{u-|-q>Bv7_SJ^Sndxk}w8WCP-egHIRHJtLR<%jdN{Am7Nwu$Xfg8MEb1Lo7*@%w)FyB$819h|zBiEWvD;6R->(G*}d z&Z!oBFc+XnU0?8!$2MYX%%XcVNokl@LH@&hSR!@9n1pq21SN=Z1(_J;)u^XIZ(5}a zE@JTHGfKf;m{#GRB6v9Yff)>@^&q#qdSou9jz3*bSWy&kUK2RCc;xz}>~Nq25wA^1Mup_d{y_0{l@fBT7G zt3Cze)Oc-R$D;S)HP+kDCqY4-U9Eyr!Xz(eOhURQ4-a*fKd#KN1a{wfb$Bzn#OBpm ze$P$@$08;fp=Z4j=x$A1YCUz6Dqbl&3%A*>+1L84dav3=S5AY zH+CYw^ebb3exe;JiuH|l{cg|owP^T@IGP+*zV+Z*aROOL`*#G>HXzM&nCh2pf+epb znRM0|N5nF@4KR%j)Ii+sF(JfD!h)AglMtrAb#S^^$JW2ET%P5!>U%&KLVhdKxJ*pay}Zb&ZC>F_s38 zjj6EUrMrcPttz%&X9oiF9E}O&42mFTWH>n2_e)!tfr35u(JSx4E{bn6Qx<2dNjEbC zyM%svf5{=4_2dpNqs@lM7?r+WKMOe{m&>-47F&Lk7yL;0!KY)Ii%Ak~F_q%OJ>j~t zjpx16XKuE;jErEY)mBGV5pxbyfoTU;bUw~!=bvWJv`fEy{zP`eKP#GPkB#xXj4Y?1cz5_Kz6$j>@*I#vzOWpHhKDaaqU#>bkUGuLvbd{F>I9C}`cdz0I z>sYk1vc4hScfai@_}%KdR?)4&7wMmCzg=#c!$W7uDvw#kBi=%*&Qpby0y1u0jUp&y zpBX7R7HGZM7yT?e%bNME;_mW0EIfREI_)G%O&)}1$s^?5BmOK%2<}9J9v}cB%JeF5 zyXbJ>-60>3qzOR=B($O|hmEU8QPl~!A3N*-mqkwtmJ34(XLg8{R5-{Yu(!AJ$d0&N z{RKP-Ro(O_eBfc#^#GWviajps+3&sXjl$ zP7b(hfN%*EKm>|U0>v9k!Bd?3`GT7gxh;66iInluI~(#DJf;08J9v zNFx6k-?utB1nsqUjlivj0IUjkQ9;Bu;v=@6duLs>4WAXGHXYc&>RU7bRZnnMc}qT(CQ>8G8qWp!p6^&Q=uSPqid}~m%7aa2oO~h3Jk>n)g8{k$aihp zfDaVRul(TvC?kQ>`5LbG>oa~EG9%lVz6j>=_0w}zOip7q<7{?8TgQ)H7(qmK!Fb0H z5g2IA2)qOPwKGQ{LdoaG#t7Nr>@wnbfdVvFlSOI+X6CATKXdNpFfAJ)%yPM??z07Y zf)SK6oC|JWGx6PkB(?#Fb~J%HCQBv&Q7i%qd5({$PErp*L}UZ?JE{FXG&o@H`64;= z=6HvoX`{!_TQPz@4#2Zx3Xf`F1PajY0LO~t@;uGOmAiRghzT*@=k+J!yKTrNBsF{~}Ys0%e!$Ra;WB9|AQ+ z(I9jQGKJ|GlK@7N+LEtqw}~eyo(rPhIgJpXP)a@%CW&x3Pt2vhPDR=!EatvV(dclF zsOGb0BU#G^G7KmE!sOV^vAV}7%$ojercitpD9mgmv@6V466kXEiPOg)p$?$h??Nbk zA%xU23=$E~X7Z7(#%xJjH%?nJ{yV=Sc$0Nj8%)RSaf-Pw7Td+1* zqbT7PR0N5_EV7bT)^1i<<_7D9xg<^hj8+3xm^iSsz=?9px<+yhzNO*LMnbOo4&KAl zXFk_mVD_lTvD5r*Tb3CaC?mo{MfsW8t5c5^a|hpA6K0T|yQ3+@$u3%iJ(AphQNx}f z(ca%YW;EEAtk%{`^d|%8#^R|15tk$6$&ES#8_9bwTYDU69h`-Guk%YeA3=D26tJZS zO9mpX?+Lk(6}n{XyL=CJq$*(Z1|-`p)ra@I1(+;11i2kFxD{}_(o1ny2AH`6pMu}9 zYle6jOt_@=-UwB7byhXglEmHn@a$h2Y=z%sc5=88^H#z0s>kc96DBt(8uqSHA_uW%Jg6|!%>}W_4n#!^@eg_S6_N1W=AR+by zN_)%i`g70*M&1k{dk|2!6sY`Ngn>3lt0?G|YLFgnu<0#6L(Aan%fYg=?)H`;?zFsC zMInB)x5!3A0*h|>S*n9TfBYAPaK?9n93XZ8FMuDAh2tD|dffVz${ zbF{kda>F6Y*(W?aJYLu+SJtLl#=b_@p-JAgTiL5v#j_jf-KXN)p%px+>OZa#{7fr! zMmKU!Ct^tt^V%S8+bD4tm9%f1^4=u%!;Rbz?j?sFrJo}kV`F0r@(W5!O6%(Cnwy&k z`Uj4a+rH%XeybQfX`J{zv^YLKK07}m&hzCE|SQHNtaYrt4hZe)-;sH-)+uJXlYALynjEb zvn{!&@8P5Vfx$FQjUk-Vr18jT#`ri&Z8B|Y{QAsu)w!wbM$eb#tPMwBFIgK5ZoRe` z*k5|re=xqYx4*vmdS#LsptHOX7p4QfPo40p-{OddQ<$=o_oZqm^56g z*|7OGCFseFkrB;BcDordKyax&K!=Uv1DAGYRQ?La{3Gm|P@fgz$w;E5Rt|;Iab6n`V)Kf*yf{C^Jz)W5>vKcb-aUr}(;7o0eIqEY54J!KGYSBnvgxV-=G^DkxONDlq69 z0SO3LsDKj9nN7@U#sE!2fSw)zpS&_w7zAJ!G^W-VG#b>WmWWjb8|#4ZlQ)v}joCyh z6)3lL0EU~!AC39MDit{yKYmg*7BYq>Gf7q|qcI+DXVt0Wu4*ObL?@FnNVSMr-q0ox z;@h^`nxSH~tCUD|Imh46zt_ry0c65&FN}jzn2UTd0L*(L06@x*Dez<$A44S|!)P&97o0acQK>X|@`E}&JPqQd~0_c}THHPrtN(0d2ycL4N11O|NrhW#SH{Y^09 zmHLfNU-L3_NK9Z@d}vg%MZzQN#D3f4f!ml=r<7jT^hXI&<~dTckkEEvQrS*NbLi&;qrR(quI8>xrYNIL&Fo>iA}rN9dFC}4jP6(4b1&>rLS*npDb)`Psd{h_Sr z(D>rZQ8Up>jow&w+e*b(+dJlpmA1_!K{(m$=;exdV1kRR*utmtiTf@}PAakrUx~m^ zIFhnO0z>wS#n4;%O!7N>2SN91w$XF^76aj6mZi?#;6g{!&N?ZV7f`CbNMz;)^)`lMHE(r|3V^HjAh(j6YYHM10f^|p z+nQwrfXOG}*N>8Mp3f%7{&QTn*a2uJ-g(dBGk}s07#a#r$|B%=d7(dJELu#Bzy2D~ zzL>07MWyq^@ntcZ%?Aow>3=br1YspgbrGqMq0rz=gaV9r!36vwsvuSh&}0(ODBW}p z>Uf3BQtTF*AErxFpkrgbZUwzAX*OW;gqY-|D4xViZ5Rmb%ezw*P1!dBpfpL2BP07N z2#A-K#fZ;Ru>w#Wv7CYF01)*~ejXleKA}@0tWsSqo!Y<~fME{<0FcE7@qrmUn%?4s zbQhwWod`$?Ycs66})>K+8Izb!Cf24)qq~-%O0Q`2!{!3~& zO#91d|AsPLLckH3h_LWKq3mxq;J${$IcQ&#))tf3mr_Fg+Y4J++xkCK+usf9FS7l6 zcfxH=Dtfj!Ui-T{SzBA<46KUnAF z74@Gp3Ay(Mh5u`)&-#DPBow#5U5i}OztOGgI22}C?c#&^>yGUml7e%`#^sja+ll_+ znYi?FJ!t@!TmB}Ne_~^!yvyJ1aX=OQFIxPwJ(gHh{RPHV^PC0W{C{ENpMCP5v{+Q% zivz}gCYk>J{?>sPzcWn#$oSy!-;^>xKaaykTuRy9+sEaTzvTGs+sS_`UuoQG`Ty7p z=al{3!?;#mJ5R!UhxdZDv(@;Oy(NIP`@yt`%jk5jfh(EU}H0?xy$JD6kj zk5l$*^JK&BP&Si8(>p$zkN!Ai_vvhmk(BX1>wM~GJetj+SD7wB<}`jYUxaV3k7NJ0 zQ}(aW?_xGmx`n(S6dv__Tn;ec;O0 z|M7I4-eUuE!&(iTQ})wJZSq&|eD0=US+OM_PLJL{jJ7mkl)dsc_VzKhBuJ-3cO~N2S5RxSsATo&nK+ymuccDp+ z`uf%F070`^*wV6(A&udl^&dOq z3!cm|5JPd=OtQD-5xZb~jJHu^Qv+Am){yI}ns52BO3!lc>KHqP1HmZW>#zB%Zkvf- z!d8ij;3RsE_EKizG{Bor?D*Bj1x_{+Z{H7+Mr*`(kqoM65=2}viu9+w92u!K?NEuQ zj5m@c_K4D@=^-Fa_mrgi>!+u!)NdS!FL&+9OhlA{_}Q4P=3$70LmV#x z9c>aC1JU0ZCQyaiQdd`!HXtFS&yi8=2M#}#CI_kK5HY6JKw_0z)gK+%jx@<5KhC7_ zNYm>R-}bYSV9!sw;jo+&wf_8B7*lxn3%M_hPp;egXyhbv=p-D zo857&PzWqZSbGbrlsiDA??|kzze7*231mF2E$qNW-TJP` z*wcmJ*-LFm14>iN#3bbfm=-^EMG61ML^NZ{rY3)*zOsc_`Anztr2{gi*JUeXY;RIZ zJR1wpdYhMaY-<5=7#<5JzZoy{o>e&|HjX){zEFVleJ9hiA?AoYj_&kPFq3Qob$u5o zbMGA?mI#&d_5+A(V3bU7n8R9=jaazdjmjV;E{S2Wgge0ue^NFfjXa@lEZ-G>CrRDK zhU%{D(V~<<>eM~Kt`d6xdM)W#yo_*>rfdeD%i66)Ha?PCTYq=;jvtLA?EwUWH07k7 zviT+~lDAzF%!seiC3JTtmeJLWn2C4Jwi$}HMV1O&`fe@XYZnK8-@7X&y0F{FdsGJ@SaHRhZ`_Qsd*0xd6wZ(CYj;V_QZiI)G zr?HKXx!wOh!o$@!=AV>-sj;3?ygi7<*!DCW6(Lk9*_dUFm0YiCCmU|oZAvPosT7PIX8rag68=Xifn!$$&0&m zFp@XC#H($GponXP@aI(IWv9YmGGZt-`duw+2J}CUpDZ}e3;;&{IeyYGa?`Q!(<6kK zIK^?rKU{4CH=PBCkwQ}1*JO15mj|thd(hUn2aRJwjlWE2{+kJnt-Y-5JpVZ^#lbVu z(=Qwcgrt72e;*`$HhvReI2Lz)6pDCdYX88*zV9#c-3&zu}Ib__49-lAN6EymImi8JStxIhpcuyzEKRYH?-dWrV+v zpLO+j8tyhWHMg|hYrEgx(b?7A(@>_H$SIbdJM=hPUaVNB;z?}pv#IHs+2=3j=3C;V zRFY(eSJxC|lT<{XRxIGq^)2pyCyX;h^uIoSJjQ0QUH;+x;>YFHZmh5t^fXlqy>N%) z+9BD8r++4rKxj%-KpG=ynW*e)6?HbTZ%FpG+< z%}0o$T?d#sb=6bCx^NRoF2M$}dsMqMeCj#BCz6zyG#HR5W!UuKIIFoWce1$vY?kGK zn@A#1C^&M@1G_v`k=E_g%Y|xqzdmg#oX@qk#b2-2cdXw({Ppa{p>(@-TOx>; zTD|e!(U{(I5-;gvqzAhJo2T{;hNqX#B-QucWObT^T>he=VhS%2O0~pdH|wG}K37-n z*TRCH5~yhpD@!?#GR}GIVWy^aB6QPL77ON7%|v}|ALG)F$lfb(krSV!VjEc8=NL;z zy(o5lQZj=OwNe8~c?Imwl88Rzd{@;>mJ23NItJYlN!mi`S7D^&-f^qFK_?UP2&%%l zcx+Wccb|R8XIsUNg6YuUX z+u*9oD7lXvjq$r6n7;a#bys5%mH6U`Yw?FY56oI{z1vP7g?Qg2P9Htki(>!uaH3G} z+M`2@rcWJ5PhM~LFBj5f^qx4qJ{mlDn|7GKck=ObE8vpq<1pAVd4Gg3$);TbBz^dP z>`IJZ6x&FxM)hSP+AU%VLYq7#@|0O%05=6>xE&zKm}lGY^o}BB%?!Qt#t5J`P@=PaRZwiqcaKQ?ed_Zrz0h<Yy83fE~jXeuZ6EPAwnGCl!)WJg(HAGnSwnpE$(y{bQcCFA`_b@N z#?$y-;P;qXbe`!OAZ!HzvG8@Rq+))l1RVK!Gc)#A%=K;6UJOku8IU=d8Khu|3^aho zvt4cSUJI3})2O~xr9E<@;2}>(Iq8+~t>TBvgIMOoYu3Uij*$bsKpHy-G&S-Kk4bUr zN4^|IyXzP{mdObtkB(HG0jDr(4-$b?e|eYI1o{_*xe)bgAkmx_!_5v&DXH-c222dT z4V?&mDPMX>P%@s2lsNG*%qgmy7=iZ;ioa(ZZawyt>1LO<<{4hN<;P*Xo#M>Mo2}T) z!Z%5v+Rz?qbtJIjq@G=cUN=&3E5-6P5=_f|--e;3fFsOx(WDooCibkbcih#0>3A}K zn+%jBF@bLloN*xT;-*cmJP`y8S$NEYvhke7cTZ`})u<7K-@#+b zrX3jTxmf*9wb7v4Dh8u_Qb%=!gyh~trn{NdN#7g>i$RUE4ZD+Ri!mB^9T)OyyLm}) z#UZiw7&0^xBKRIaAcqDLYXdyXy)_Jc!WZj2a;j{+F^BK_UZN#WWz4ZXAI!t@UiteJ z$lsV)wsfbm=*X0ma=o`??V4v{(N-Yjv%h(r=Nlu%cziL#WQ*PRN0ualn6f}cGT=!p zFhK?b5!`BVI-LU1Tov4ip*f@`7fBdRRKpATxVSHP{CqOptsPL^>Y9;UZ8ME1VR7gcF z^9cb5y!Z}0AY>fXbM^On=SHXz(k~I zRy4^jU7YKPHVYu-Zu{;Y1E%*^aHmBEDI5=4jhiP00ESo7QgPhEYvAe(XtJd-gyIa# zQRqBz#=PkiYg?DL&XSy&l(7Zxj);4VBuv%oGNxSrU?nn;2_P7F1DJsO^#3I*{U21_ z{tlS?TR+2b>2FrD@y4;z?-H8Bf0WR0#WNf(x%{Ein}Lacfe42}|HK>|*ZkjP7JMu+ z4y`T7kTqE!@JwH(YOXOz_nyuDmDZ5yqwvRD?V(q$r7zw~d5{k+zDpN_IA*MI6wjMMX8L+Bf;EmzraM12*jcCG3Mvwohm6|UnRw*SuO+v*3yu?#6G7394TgkW zp5&x7Zzgl}<3kZF*`kl(j1nQ`I^xr@B%4x#E*|%79%BR8pa?=o z6}Q0Cm~{tFS%h8L>>c3*6e8F zp(REF;8{M|9F3f};g2Rjkgq1dY8O(u#+qz5f1ozJ?H#XFj+?GV(H4`(!JRG-eoty* zk+_Z8s@SQG##ZhvWJ;$_c5`!D&r~yBiWpH$RQHsVKnqFnlpdiW{#wiZ$DOH*(&J~L zht1DYn@MgA@Z#+6<}_QH@ZRRPtKdC%YH8@LlX4irFJKVB`WEhKrovM{v|U>CpTx2W z_n*E^n;3+C`1qOT>iOm8VN-B9&j_KsBRZLgV*u?%#&gx40n=0X!bc+?@r9Y*Yrvm@ znd+*8fsH~zY?ABpwip9ni(nca?}XsD8AdA%&$!UyWq_o_cfqyUu)Xs8f_%Wpc=OMq zftxjgq7SWT-5bjZx*CZKB`fp*rNd@-_-Is zTa9J6nSEXJeD<_Dd8gvRMz38eyaBT~`(XYY&eBvD$|u>ZOC;B8 z-?4kL&x?c&C0Ff9U{Ww^RwlW(b=MWal}*=&IjkAG6GQ(z-0w-=R^F81Z4e=!_* zJyg0pV!ikU`*K}bIpTCTRQcCMRv(t-=eJF4CJYH9%f>*`=YRrvNC-=N3^@}5Owc_{ zMBEq)ucyElm#HKR5RqfKLZDT7RhP5d<4a`OhzaUOX-4lPT-zfibxzKwUufr!Qw2aW zr4?VTqY`5&$!3}A6dwK*;R|Mrxtpmf1$dRVK z!angRc^oB#WOh)9rHj*iB9_|30Mx02EhTqIsbc4;FDEg0b1U+aAiwmGdZKxky&9$G zU$VS$(`&AOmn%ZBfsoV@g5 z3?zVRhTex-u`y1L5>;z!BO-ml(?;89gnnM-*7A(Gs+QI*3pCDG)>hHCIVr(pBE zrfYZI_bXoQQ@(|qv#xq>2(PZG++0ED%W5n%l23<=*Hj(d-C)zlXZU@8p<-0!uF^da zD$b_8lCpqIO_85Ex7jR#zNg92!PE3kvsp9Ct7i2HPgKtb)3#49TVxf5%thrIE=M0W&KKSa$v zVjB0w@9|RkWH{Fk1^eMmlp(*u=LUf4SU>E`GUcIhkUwdW(Lap-MMQW@fpv!j7y?W+<4lH_``V|sWU zbL!>dln-Xc`PBTMnK6mMZ_P~5$N5jVFo`lq&pd(G@tLtc8e-1ck5YR3b1q-{l zYor)pnAhr3`JwNb^Ki%0bW+zWrH4~zPGW^=;rl|jRHoP$JBy0=--^ojOuIb?6&qN% ziQm7l_Y0vfT_kOn%94HlWAxkH;i0SI^7IS2-EW1!^=qnm({pR8CokXXyVnpiOs8%z z)-ley@|)VsX0-*>NB0~IyE05wNR{8&7QXRLC+`PQjhlJ1R*$D~kBuq5)d+!Rq$fYr z0U|Iw{L3>k;W{f~8TmGt#+M9Z|52`Cv=Y>jMs~=;bA43+fa%USI&{}^TwU}%``#)9 z@pivldA)nqQzvv3U@DKh*czYJ&QR%D$=Ws^PwI9z6ge@zIuhfPi92?8ajpovjeb- znOx8C`=2zYkMT)%&L3cm|5>Z@3r)w# zlQ=qSZtMQtYJe*z;l`H!KlZ{=8|F8)r7^uAp!Sr*Q5)6C7pu&bT8Dp8oBp-37w@~n zXK~bqj@=UBVImCh4S9j1HfwZjTzo=eQgTXCC=mq(H8pK64Lv24lo*65E3b%5{aaaz zBfwP<85x*yl_h2T+pDte;ADg`q+F>pv29( z#kzr+NCPERR7NG`^T;orb@TDzuiM_3Jke}2xog!0H$iWkL*&q{+^=+dTNywS^7@LU zrU$VQx!I`-#>A0lss&SQ?M{LD!{q**t~;Y=G(E>}TZdABpi|Rpnu3 zbH~1-w(NxPITQH9?!}QdDFvByD;|6@DloWsc)0`GA3>2VHQRqTw$$PDNpQ7FEST(R zVE{f-W%Sd=oQ4we*`r{$p{gY0Fcbm6CwNhpmgRwIz=z0)-s(1}2i;-^OAOzrc09TF z<(z}x!=dR>a1xCO3e2LRJzQ}MDwww!iv{1UR3kjzUxs~Q0{|ka)<(AyF|wRkfIqbd zDki}Sg#zLth=4pvuylXUpNZNl2MFPv;xWw9 z|MhY{oeGpPtO@XbY8PtY#ugq1Lmh-O0x`d3~)-e+dV z<$~`MP43F_ch2d-4x^Tvskh-a&kb=lG&Kq9M6jyJOFDi&3AqUZOTZB!+eivPz0p}H zFL5p1FvU*hJy1d{iL<7*w^UE>OUx>FXmOj(c3In723_xfSS91FWe)x3rLWVgR{%Te@-QuomV?cht=zP(bmPks9hjMyg+nVUX-qCE`1 z{4`MYgpqIX(^?C~(B}`Af{zb=qzMdf63_6DY%#PVM)&yBxyF{{B811+bjx`rXs(m; zKcV+ZCwU6HeI+_MpQ<4C>}lx?(G*w96>OTX|B7Hnboz>D_R-oQ!E@Pa;%HU~;6K=v-b%yr;jG)?o31ycn~m)>GpQjR-F#@6$>vc>?V~g6JC_D|n-p)hl*o zWIStXD;iGmAaY0BRo|zpFuY*-9}FOv6M%0s^qo(G33xzC@=@Z)3>o}b<)yw#Cm8R= zp5Jv>+1+BVCfPk3iZ`-(S>I^F`s<1w-r59kwgI=&p3eT5Xp$zve)qXgq9P5uC2ZS& zL~*JFuGb}beDYbyNpXbR#<~DH)X=vcvJ??x;`B^-?PnVDRO2@~5tQmR zJGQVi4jtWya;x~aim-GxSlvmwgTh*kXj*~I?%L3~W~u@<5s(>X24A!4k>l>*NTttb z=@@>Raq%UKyoLfyo~C z+0FnKi<<-!E>FYn z*z=f%1VPQJx1P9Xa)i%<0sf6ddS0sE*kG}x^HQcd6k&_3EGo#0+_*=9Uw4x~F%?kk zhRud86LgVbT0i%G4m#)m*usY;U*#LR3iB0Tt*_>H&09km11 zyqaT_^BMfzbu2!-+G@n}X}S5;9EwdE6pbeNrQLV1tT^RUgMYE4F#nE}dAw$zZeqn( zGg76Wd~~t=X2Q^(#&B&ulNachccf^VjD$SR669W0Srpv0oV{t`^XOGc2u*{%&rK_H zxuv|qf_itdn>LD%mNI&2>iq6`;5t;7la>l7_=29hV?P5__T(s}%=KOHgBqSuSs=&pALe@sPus8E=q_gLIpN&W_ zJnephlXs5HYxX6Jze#WWD3FeN=YzEmUwmNqrRRJ2=SaJV9O=!?R}VtI z8{CM}da(KYlr|7sVT)mub{M0P@goSi9y{B+bzgGDlfum=K1Ry1(wxqnw!u2lvDdLM zn9h}z+d5fA$|l<3+bUA4QZCv8?c7{;_w7Is|_@xD>Og3T*s1o-D{T@2pDEwOVK=N(ow#4*y zGhgW}&0C%#Sr<>cK%>_)d(xKE&xO*u%42ljvG3B`M7OP$e4lYsq+*y#R65DOB;7+Y zPR}dqb=UUHc-*yMXf7)BY-oCPvn7O~rE1Bu(eR<1=lw#4*1K@8=4X#QyLuV!-M8>+ zU6u2Cu*A^zpwO%B;E`AV8N>a@OJ404a^8>OjO|b1-kn7K-eXdX9WN}ryXob9o?0+= zF88`Y|A9!>c=|X&mEn)1`d94x+ct(%RpnI8aD1eM6Z`c3CHCo<{7YChvGp~xL;tQ` zIfvN(Z2&AB8qe-Gmwu-Pp7-kX)O3YEGQ+xz>YaalEJQx$7!K;f(Z4yTbT1jc+~( z<5B98-8lY2<^etX&K(D*;U51$YxEp*4YyJM?kN9r%_0<}4xj^K07*EBT1Z+)Ku(WO z$yh{MTU<^@MoCXn8O7pm!|Y|t;uym063*-q#SrX37w*KZWyq~-$g85qt!c$&gyPb( z<5e}|G&NzjF;_(Di>a84tJ%n^nc=<*PD4j_V^=own+R(k#C5c^j-!l$yR6YoIb$z* zGc=o{m8yoJx)w@H#~3Hk>gt-HjLb}p&8^HVtu3r>Slj$<j zd}o-bX&ll%i`TON?t29sT!V~Z@TTGbFB1X78-UR*^TZzWfZeBX4^;4j$`y7HX+y}JzW}JYKd~ zb^WM!ywkFTYu>9Yw+U`s>GD|AHJdWK_xgcvTxo*d1K*`G*`p8ZX}GO?2!r?egf1a* zYb@D*quPn9WB2Lr<~{v~o%_?p;$9mw54+y|Zr)=tc+`EcNJYe;?h`hWL##zD!ql}@ z@3qn?84iB7)>)=N^0m(~*}d=C=17M7$?Ml*WeSvE+u7O%*dPiX+)7zWH#j!l7A)^! zf!Xc`qvRxDYGtdKk?eSC>ODTS7|`h1XR1^lKP5+CYCwz@Jwp%xsTBeuLTIssZFR5! zoJCd;^aR^%Y;hn$YPPsgR46nCpFK32++s@vb{*u)y}`ho%~p+t@VL7hmsU`Eq)kq5VAaxdXrk5%#?EEJ#`OtgSW47e}`!u=5M91uT zPmJgAnc;8!GUN9dvg~`j^#E;#cWO*~!z1cIcAM3}h+l5yxoo^hEf!Bi{T@hd51Z87+^Wer zkxdoJ!|#eGSe(QZM0abvIjkGlnm6twCm8qS+69W-yqFgj*l)qDLO>EJ0!Sy09Ntw~ z3%E_`m}u_RrjsTa^6AlG@5fI#8w!EX11C#fp9jz1wtOD4Tpd=&H%PC}c?=*aH-@IR zw@;GO(YH^IArX5b|k|5ff=+%Bz3GhTO?;aP>u8ai@GXXmTiD$Uc8ew zZyu8i5j%k_C`C;^XhDa!2=VgU3l-@I9d;smCn6vX8CkcQ1W9BH3DJZ?;B3u-u{Ke{ z`xq2{0%uM(AcERN8$on)wr81MgOZq{Mm+$bx-O|KoY(N8v`|HLWP^xc#@SnAB~#4U0yM_S785XQ#~_&L6tXi=EehL z;hV@glH`+PR@LOs_r9xu4;?1S_2tKjcoLG>i2%jk?5NL=2`DfmiYEX7Nv1=9Jm4X# z84{Y*SKWtHjb~R>ZMwe^?x77}2OzYWT+S{NRzfj^0K!-))r0$+N~)Z?BspwLWASXZ z?hoQ@*{KN+#>Fn%VX25u9jW~HK8$;XUcYN+c4H4~EX@Iv+D zcS6eFt5ec^MH)dV5-QP?F!zuVT6`cND#!r=`Kr3}6M!c7cHo5*Vc@?28Lw=d8NEQ= zp#Q<%S%yW~@6r019{L#?C5BEF6a_?d=&&dW3F#7b=&)w!X6Td>B&0z?P)P|wg_LZz zpBG=1I;RAI9jMgBmfF~eY`fZVH_sx$O`Lzsiqxf4?!qgVOLdnzq*I%7GNpIy>C=-XrX+%Xj9zKEojtVT9_T-*f#* z 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 BTc!X2 diff --git a/vendor/gems/graphql/guides/defer/graphiql.md b/vendor/gems/graphql/guides/defer/graphiql.md deleted file mode 100644 index 661abeeabfa..00000000000 --- a/vendor/gems/graphql/guides/defer/graphiql.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - Defer -title: Use with GraphiQL -desc: Using @defer with the GraphiQL IDE -index: 4 -pro: true ---- - -You can use `@defer` and `@stream` with [GraphiQL](https://github.com/graphql/graphiql/blob/main/packages/graphiql/README.md), an in-browser IDE. - -Using @defer with GraphiQL - -## Incremental responses - -If you're using the proposed `incremental: ...` response syntax ([proposal](https://github.com/graphql/graphql-spec/pull/742), [Ruby support](/defer/setup.html#example-rails-with-apollo-client)), you'll need a custom "fetcher" function to handle the `incremental: ...` part of the response. For example: - -```js -import { meros } from "meros"; // for handling multipart responses - -const customFetcher = async function* (graphqlParams, fetcherOpts) { - // Make the initial fetch - var result = await fetch("/graphql", { - method: "POST", - body: JSON.stringify(graphqlParams), - headers: { - 'content-type': 'application/json', - } - }).then((r) => { - // Use meros to turn multipart responses into streams - return meros(r, { multiple: true }) - }) - - if (!isAsyncIterable(result)) { - // Return plain responses as promises - return result.json() - } else { - // Handle multipart responses one chunk at a time - for await (const chunk of result) { - yield chunk.map(part => { - // Move the incremental part of the response into top-level - // This assumes there's only one `incremental` entry - // which is currently true for GraphQL-Pro's @defer implementation - var newJson = {...part.body} - if (newJson.incremental) { - newJson.data = newJson.incremental[0].data - newJson.path = newJson.incremental[0].path - delete newJson.incremental - } - return newJson - }); - } - } -} - -// Helper for checking for a multipart response: -function isAsyncIterable(input) { - return ( - typeof input === "object" && - input !== null && - ( - input[Symbol.toStringTag] === "AsyncGenerator" || - (Symbol.asyncIterator && Symbol.asyncIterator in input) - ) - ); -} - -``` - -Hopefully a new GraphiQL version will support this out of the box; follow the [issue on GitHub](https://github.com/graphql/graphiql/issues/3470). diff --git a/vendor/gems/graphql/guides/defer/overview.md b/vendor/gems/graphql/guides/defer/overview.md deleted file mode 100644 index 0b58e7b915d..00000000000 --- a/vendor/gems/graphql/guides/defer/overview.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - Defer -title: Overview -desc: What is @defer, and why use it? -index: 0 -pro: true ---- - -`@defer` is a {% internal_link "directive", "/type_definitions/directives" %} for streaming GraphQL responses from the server to the client. - -By streaming the response, the server can send the most critical (or most available) data _first_, following up with secondary data shortly afterward. - -`@defer` was first described by [Lee Byron at React Europe 2015](https://youtu.be/ViXL0YQnioU?t=768) and got experimental support in [Apollo in 2018](https://blog.apollographql.com/introducing-defer-in-apollo-server-f6797c4e9d6e). - -`@stream` is like `@defer`, but it returns list items one at a time. Find details in the {% internal_link "Stream guide", "/defer/stream" %}. - -## Example - -GraphQL queries can be large and complex, requiring lots of computation or dependencies on slow external services. - -In this example, the local server maintains an index of items ("decks"), but the item data ("cards") is actually hosted on a remote server. So, GraphQL queries must make remote calls in order to serve that data. - -Without `@defer`, the whole query is blocked until the last field is done resolving: - -{{ "https://user-images.githubusercontent.com/2231765/53442028-4a122b00-39d6-11e9-8e33-b91791bf3b98.gif" | link_to_img:"Rails without defer" }} - -But, we can add `@defer` to slow fields: - -```diff - deck { - slots { - quantity -- card -+ card @defer { - name - price - } - } - } -``` - -Then, the response will stream to the client bit by bit, so the page can load progressively: - -{{ "https://user-images.githubusercontent.com/2231765/53442027-4a122b00-39d6-11e9-8d7b-feb7a4f7962a.gif" | link_to_img:"Rails with defer" }} - - -This way, clients get a snappy feel from the app even while data is still loading. - -View the full demo at https://github.com/rmosolgo/graphql_defer_example. - -## Considerations - -- `@defer` adds some overhead to the response, so only apply it judiciously. -- `@defer` is single-threaded. `@defer`ed fields are still evaluated in sequence, but in a chunk-by-chunk way. - -## Next Steps - -{% internal_link "Set up your server", "/defer/setup" %} to support `@defer` or read about {% internal_link "client usage", "/defer/usage" %} of it. diff --git a/vendor/gems/graphql/guides/defer/setup.md b/vendor/gems/graphql/guides/defer/setup.md deleted file mode 100644 index fc8895c3bf7..00000000000 --- a/vendor/gems/graphql/guides/defer/setup.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - Defer -title: Server Setup -desc: Configuring the schema and server to use @defer -index: 1 -pro: true ---- - -Before using `@defer` in queries, you have to: - -- Update `graphql` and `graphql-pro` gems -- Add `@defer` to your GraphQL schema -- Update your HTTP handlers (eg, Rails controllers) to send streaming responses -- Optionally, customize `@defer` to work with GraphQL-Batch - -You can also see a [full Rails & Apollo-Client demo](https://github.com/rmosolgo/graphql_defer_example). - -## Updating the gems - -GraphQL-Ruby 1.9+ and GraphQL-Pro 1.10+ are required: - -```ruby -gem "graphql", "~>1.9" -gem "graphql-pro", "~>1.10" -``` - -And then install them: - -``` -$ bundle update graphql graphql-pro -``` - -## Adding `@defer` to your schema - -Then, add `GraphQL::Pro::Defer` to your schema as a plugin: - -```ruby -class MySchema < GraphQL::Schema - use GraphQL::Pro::Defer -end -``` - -This will: - -- Attach a {% internal_link "custom directive", "/type_definitions/directives" %} called `@defer` -- Add instrumentation to queries to track deferred work and execute it later - -## Sending streaming responses - -Many web frameworks have support for streaming responses, for example: - -- Rails has [ActionController::Live](https://api.rubyonrails.org/classes/ActionController/Live.html) -- Sinatra has [Sinatra::Streaming](http://sinatrarb.com/contrib/streaming.html) -- Hanami::Controller can [stream responses](https://github.com/hanami/controller#streamed-responses) - -See below for how to integrate GraphQL's deferred patches with a streaming response API. - -To investigate support with a web framework, please {% open_an_issue "Server support for @defer with ..." %} or email `support@graphql.pro`. - -### Checking for deferrals - -When a query has any `@defer`ed fields, you can check for `context[:defer]`: - -```ruby -if context[:defer] - # some fields were `@defer`ed -else - # normal GraphQL, no `@defer` -end -``` - -### Working with deferrals - -To handle deferrals, you can enumerate over `context[:defer]`, for example: - -```ruby -context[:defer].each do |deferral| - # do something with the `deferral`, eg - # stream_to_client(deferral.to_h) -end -``` - -The initial result is _also_ present in the deferrals, so you can treat it just like a patch. - -Each deferred patch has a few methods for building a response: - -- `.to_h` returns a hash with `path:`, `data:`, and/or `errors:`. (There is no `path:` for the root result.) -- `.to_http_multipart(incremental: true)` returns a string which works with Apollo client's `@defer` support. (Use `incremental: true` to format patches for the forthcoming spec.) -- `.path` returns the path to this patch in the response -- `.data` returns successfully-resolved results of the patch -- `.errors` returns an array of errors, if there were any - -Calling `.data` or `.errors` on a deferral will resume GraphQL execution until the patch is complete. - -### Example: Rails with Apollo Client - -In this example, a Rails controller will stream HTTP Multipart patches to the client, in Apollo Client's supported format. - -```ruby -class GraphqlController < ApplicationController - # Support `response.stream` below: - include ActionController::Live - - def execute - # ... - result = MySchema.execute(query, variables: variables, context: context, operation_name: operation_name) - - # Check if this is a deferred query: - if (deferred = result.context[:defer]) - # Required for Rack 2.2+, see https://github.com/rack/rack/issues/1619 - response.headers['Last-Modified'] = Time.now.httpdate - # Use built-in `stream_http_multipart` with Apollo-Client & ActionController::Live - deferred.stream_http_multipart(response, incremental: true) - else - # Return a plain, non-deferred result - render json: result - end - ensure - # Always make sure to close the stream - response.stream.close - end -end -``` - -You can also investigate a [full Rails & Apollo-Client demo](https://github.com/rmosolgo/graphql_defer_example) - -## With GraphQL-Batch - -`GraphQL-Batch` is a third-party data loading library that wraps GraphQL-Ruby execution. Deferred resolution happens outside the normal execution flow, so to work with GraphQL-Batch, you have to customize `GraphQL::Pro::Defer` a bit. Also, you'll need GraphQL-Pro `v1.24.6` or later. Here's a custom `Defer` implementation: - -```ruby -# app/graphql/directives/defer.rb -module Directives - # Modify the library's `@defer` implementation to work with GraphQL-Batch - class Defer < GraphQL::Pro::Defer - def self.resolve(obj, arguments, context, &block) - # While the query is running, store the batch executor to re-use later - context[:graphql_batch_executor] ||= GraphQL::Batch::Executor.current - super - end - - class Deferral < GraphQL::Pro::Defer::Deferral - def resolve - # Before calling the deferred execution, - # set GraphQL-Batch back up: - prev_executor = GraphQL::Batch::Executor.current - GraphQL::Batch::Executor.current ||= @context[:graphql_batch_executor] - super - ensure - # Clean up afterward: - GraphQL::Batch::Executor.current = prev_executor - end - end - end -end -``` - -And update your schema to use your custom defer implementation: - -```ruby -# Use our GraphQL-Batch-compatible defer: -use Directives::Defer -``` -## Next Steps - -Read about {% internal_link "client usage", "/defer/usage" %} of `@defer`. diff --git a/vendor/gems/graphql/guides/defer/stream.md b/vendor/gems/graphql/guides/defer/stream.md deleted file mode 100644 index 6dd3d1ac150..00000000000 --- a/vendor/gems/graphql/guides/defer/stream.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - Defer -title: Stream -desc: Using @stream to receive list items one at a time -index: 3 -pro: true ---- - -`@stream` works very much like `@defer`, except it only applies to list fields. When a field has `@stream` and it returns a list, then each item in the list is returned to the client as a patch. `@stream` is described in a [proposal to the GraphQL specification](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md). - -__Note:__ `@stream` was added in GraphQL-Pro 1.21.0 and requires GraphQL-Ruby 1.13.6+. - -## Installation - -To support `@stream` in your schema, add it with `use GraphQL::Pro::Stream`: - -```ruby -class MySchema < GraphQL::Schema - # ... - use GraphQL::Pro::Stream -end -``` - -Additionally, you should update your controller to handle deferred parts of the response. See the {% internal_link "@defer setup guide", "defer/setup#sending-streaming-responses" %} for details. (`@stream` uses the same deferral pipeline as `@defer`, so the same setup instructions apply.) - -## Usage - -After that, you can include `@stream` in your queries, for example: - -```ruby -{ - # Send each movie in its own patch: - nowPlaying @stream { - title - director { name } - } -} -``` - -If `@stream` is applied to non-list fields, it's ignored. - -`@stream` supports several arguments: - -- `if: Boolean = true`: when `false`, the list is _not_ streamed. Instead, all items are returned synchronously. -- `label: String`: if present, the given string is returned in patches as `"label": "..."` -- `initialCount: Int = 0`: this number of list items are returned synchronously. (If the list is shorter than `initialCount`, then the whole list is returned synchronously.) diff --git a/vendor/gems/graphql/guides/defer/usage.md b/vendor/gems/graphql/guides/defer/usage.md deleted file mode 100644 index 92925da3fec..00000000000 --- a/vendor/gems/graphql/guides/defer/usage.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - Defer -title: Usage -desc: Using @defer on the client side -index: 2 -pro: true ---- - - -`@defer` is a [GraphQL directive](https://graphql.org/learn/queries/#directives) which instructs the server to execute the field in a special way: - -```graphql -query GetPlayerInfo($handle: String!){ - player(handle: $handle) { - name - # Send this field later, to avoid slowing down the initial response: - topScore(from: 2000, to: 2020) @defer - } -} -``` - -The directives `@skip` and `@include` are built into any GraphQL server and client, but `@defer` requires special attention. - -Apollo-Client [currently supports the @defer directive](https://www.apollographql.com/docs/react/data/defer/). - -`@defer` also accepts a `label:` option which will be included in outgoing patches when it's present in the query (eg, `@defer(label: "patch1")`). - -Want to use `@defer` with another client? Please {% open_an_issue "Client support for @defer with ..." %} or email `support@graphql.pro` and we'll dig in! - -## Next Steps - -{% internal_link "Set up your server", "/defer/setup" %} to support `@defer`. diff --git a/vendor/gems/graphql/guides/development.md b/vendor/gems/graphql/guides/development.md deleted file mode 100644 index e17d09f8e85..00000000000 --- a/vendor/gems/graphql/guides/development.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Development -section: Other -desc: Hacking on GraphQL Ruby ---- - -So, you want to hack on GraphQL Ruby! Here are some tips for getting started. - -- [Setup](#setup) your development environment -- [Run the tests](#running-the-tests) to verify your setup -- [Debug](#debugging-with-pry) with pry -- [Run the benchmarks](#running-the-benchmarks) to test performance in your environment -- [Coding guidelines](#coding-guidelines) for working on your contribution -- Special tools for building the [lexer and parser](#lexer-and-parser) -- Building and publishing the [GraphQL Ruby website](#website) -- [Versioning](#versioning) describes how changes are managed and released -- [Releasing](#releasing) Gem versions - -## Setup - -Get your own copy of graphql-ruby by forking [`rmosolgo/graphql-ruby` on GitHub](https://github.com/rmosolgo/graphql-ruby) and cloning your fork. - -Then, install the dependencies: - -- Install SQLite3 and MongoDB (eg, `brew install sqlite && brew tap mongodb/brew && brew install mongodb-community`) -- `bundle install` -- `rake compile # If you get warnings at this step, you can ignore them.` -- Optional: [Ragel](https://www.colm.net/open-source/ragel/) is required to build the lexer - -## Running the Tests - -### Unit tests - -You can run the tests with - -``` -bundle exec rake # tests & Rubocop -bundle exec rake test # tests only -``` - -You can run a __specific file__ with `TEST=`: - -``` -bundle exec rake test TEST=spec/graphql/query_spec.rb -# run tests in `query_spec.rb` only -``` - -You can focus on a __specific example__ with `focus`: - -```ruby -focus -it "does something cool" do - # ... -end -``` - -Then, only `focus`ed tests will run: - -``` -bundle exec rake test -# only the focused test will be run -``` - -(This is provided by `minitest-focus`.) - -### Integration tests - -You need to pick a specific gemfile from gemfiles/ to run integration tests. For example: - -``` -BUNDLE_GEMFILE=gemfiles/rails_6.1.gemfile bundle install -BUNDLE_GEMFILE=gemfiles/rails_6.1.gemfile bundle exec rake test TEST=spec/integration/rails/graphql/relay/array_connection_spec.rb -``` - -### GraphQL-CParser tests - -To test the `graphql_cparser` gem, you have to build the binary first: - -``` -bundle exec rake build_ext -``` - -Then, run the test suite with `GRAPHQL_CPARSER=1`: - -``` -GRAPHQL_CPARSER=1 bundle exec rake test -``` - -(Add `TEST=` to pick a certain file.) - - -### Other tests - -There are system tests for checking ActionCable behavior, use: - -``` -bundle exec rake test:system -``` - -And JavaScript tests: - -``` -bundle exec rake test:js -``` - -## Gemfiles, Gemfiles, Gemfiles - -`graphql-ruby` has several gemfiles to ensure support for various Rails versions. You can specify a gemfile with `BUNDLE_GEMFILE`, eg: - -``` -BUNDLE_GEMFILE=gemfiles/rails_5.gemfile bundle exec rake test -``` - -## Debugging with Pry - -[`pry`](https://pryrepl.org/) is included with GraphQL-Ruby's development setup to help with debugging. - -To pause execution in Ruby code, add: - -```ruby -binding.pry -``` - -Then, the program will pause and your terminal will become a Ruby REPL. Feel free to use `pry` in your development process! - -## Running the Benchmarks - -This project includes some Rake tasks to record benchmarks: - -```sh -$ bundle exec rake -T | grep bench: -rake bench:profile # Generate a profile of the introspection query -rake bench:query # Benchmark the introspection query -rake bench:validate # Benchmark validation of several queries -``` - -You can save results by sending the output into a file: - -```sh -$ bundle exec rake bench:validate > before.txt -$ cat before.txt -# ... -# --> benchmark output here -``` - -If you want to check performance, create a baseline by running these tasks before your changes. Then, make your changes and run the tasks again and compare your results. - -Keep these points in mind when using benchmarks: - -- The results are hardware-specific: computers with different hardware will have different results. So don't compare your results to results from other computers. -- The results are environment-specific: CPU and memory availability are affected by other processes on your computer. So try to create similar environments for your before-and-after testing. - -## Coding Guidelines - -GraphQL-Ruby uses a thorough test suite to make sure things work reliably day-after-day. Please include tests that describe your changes, for example: - -- If you contribute a bug fix, include a test for the code that _was_ broken (and is now fixed) -- If you contribute a feature, include tests for all intended uses of that feature -- If you modify existing behavior, update the tests to cover all intended behaviors for that code - -Don't fret about coding style or organization. There's a minimal Rubocop config in `.rubocop.yml` which runs during CI. You can run it manually with `bundle exec rake rubocop`. - -## Website - -To update the website, update the `.md` files in `guides/`. - -To preview your changes, you can serve the website locally: - -``` -bundle exec rake site:serve -``` - -Then visit `http://localhost:4000`. - -To publish the website with GitHub pages, run the Rake task: - -``` -bundle exec rake site:publish -``` - -### Search Index - -GraphQL-Ruby's search index is powered by Algolia. To update the index, you need the API key in an environment variable: - -``` -$ export ALGOLIA_API_KEY=... -``` - -Without this key, the search index will fall out-of-sync with the website. Contact @rmosolgo to gain access to this key. - -### API Docs - -The GraphQL-Ruby website has its own rendered version of the gem's API docs. They're pushed to GitHub pages with a special process. - -First, generate local copies of the docs you want to publish: - -``` -$ bundle exec rake apidocs:gen_version[1.8.0] # for example, generate docs that you want to publish -``` - -Then, check them out locally: - -``` -$ bundle exec rake site:serve -# then visit http://localhost:4000/api-doc/1.8.0/ -``` - -Then, publish them as part of the whole site: - -``` -$ bundle exec rake site:publish -``` - -Finally, check your work by visiting the docs on the website. - -## Versioning - -GraphQL-Ruby does _not_ attempt to deliver "semantic versioning" for the reasons described in `jashkenas`' -s post, ["Why Semantic Versioning Isn't"](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e). Instead, the following scheme is used as a guideline: - -- Version numbers consist of three parts, `MAJOR.MINOR.PATCH` -- __`PATCH`__ version indicates bug fixes or small features for specific use cases. Ideally, you can upgrade patch versions with only a quick skim of the changelog. -- __`MINOR`__ version indicates significant additions, internal refactors, or small breaking changes. When upgrading a minor version, check the changelog for any new features or breaking changes which apply to your system. The changelog will always include an upgrade path for any breaking changes. Minor versions may also include deprecation warnings to warn about upcoming breaking changes. -- __`MAJOR`__ version indicates significant breaking changes. Do not expect code to run without some modification, especially if the code yielded deprecation warnings. - -This policy is inspired by the [Ruby 2.1.0+ version policy](https://www.ruby-lang.org/en/news/2013/12/21/ruby-version-policy-changes-with-2-1-0/). - -Pull requests and issues may be tagged with a [GitHub milestone](https://github.com/rmosolgo/graphql-ruby/milestones) to denote when they'll be released. - -The [changelog](https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md) should always contain accurate and thorough information so that users can upgrade. If you have trouble upgrading based on the changelog, please open an issue on GitHub. - -## Releasing - -GraphQL-Ruby doesn't have a strict release schedule. If you think it should, consider opening an issue to share your thoughts. - -To cut a release: - -- Update `CHANGELOG.md` for the new version: - - Add a new heading for the new version, and paste the four categories of changes into the new section - - Open the GitHub milestone corresponding to the new version - - Check each pull request and put it in the category (or categories) that it belongs in - - If a change affects the default behavior of GraphQL-Ruby in a disruptive way, add it to `## Breaking Changes` and include migration notes if possible - - Include the PR number beside the change description for future reference -- Update `lib/graphql/version.rb` with the new version number -- Commit changes to master -- Push changes to GitHub: `git push origin master`. GitHub Actions will update the website. -- Release to RubyGems: `bundle exec rake release`. This will also push the tag to GitHub which will kick off a GitHub Actions job to update the API docs. -- Celebrate 🎊 ! diff --git a/vendor/gems/graphql/guides/errors/error_handling.md b/vendor/gems/graphql/guides/errors/error_handling.md deleted file mode 100644 index 0b49bb31221..00000000000 --- a/vendor/gems/graphql/guides/errors/error_handling.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Errors -title: Error Handling -desc: Rescuing application errors from field resolvers -index: 3 ---- - -You can configure your schema to rescue application errors during field resolution. Errors during batch loading will also be rescued. - -Thanks to [`@exAspArk`](https://github.com/exaspark) for the [`graphql-errors`](https://github.com/exAspArk/graphql-errors) gem which inspired this behavior and [`@thiago-sydow`](https://github.com/thiago-sydow) who [suggested](https://github.com/rmosolgo/graphql-ruby/issues/2139#issuecomment-524913594) an implementation like this. - -## Add error handlers - -Handlers are added with `rescue_from` configurations in the schema: - -```ruby -class MySchema < GraphQL::Schema - # ... - - rescue_from(ActiveRecord::RecordNotFound) do |err, obj, args, ctx, field| - # Raise a graphql-friendly error with a custom message - raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found" - end - - rescue_from(SearchIndex::UnavailableError) do |err, obj, args, ctx, field| - # Log the error - Bugsnag.notify(err) - # replace it with nil - nil - end -end -``` - -The handler is called with several arguments: - -- __`err`__ is the error that was raised during field execution, then rescued -- __`obj`__ is the object which was having a field resolved against it -- __`args`__ is the Hash of arguments passed to the resolver -- __`ctx`__ is the query context -- __`field`__ is the {{ "GraphQL::Schema::Field" | api_doc }} instance for the field where the error was rescued - -Inside the handler, you can: - -- Raise a GraphQL-friendly {{ "GraphQL::ExecutionError" | api_doc }} to return to the user -- Re-raise the given `err` to crash the query and halt execution. (The error will propagate to your application, eg, the controller.) -- Report some metrics from the error, if applicable -- Return a new value to be used for the error case (if not raising another error) diff --git a/vendor/gems/graphql/guides/errors/execution_errors.md b/vendor/gems/graphql/guides/errors/execution_errors.md deleted file mode 100644 index 8f5cc0c974c..00000000000 --- a/vendor/gems/graphql/guides/errors/execution_errors.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Errors -title: Top-level "errors" -desc: The top-level "errors" array and how to use it. -index: 1 ---- - -The GraphQL specification [allows for a top-level `"errors"` key](https://graphql.github.io/graphql-spec/June2018/#sec-Errors) in the response which may contain information about what went wrong during execution. For example: - -```ruby -{ - "errors" => [ ... ] -} -``` - -The response may include _both_ `"data"` and `"errors"` in the case of a partial success: - -```ruby -{ - "data" => { ... } # parts of the query that ran successfully - "errors" => [ ... ] # errors that prevented some parts of the query from running -} -``` - -## When to Use Top-Level Errors - -In general, top-level errors should only be used for exceptional circumstances when a developer should be made aware that the system had some kind of problem. - -For example, the GraphQL specification says that when a non-null field returns `nil`, an error should be added to the `"errors"` key. This kind of error is not recoverable by the client. Instead, something on the server should be fixed to handle this case. - -When you want to notify a client some kind of recoverable issue, consider making error messages part of the schema, for example, as in {% internal_link "mutation errors", "/mutations/mutation_errors" %}. - -## Adding Errors to the Array - -In GraphQL-Ruby, you can add entries to this array by raising `GraphQL::ExecutionError` (or a subclass of it), for example: - -```ruby -raise GraphQL::ExecutionError, "Can't continue with this query" -``` - -When this error is raised, its `message` will be added to the `"errors"` key and GraphQL-Ruby will automatically add the `line`, `column` and `path` to it. So, the above error might be: - -```ruby -{ - "errors" => [ - { - "message" => "Can't continue with this query", - "locations" => [ - { - "line" => 2, - "column" => 10, - } - ], - "path" => ["user", "login"], - } - ] -} -``` - -## Customizing Error JSON - -The default error JSON includes `"message"`, `"locations"` and `"path"`. The [forthcoming version](https://spec.graphql.org/draft/#example-fce18) of the GraphQL spec recommends putting custom data in the `"extensions"` key of the error JSON. - -You can customize this in two ways: - -- Pass `extensions:` when raising an error, for example: - ```ruby - raise GraphQL::ExecutionError.new("Something went wrong", extensions: { "code" => "BROKEN" }) - ``` - In this case, `"extensions" => { "code" => "BROKEN" }` will be added to the error JSON. - -- Override `#to_h` in a subclass of `GraphQL::ExecutionError`, for example: - ```ruby - class ServiceUnavailableError < GraphQL::ExecutionError - def to_h - super.merge({ "extensions" => {"code" => "SERVICE_UNAVAILABLE"} }) - end - end - ``` - Now, `"extensions" => { "code" => "SERVICE_UNAVAILABLE" }` will be added to the error JSON. diff --git a/vendor/gems/graphql/guides/errors/overview.md b/vendor/gems/graphql/guides/errors/overview.md deleted file mode 100644 index ff5f6d1e2b6..00000000000 --- a/vendor/gems/graphql/guides/errors/overview.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Errors -title: Errors in GraphQL -desc: A conceptual introduction to errors in GraphQL -index: 0 -redirect_from: - - /schema/type_errors/ - - /queries/error_handling/ ---- - -There are a _lot_ of different kinds of errors in GraphQL! In this guide, we'll discuss some of the main categories and learn when they apply. - -## Validation Errors - -Because GraphQL is strongly typed, it performs validation of all queries before executing them. If an incoming query is invalid, it isn't executed. Instead, a response is sent back with `"errors"`: - -```ruby -{ - "errors" => [ ... ] -} -``` - -Each error has a message, line, column and path. - -The validation rules are part of the GraphQL specification and built into GraphQL-Ruby, so there's not really a way to customize this behavior, except to pass `validate: false` when executing a query, which skips validation altogether. - -You can configure your schema to stop validating after a certain number of errors by setting {{ "Schema.validate_max_errors" | api_doc }}. Also, you can add a timeout to this step with {{ "Schema.validate_timeout" | api_doc }}. - -## Analysis Errors - -GraphQL-Ruby supports pre-execution analysis, which may return `"errors"` instead of running a query. You can find details in the {% internal_link "Analysis guide", "queries/ast_analysis" %}. - -## GraphQL Invariants - -While GraphQL-Ruby is executing a query, some constraints must be satisfied. For example: - -- Non-null fields may not return `nil`. -- Interface and union types must resolve objects to types that belong to that interface/union. - -These constraints are part of the GraphQL specification, and when they are violated, it must be addressed somehow. Read more in {% internal_link "Type Errors", "/errors/type_errors" %}. - -## Top-level `"errors"` - -The GraphQL specification provides for a top-level `"errors"` key which may include information about errors during query execution. `"errors"` and `"data"` may _both_ be present in the case of a partial success. - -In your own schema, you can add to the `"errors"` key by raising `GraphQL::ExecutionError` (or subclasses of it) in your code. Read more in the {% internal_link "Execution Errors guide", "/errors/execution_errors" %}. - -## Handled Errors - -A schema can be configured to handle certain errors during field execution with handlers that you give it, using `rescue_from`. Read more in the {% internal_link "Error Handling guide", "/errors/error_handling" %}. - -## Unhandled Errors (Crashes) - -When a `raise`d error is not `rescue`d, the GraphQL query crashes entirely and the surrounding code (like a Rails controller) must handle the exception. - -For example, Rails will probably return a generic `500` page. - -## Errors as Data - -When you want end users (human beings) to read error messages, you can express errors _in the schema_, using normal GraphQL fields and types. In this approach, errors are strongly-typed data, queryable in the schema, like any other application data. - -For more about this approach, see {% internal_link "Mutation Errors", "/mutations/mutation_errors" %} diff --git a/vendor/gems/graphql/guides/errors/type_errors.md b/vendor/gems/graphql/guides/errors/type_errors.md deleted file mode 100644 index 0edc12c62b3..00000000000 --- a/vendor/gems/graphql/guides/errors/type_errors.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Errors -title: Type Errors -desc: Handling type errors -index: 3 ---- - -The GraphQL specification _requires_ certain assumptions to hold true when executing a query. However, it's possible that some code would violate that assumption, resulting in a type error. - -Here are two type errors that you can customize in GraphQL-Ruby: - -- A field with `null: false` returned `nil` -- A field returned a value as a union or interface, but that value couldn't be resolved to a member of that union or interface. - -You can specify behavior in these cases by defining a {{ "Schema.type_error" | api_doc }} hook: - -```ruby -class MySchema < GraphQL::Schema - def self.type_error(err, query_ctx) - # Handle a failed runtime type coercion - end -end -``` - -It is called with an instance of {{ "GraphQL::UnresolvedTypeError" | api_doc }} or {{ "GraphQL::InvalidNullError" | api_doc }} and the query context (a {{ "GraphQL::Query::Context" | api_doc }}). - -If you don't specify a hook, you get the default behavior: - -- Unexpected `nil`s add an error the response's `"errors"` key -- Unresolved Union / Interface types raise {{ "GraphQL::UnresolvedTypeError" | api_doc }} - -An object that fails type resolution is treated as `nil`. diff --git a/vendor/gems/graphql/guides/faq.md b/vendor/gems/graphql/guides/faq.md deleted file mode 100644 index eab9a195531..00000000000 --- a/vendor/gems/graphql/guides/faq.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: FAQ -other: true -desc: How to do common tasks ---- - - -Returning Route URLs -==================== -With GraphQL there is less of a need to include resource URLs to other REST resources, however sometimes you want to use Rails routing to include a URL as one of your fields. A common use case would be to build HTML format URLs to render a link in your React UI. In that case you can pass the request to your context, so that the helpers are able to build full URLs based on the incoming host, port and protocol. - -Example -------- -```ruby -class Types::UserType < Types::BaseObject - include ActionController::UrlFor - include Rails.application.routes.url_helpers - # Needed by ActionController::UrlFor to extract the host, port, protocol etc. from the current request - def request - context[:request] - end - # Needed by Rails.application.routes.url_helpers, it will then use the url_options defined by ActionController::UrlFor - def default_url_options - {} - end - - field :profile_url, String, null: false - def profile_url - user_url(object) - end -end - -# In your GraphQL controller, add the request to `context`: -MySchema.execute( - params[:query], - variables: params[:variables], - context: { - request: request - }, -) -``` - -Returning ActiveStorage blob URLs -================================= -If you are using ActiveStorage and need to return a URL to an attachment blob, you will find that using `Rails.application.routes.url_helpers.rails_blob_url` alone will throw an exception since Rails won't know what host, port or protocol to use in it. -You can include `ActiveStorage::SetCurrent` in your GraphQL controller to pass on this information into your resolvers. - -Example -======= - -```ruby -class GraphqlController < ApplicationController - include ActiveStorage::SetCurrent - ... -end - -class Types::UserType < Types::BaseObject - field :picture_url, String, null: false - def picture_url - Rails.application.routes.url_helpers.rails_blob_url( - object.picture, - protocol: ActiveStorage::Current.url_options[:protocol], - host: ActiveStorage::Current.url_options[:host], - port: ActiveStorage::Current.url_options[:port] - ) - end -end -``` diff --git a/vendor/gems/graphql/guides/fields/arguments.md b/vendor/gems/graphql/guides/fields/arguments.md deleted file mode 100644 index a6f38a7c191..00000000000 --- a/vendor/gems/graphql/guides/fields/arguments.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Fields -title: Arguments -desc: Fields may take arguments as inputs -index: 1 ---- - -Fields can take **arguments** as input. These can be used to determine the return value (eg, filtering search results) or to modify the application state (eg, updating the database in `MutationType`). - -Arguments are defined with the `argument` helper. These arguments are passed as [keyword arguments](https://robots.thoughtbot.com/ruby-2-keyword-arguments) to the resolver method: - -```ruby -field :search_posts, [PostType], null: false do - argument :category, String -end - -def search_posts(category:) - Post.where(category: category).limit(10) -end -``` - -## Nullability - -To make an argument optional, set `required: false`, and set default values for the corresponding keyword arguments: - -```ruby -field :search_posts, [PostType], null: false do - argument :category, String, required: false -end - -def search_posts(category: nil) - if category - Post.where(category: category).limit(10) - else - Post.all.limit(10) - end -end -``` - -Be aware that if all arguments are optional and the query does not provide any arguments, then the resolver method will be called with no arguments. To prevent an `ArgumentError` in this case, you must either specify default values for all keyword arguments (as done in the prior example) or use the double splat operator argument in the method definition. For example: - -```ruby -def search_posts(**args) - if args[:category] - Post.where(category: args[:category]).limit(10) - else - Post.all.limit(10) - end -end -``` - -### Default Values - -Another approach is to use `default_value: value` to provide a default value for the argument if it is not supplied in the query. - -```ruby -field :search_posts, [PostType], null: false do - argument :category, String, required: false, default_value: "Programming" -end - -def search_posts(category:) - Post.where(category: category).limit(10) -end -``` - -Arguments with `required: false` _do_ accept `null` as inputs from clients. This can be surprising in resolver code, for example, an argument with `Integer, required: false` can sometimes be `nil`. In this case, you can use `replace_null_with_default: true` to apply the given `default_value: ...` when clients provide `null`. For example: - -```ruby -# Even if clients send `query: null`, the resolver will receive `"*"` for this argument: -argument :query, String, required: false, default_value: "*", replace_null_with_default: true -``` - -Finally, `required: :nullable` will require clients to pass the argument, although it will accept `null` as a valid input. For example: - -```ruby -# This argument _must_ be given -- send `null` if there's no other appropriate value: -argument :email_address, String, required: :nullable -``` - - -## Deprecation - -**Experimental:** __Deprecated__ arguments can be marked by adding a `deprecation_reason:` keyword argument: - -```ruby -field :search_posts, [PostType], null: false do - argument :name, String, required: false, deprecation_reason: "Use `query` instead." - argument :query, String, required: false -end -``` - -## Aliasing - -Use `as: :alternate_name` to use a different key from within your resolvers while -exposing another key to clients. - -```ruby -field :post, PostType, null: false do - argument :post_id, ID, as: :id -end - -def post(id:) - Post.find(id) -end -``` - -## Preprocessing - -Provide a `prepare` function to modify or validate the value of an argument before the field's resolver method is executed: - -```ruby -field :posts, [PostType], null: false do - argument :start_date, String, prepare: ->(startDate, ctx) { - # return the prepared argument. - # raise a GraphQL::ExecutionError to halt the execution of the field and - # add the exception's message to the `errors` key. - } -end - -def posts(start_date:) - # use prepared start_date -end -``` - -## Automatic camelization - -Arguments that are snake_cased will be camelized in the GraphQL schema. Using the example of: - -```ruby -field :posts, [PostType], null: false do - argument :start_year, Int -end -``` - -The corresponding GraphQL query will look like: - -```graphql -{ - posts(startYear: 2018) { - id - } -} -``` - -To disable auto-camelization, pass `camelize: false` to the `argument` method. - -```ruby -field :posts, [PostType], null: false do - argument :start_year, Int, camelize: false -end -``` - -Furthermore, if your argument is already camelCased, then it will remain camelized in the GraphQL schema. However, the argument will be converted to snake_case when it is passed to the resolver method: - -```ruby -field :posts, [PostType], null: false do - argument :startYear, Int -end - -def posts(start_year:) - # ... -end -``` - -## Valid Argument Types - -Only certain types are valid for arguments: - -- {{ "GraphQL::Schema::Scalar" | api_doc }}, including built-in scalars (string, int, float, boolean, ID) -- {{ "GraphQL::Schema::Enum" | api_doc }} -- {{ "GraphQL::Schema::InputObject" | api_doc }}, which allows key-value pairs as input -- {{ "GraphQL::Schema::List" | api_doc }}s of a valid input type, configured using `[...]` -- {{ "GraphQL::Schema::NonNull" | api_doc }}s of a valid input type (arguments are non-null by default; use `required: false` to make optional arguments) diff --git a/vendor/gems/graphql/guides/fields/introduction.md b/vendor/gems/graphql/guides/fields/introduction.md deleted file mode 100644 index c84fd8d3d9e..00000000000 --- a/vendor/gems/graphql/guides/fields/introduction.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Fields -title: Introduction -desc: Implement fields and resolvers with the Ruby DSL -index: 0 ---- - - -Object fields expose data about that object or connect the object to other objects. You can add fields to your object types with the `field(...)` class method, for example: - -```ruby -field :name, String, "The unique name of this list", null: false -``` - -{% internal_link "Objects", "/type_definitions/objects" %} and {% internal_link "Interfaces", "/type_definitions/interfaces" %} have fields. - -The different elements of field definition are addressed below: - -- [Names](#field-names) identify the field in GraphQL -- [Return types](#field-return-type) say what kind of data this field returns -- [Documentation](#field-documentation) includes description, comments and deprecation notes -- [Resolution behavior](#field-resolution) hooks up Ruby code to the GraphQL field -- [Arguments](#field-arguments) allow fields to take input when they're queried -- [Extra field metadata](#extra-field-metadata) for low-level access to the GraphQL-Ruby runtime -- [Add default values for field parameters](#field-parameter-default-values) - -## Field Names - -A field's name is provided as the first argument or as the `name:` option: - -```ruby -field :team_captain, ... -# or: -field ..., name: :team_captain -``` - -Under the hood, GraphQL-Ruby **camelizes** field names, so `field :team_captain, ...` would be `{ teamCaptain }` in GraphQL. You can disable this behavior by adding `camelize: false` to your field definition or to the [default field options](#field-parameter-default-values). - -The field's name is also used as the basis of [field resolution](#field-resolution). - -## Field Return Type - -The second argument to `field(...)` is the return type. This can be: - -- A built-in GraphQL type (`Integer`, `Float`, `String`, `ID`, or `Boolean`) -- A GraphQL type from your application -- An _array_ of any of the above, which denotes a {% internal_link "list type", "/type_definitions/lists" %}. - -{% internal_link "Nullability", "/type_definitions/non_nulls" %} is expressed with the `null:` keyword: - -- `null: true` (default) means that the field _may_ return `nil` -- `null: false` means the field is non-nullable; it may not return `nil`. If the implementation returns `nil`, GraphQL-Ruby will return an error to the client. - -Additionally, list types maybe nullable by adding `[..., null: true]` to the definition. - -Here are some examples: - -```ruby -field :name, String # `String`, may return a `String` or `nil` -field :id, ID, null: false # `ID!`, always returns an `ID`, never `nil` -field :teammates, [Types::User], null: false # `[User!]!`, always returns a list containing `User`s -field :scores, [Integer, null: true] # `[Int]`, may return a list or `nil`, the list may contain a mix of `Integer`s and `nil`s -``` - -## Field Documentation - -Fields may be documented with a __description__, __comment__ and may be __deprecated__. - -__Descriptions__ can be added with the `field(...)` method as a positional argument, a keyword argument, or inside the block: - -```ruby -# 3rd positional argument -field :name, String, "The name of this thing", null: false - -# `description:` keyword -field :name, String, null: false, - description: "The name of this thing" - -# inside the block -field :name, String, null: false do - description "The name of this thing" -end -``` - -__Comments__ can be added with the `field(...)` method as a keyword argument, or inside the block: -```ruby -# `comment:` keyword -field :name, String, null: false, comment: "Rename to full name" - -# inside the block -field :name, String, null: false do - comment "Rename to full name" -end -``` - -Generates field name with comment above "Rename to full name" above. - -```graphql -type Foo { - # Rename to full name - name: String! -} -``` - -__Deprecated__ fields can be marked by adding a `deprecation_reason:` keyword argument: - -```ruby -field :email, String, - deprecation_reason: "Users may have multiple emails, use `User.emails` instead." -``` - -Fields with a `deprecation_reason:` will appear as "deprecated" in GraphiQL. - -## Field Resolution - -In general, fields return Ruby values corresponding to their GraphQL return types. For example, a field with the return type `String` should return a Ruby string, and a field with the return type `[User!]!` should return a Ruby array with zero or more `User` objects in it. - -By default, fields return values by: - -- Trying to call a method on the underlying object; _OR_ -- If the underlying object is a `Hash`, lookup a key in that hash. -- An optional `:fallback_value` can be supplied that will be used if the above fail. - -The method name or hash key corresponds to the field name, so in this example: - -```ruby -field :top_score, Integer, null: false -``` - -The default behavior is to look for a `#top_score` method, or lookup a `Hash` key, `:top_score` (symbol) or `"top_score"` (string). - -You can override the method name with the `method:` keyword, or override the hash key(s) with the `hash_key:` or `dig:` keyword, for example: - -```ruby -# Use the `#best_score` method to resolve this field -field :top_score, Integer, null: false, - method: :best_score - -# Lookup `hash["allPlayers"]` to resolve this field -field :players, [User], null: false, - hash_key: "allPlayers" - -# Use the `#dig` method on the hash with `:nested` and `:movies` keys -field :movies, [Movie], null: false, - dig: [:nested, :movies] -``` - -To pass-through the underlying object without calling a method on it, you can use `method: :itself`: - -```ruby -field :player, User, null: false, - method: :itself -``` - -This is equivalent to: - -```ruby -field :player, User, null: false - -def player - object -end -``` - -If you don't want to delegate to the underlying object, you can define a method for each field: - -```ruby -# Use the custom method below to resolve this field -field :total_games_played, Integer, null: false - -def total_games_played - object.games.count -end -``` - -Inside the method, you can access some helper methods: - -- `object` is the underlying application object (formerly `obj` to resolve functions) -- `context` is the query context (passed as `context:` when executing queries, formerly `ctx` to resolve functions) - -Additionally, when you define arguments (see below), they're passed to the method definition, for example: - -```ruby -# Call the custom method with incoming arguments -field :current_winning_streak, Integer, null: false do - argument :include_ties, Boolean, required: false, default_value: false -end - -def current_winning_streak(include_ties:) - # Business logic goes here -end -``` - -As the examples above show, by default the custom method name must match the field name. If you want to use a different custom method, the `resolver_method` option is available: - -```ruby -# Use the custom method with a non-default name below to resolve this field -field :total_games_played, Integer, null: false, resolver_method: :games_played - -def games_played - object.games.count -end -``` - -`resolver_method` has two main use cases: - -1. resolver re-use between multiple fields -2. dealing with method conflicts (specifically if you have fields named `context` or `object`) - -Note that `resolver_method` _cannot_ be used in combination with `method` or `hash_key`. - -## Field Arguments - -_Arguments_ allow fields to take input to their resolution. For example: - -- A `search()` field may take a `term:` argument, which is the query to use for searching, eg `search(term: "GraphQL")` -- A `user()` field may take an `id:` argument, which specifies which user to find, eg `user(id: 1)` -- An `attachments()` field may take a `type:` argument, which filters the result by file type, eg `attachments(type: PHOTO)` - -Read more in the {% internal_link "Arguments guide", "/fields/arguments" %} - -## Extra Field Metadata - -Inside a field method, you can access some low-level objects from the GraphQL-Ruby runtime. Be warned, these APIs are subject to change, so check the changelog when updating. - -A few `extras` are available: - -- `ast_node` -- `graphql_name` (the field's name) -- `owner` (the type that this field belongs to) -- `lookahead` (see {% internal_link "Lookahead", "/queries/lookahead" %}) -- `execution_errors`, whose `#add(err_or_msg)` method should be used for adding errors -- `argument_details` (Interpreter only), an instance of {{ "GraphQL::Execution::Interpreter::Arguments" | api_doc }} with argument metadata -- `parent` (the previous `object` in the query) -- Custom extras, see below - -To inject them into your field method, first, add the `extras:` option to the field definition: - -```ruby -field :my_field, String, null: false, extras: [:ast_node] -``` - -Then add `ast_node:` keyword to the method signature: - -```ruby -def my_field(ast_node:) - # ... -end -``` - -At runtime, the requested runtime object will be passed to the field. - -__Custom extras__ are also possible. Any method on your field class can be passed to `extras: [...]`, and the value will be injected into the method. For example, `extras: [:owner]` will inject the object type who owns the field. Any new methods on your custom field class may be used, too. - -## Field Parameter Default Values - -The field method requires you to pass `null:` keyword argument to determine whether the field is nullable or not. For another field you may want to override `camelize`, which is `true` by default. You can override this behavior by adding a custom field with overwritten `camelize` option, which is `true` by default. - -```ruby -class CustomField < GraphQL::Schema::Field - # Add `null: false` and `camelize: false` which provide default values - # in case the caller doesn't pass anything for those arguments. - # **kwargs is a catch-all that will get everything else - def initialize(*args, null: false, camelize: false, **kwargs, &block) - # Then, call super _without_ any args, where Ruby will take - # _all_ the args originally passed to this method and pass it to the super method. - super - end -end -``` - -To use `CustomField` in your Objects and Interfaces, you'll need to register it as a `field_class` on those classes. See [Customizing Fields](https://graphql-ruby.org/type_definitions/extensions#customizing-fields) for more information on how to do so. diff --git a/vendor/gems/graphql/guides/fields/limits.md b/vendor/gems/graphql/guides/fields/limits.md deleted file mode 100644 index 0f03eb9bd86..00000000000 --- a/vendor/gems/graphql/guides/fields/limits.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Fields -title: Limits -desc: Always limit lists of items -index: 4 ---- - -## List Fields - -Always limit the number of items which can be returned from a list field. For example, use a `limit:` argument and make sure it's not too big. The `prepare:` function provides a convenient place to cap the number of items: - -```ruby -field :items, [Types::ItemType] do - # Cap the number of items at 30 - argument :limit, Integer, default_value: 20, prepare: ->(limit, ctx) {[limit, 30].min} -end - -def items(limit:) - object.items.limit(limit) -end -``` - -This way, you won't hit your database for 1000 items! - -## Connections - -Connections accept a {% internal_link "`max_page_size` option","/pagination/using_connections#max-page-size" %} which limits the number of nodes. diff --git a/vendor/gems/graphql/guides/fields/resolvers.md b/vendor/gems/graphql/guides/fields/resolvers.md deleted file mode 100644 index 4ba1320b984..00000000000 --- a/vendor/gems/graphql/guides/fields/resolvers.md +++ /dev/null @@ -1,212 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Fields -title: Resolvers -desc: Reusable, extendable resolution logic for complex fields -index: 2 -redirect_from: - - /fields/functions ---- - -A {{ "GraphQL::Schema::Resolver" | api_doc }} is a container for field signature and resolution logic. It can be attached to a field with the `resolver:` keyword: - -```ruby -# Use the resolver class to execute this field -field :pending_orders, resolver: PendingOrders -``` - -Under the hood, {{ "GraphQL::Schema::Mutation" | api_doc }} is a specialized subclass of `Resolver`. - -## First, ask yourself ... - -Do you really need a `Resolver`? Putting logic in a Resolver has some downsides: - -- Since it's coupled to GraphQL, it's harder to test than a plain ol' Ruby object in your app -- Since the base class comes from GraphQL-Ruby, it's subject to upstream changes which may require updates in your code - -Here are a few alternatives to consider: - -- Put display logic (sorting, filtering, etc.) into a plain ol' Ruby class in your app, and test that class -- Hook up that object with a method, for example: - -```ruby -field :recommended_items, [Types::Item], null: false -def recommended_items - ItemRecommendation.new(user: context[:viewer]).items -end -``` - -- If you have lots of arguments to share, use a class method to generate fields, for example: - -```ruby -# Generate a field which returns a filtered, sorted list of items -def self.items_field(name, override_options, &block) - # Prepare options - default_field_options = { type: [Types::Item], null: false } - field_options = default_field_options.merge(override_options) - # Create the field - field(name, **field_options) do - argument :order_by, Types::ItemOrder, required: false - argument :category, Types::ItemCategory, required: false - # Allow an override block to add more arguments - instance_eval(&block) if block_given? - end -end - -# Then use the generator to create a field: -items_field(:recommended_items) do |field| - field.argument :similar_to_product_id, ID, required: false -end -# Implement the field -def recommended_items - # ... -end -``` - -As a matter of code organization, that class method could be put in a module and shared between different classes that need it. - -- If you need the _same_ logic shared between several objects, consider using a Ruby module and its `self.included` hook, for example: - -```ruby -module HasRecommendedItems - def self.included(child_class) - # attach the field here - child_class.field(:recommended_items, [Types::Item], null: false) - end - - # then implement the field - def recommended_items - # ... - end -end - -# Add the field to some objects: -class Types::User < BaseObject - include HasRecommendedItems # adds the field -end -``` - -- If the module approach looks good to you, also consider {% internal_link "Interfaces", "/type_definitions/interfaces" %}. They also share behavior between objects (since they're just modules that get included, after all), and they expose that commonality to clients via introspection. - -## When do you really need a resolver? - -So, if there are other, better options, why does `Resolver` exist? Here are a few specific advantages: - -- __Isolation__. A `Resolver` is instantiated for each call to the field, so its instance variables are private to that object. If you need to use instance variables for some reason, this helps. You have a guarantee that those values won't hang around when the work is done. -- __Complex Schema Generation__. `RelayClassicMutation` (which is a `Resolver` subclass) generates input types and return types for each mutation. Using a `Resolver` class makes it easier to implement, share and extend this code generation logic. - -## Using `resolver` - -Use the base resolver class: - -```ruby -module Resolvers - class RecommendedItems < BaseResolver - type [Types::Item], null: false - description "Items this user might like" - - argument :order_by, Types::ItemOrder, required: false - argument :category, Types::ItemCategory, required: false - - def resolve(order_by: nil, category: nil) - # call your application logic here: - recommendations = ItemRecommendation.new( - viewer: context[:viewer], - recommended_for: object, - order_by: order_by, - category: category, - ) - # return the list of items - recommendations.items - end - end -end -``` - -And attach it to your field: - -```ruby -class Types::User < Types::BaseObject - field :recommended_items, resolver: Resolvers::RecommendedItems -end -``` - -Since the `Resolver` lifecycle is managed by the GraphQL runtime, the best way to test it is to execute GraphQL queries and check the results. - -### Nesting resolvers of the same type - -You may run into cyclical loading issues when using a resolver within the definition of the type the resolver returns e.g. - -```ruby -# app/graphql/types/query_type.rb - -module Types - class QueryType < Types::BaseObject - field :tasks, resolver: Resolvers::TasksResolver - end -end - -# app/graphql/types/task_type.rb - -module Types - class TaskType < Types::BaseObject - field :title, String, null: false - field :tasks, resolver: Resolvers::TasksResolver - end -end - -# app/graphql/resolvers/tasks_resolver.rb - -module Resolvers - class TasksResolver < BaseResolver - type [Types::TaskType], null: false - - def resolve - [] - end - end -end -``` - -The above can produce the following error: `Failed to build return type for Task.tasks from nil: Unexpected type input: (NilClass)`. - -A simple solution is to express the type as a string in the resolver: - -```ruby -module Resolvers - class TasksResolver < BaseResolver - type "[Types::TaskType]", null: false - - def resolve - [] - end - end -end -``` - -In doing so, you can defer the loading of the type class until the nested resolver has already been loaded. - -# Extensions - -In cases when you want your resolvers to add some extensions to the field they resolve, you can use `extension` method, which accepts extension class and options. Multiple extensions can be configured for a single resolver. - -```ruby -class GreetingExtension < GraphQL::Schema::FieldExtension - def resolve(object:, arguments:, **rest) - name = yield(object, arguments) - "#{options[:greeting]}, #{name}!" - end -end - -class ResolverWithExtension < BaseResolver - type String, null: false - - extension GreetingExtension, greeting: "Hi" - - def resolve - "Robert" - end -end -``` diff --git a/vendor/gems/graphql/guides/fields/validation.md b/vendor/gems/graphql/guides/fields/validation.md deleted file mode 100644 index a35af7711b7..00000000000 --- a/vendor/gems/graphql/guides/fields/validation.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Fields -title: Validation -desc: Rails-like validations for arguments -index: 3 ---- - -Arguments can be validated at runtime using built-in or custom validators. - -Validations are configured in `argument(...)` calls on fields or input objects: - -```ruby -argument :home_phone, String, - description: "A US phone number", - validates: { format: { with: /\d{3}-\d{3}-\d{4}/ } } -``` - -or, `validates required: { ... }` inside a `field ... do ... end` block: - -```ruby -field :comments, [Comment], - description: "Find comments by author ID or author name" do - argument :author_id, ID, required: false - argument :author_name, String, required: false - # Either `authorId` or `authorName` must be provided by the client, but not both: - validates required: { one_of: [:author_id, :author_name] } -end -``` - -Validations can be provided with a keyword (`validates: { ... }`) or with a method inside the configuration block (`validates ...`). - -## Built-In Validations - -See each validator's API docs for details: - -- `length: { maximum: ..., minimum: ..., is: ..., within: ... }` {{ "Schema::Validator::LengthValidator" | api_doc }} -- `format: { with: /.../, without: /.../ }` {{ "Schema::Validator::FormatValidator" | api_doc }} -- `numericality: { greater_than:, greater_than_or_equal_to:, less_than:, less_than_or_equal_to:, other_than:, odd:, even: }` {{ "Schema::Validator::NumericalityValidator" | api_doc }} -- `inclusion: { in: [...] }` {{ "Schema::Validator::InclusionValidator" | api_doc }} -- `exclusion: { in: [...] }` {{ "Schema::Validator::ExclusionValidator" | api_doc }} -- `required: { one_of: [...] }` {{ "Schema::Validator::RequiredValidator" | api_doc }} -- `allow_blank: true|false` {{ "Schema::Validator::AllowBlankValidator" | api_doc }} -- `allow_null: true|false` {{ "Schema::Validator::AllowNullValidator" | api_doc }} -- `all: { ... }` {{ "Schema::Validator::AllValidator" | api_doc }} - -Some of the validators accept customizable messages for certain validation failures; see the API docs for examples. - -`allow_blank:` and `allow_null:` may affect other validations, for example: - -```ruby -validates: { format: { with: /\A\d{4}\Z/ }, allow_blank: true } -``` - -Will permit any String containing four digits, or the empty string (`""`) if Rails is loaded. (GraphQL-Ruby checks for `.blank?`, which is usually defined by Rails.) - -Alternatively, they can be used alone, for example: - -```ruby -argument :id, ID, required: false, validates: { allow_null: true } -``` - -Will reject any query that passes `id: null`. - -## Custom Validators - -You can write custom validators, too. A validator is a class that extends `GraphQL::Schema::Validator`. It should implement: - -- `def initialize(..., **default_options)` to accept any validator-specific options and pass along the defaults to `super(**default_options)` -- `def validate(object, context, value)` which is called at runtime to validate `value`. It may return a String error message or an Array of Strings. GraphQL-Ruby will add those messages to the top-level `"errors"` array along with runtime context information. - -Then, custom validators can be attached either: - -- directly, passed to `validates`, like `validates: { MyCustomValidator => { some: :options }` -- by keyword, if the keyword is registered with `GraphQL::Schema::Validator.install(:custom, MyCustomValidator)`. (That would support `validates: { custom: { some: :options }})`.) - -Validators are initialized when the schema is constructed (at application boot), and `validate(...)` is called while executing the query. There's one `Validator` instance for each configuration on each field, argument, or input object. (`Validator` instances aren't shared.) diff --git a/vendor/gems/graphql/guides/getting_started.md b/vendor/gems/graphql/guides/getting_started.md deleted file mode 100644 index 5d88f4f462a..00000000000 --- a/vendor/gems/graphql/guides/getting_started.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Getting Started -section: Other -desc: Start here! ---- - -## Installation - -You can install `graphql` from RubyGems by adding to your application's `Gemfile`: - -```ruby -# Gemfile -gem "graphql" -``` - -Then, running `bundle install`: - -```sh -$ bundle install -``` - -## Getting Started - -On Rails, you can get started with a few [GraphQL generators](https://rmosolgo.github.io/graphql-ruby/schema/generators#graphqlinstall): - -```sh -# Add graphql-ruby boilerplate and mount graphiql in development -$ rails g graphql:install -# You may need to run bundle install again, as by default graphiql-rails is added on installation. -$ bundle install -# Make your first object type -$ rails g graphql:object Post title:String rating:Int comments:[Comment] -``` - -Or, you can build a GraphQL server by hand: - -- Define some types -- Connect them to a schema -- Execute queries with your schema - -### Declare types - -Types describe objects in your application and form the basis for [GraphQL's type system](https://graphql.org/learn/schema/#type-system). - -```ruby -# app/graphql/types/post_type.rb -module Types - class PostType < Types::BaseObject - description "A blog post" - field :id, ID, null: false - field :title, String, null: false - # fields should be queried in camel-case (this will be `truncatedPreview`) - field :truncated_preview, String, null: false - # Fields can return lists of other objects: - field :comments, [Types::CommentType], - # And fields can have their own descriptions: - description: "This post's comments, or null if this post has comments disabled." - end -end - -# app/graphql/types/comment_type.rb -module Types - class CommentType < Types::BaseObject - field :id, ID, null: false - field :post, PostType, null: false - end -end -``` - -### Build a Schema - -Before building a schema, you have to define an [entry point to your system, the "query root"](https://graphql.org/learn/schema/#the-query-and-mutation-types): - -```ruby -class QueryType < GraphQL::Schema::Object - description "The query root of this schema" - - field :post, resolver: Resolvers::PostResolver -end -``` - -Define how this field is resolved by creating a resolver class: - -```ruby -# app/graphql/resolvers/post_resolver.rb -module Resolvers - class PostResolver < BaseResolver - type Types::PostType, null: false - argument :id, ID - - def resolve(id:) - ::Post.find(id) - end - end -end -``` - -Then, build a schema with `QueryType` as the query entry point: - -```ruby -class Schema < GraphQL::Schema - query Types::QueryType -end -``` - -This schema is ready to serve GraphQL queries! {% internal_link "Browse the guides","/guides" %} to learn about other GraphQL Ruby features. - -### Execute queries - -You can execute queries from a query string: - -```ruby -query_string = " -{ - post(id: 1) { - id - title - truncatedPreview - } -}" -result_hash = Schema.execute(query_string) -# { -# "data" => { -# "post" => { -# "id" => 1, -# "title" => "GraphQL is nice" -# "truncatedPreview" => "GraphQL is..." -# } -# } -# } -``` - -See {% internal_link "Executing Queries","/queries/executing_queries" %} for more information about running queries on your schema. - -## Use with Relay - -If you're building a backend for [Relay](https://facebook.github.io/relay/), you'll need: - -- A JSON dump of the schema, which you can get by sending [`GraphQL::Introspection::INTROSPECTION_QUERY`](https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/introspection/introspection_query.rb) -- Relay-specific helpers for GraphQL, see the {% internal_link "Connection guide", "/pagination/connection_concepts" %}, {% internal_link "Mutation guide", "mutations/mutation_classes" %}, and {% internal_link "Object Identification guide", "/schema/object_identification" %}. - -## Use with Apollo Client - -[Apollo Client](https://www.apollographql.com/) is a full featured, simple to use GraphQL client with convenient integrations for popular view layers. You don't need to do anything special to connect Apollo Client to a `graphql-ruby` server. - -## Use with GraphQL.js Client - -[GraphQL.js Client](https://github.com/f/graphql.js) is a tiny client that is platform- and framework-agnostic. It works well with `graphql-ruby` servers, since GraphQL requests are simple query strings transported over HTTP. diff --git a/vendor/gems/graphql/guides/graphql-ruby-dark.png b/vendor/gems/graphql/guides/graphql-ruby-dark.png deleted file mode 100644 index 4e3241b4971067e03b9304e97e587864fd9737ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2287 zcmaKsdpuMBAIIlbSt<9*twPE4^;K^5qf&_NY-2k+mdiAS+`34juS+PR3zbVKUC1Rw zrX~?n5t6CY7ZI_Yxg<&n-@f^N&gjv9zsKY3?CkwIujlLa-eWrnj#N82yc!;d!^sit zZRp^g1zz@xq`*1no@^%$C;7mU%&-L?XTcE_HH#p#R#q+E#%~bh9g*0}wi-YXltLL~ z?HWdq5h``mmDq#W^g23zaDfI8I{}S`I#Walagerah)NwsNPTp>ekyeUAq(h46rqS5 z?T28f-;p?gPzPYLfJPD`_9zVk77nx7Lq+cnZ+gu!|B>ocS)}IghC5a=%iE|}?&F6B(W#&uH8L{(x zTWp2jLomk@!Ek;@aDl;pAHSsElmD;I&dyGqTx|`0yXa9m6~;+ON=eHsShz?Qzj%q< z(q-}rib~2Vs>@fbR8!Z`TCJ_4yLO$PKEYtaABIL7HyN9lnQyVM+`4VM)sCIkHnzL$ z?1?0ZOrbhB(&#V)aboUvc5!uMx$j|n?A_<-<-Om>*UvxTz`;X@1CIn93kf|Q9&sWn z=2YD2_=GcO&z(<9x^VGQa!P7idPe4zt6A5s-?*7`D>pB{;C5kA@tu;=yPWcS_qi1h zDj!x=KdPza)jfXlw7%h4V^j0y^^_pov(IBK z<^INH4>i_oIU-oPCC7|ObNNI2zUGxwI)jn8n|WvVF-_q*8)4zviWgZr3TrQAcwd{2 z__?%8bllyd{GTfs`3L^0^$Hn8lv)W$SPx;HoFOypB3!yE?A&96ur`?*3x`g>3S4ax z%>B9QOYP+_{bG@i%68X`gAH&F+3sLA0x@HQ6lzMd{esvU{KiPHNN+6e11l2yunrG;JH1@tw+OMmkqr~$&#X@{PB%73t|=h8_ex8+<6mcHnBX% zO`a(~c7Oia)HZc!v*j~==m4JYpG#1MNsqoh_+BN{J#LQ|LT8`oaONoapkE9GU%Rd-8ZUXL+$4LWEqT1xZt*M&a!zouW&BdK7fEM2|i9RB@FX2{U*SRVi1oY+AwP zuf|WiLRl;Ks#Yk8emzNp)v3Na#% zn(~I@i}RE=TP>3h-Q{$7$Jw08r8DXTui)*`KQnjSpqjBFqk`VNT+$(*{Nlh=%gEtW4D0@|^Z97l6taXAH>b3Hak&@?tNz83pTn9hhIOW~>aaVJ% z6_*>xDOs-zco7w9q4;_fe)xpnZNRNx&ZClI77M4@cVRvI|^TR~pE)NUGAUXZD1^nkX8%6-K!ot`{`&0#sx}bqlrQAIVu5B6(+|Efo>k(FpZpZrNtm{o_6i z+gIN-I|36Vt^3>Lk(Wm!79Mlk!0+2^VuRQS$E>USV`i8m{L=U*`)Df2^g?z=6K{Z~ z(i!P5yFs7!^1N;vv%Vspw^^mPT-v*E>XYu_*fV3gC-(@uQIt<{XGa`-U}tv$g-ueRD!P0%$p{tbN0NKr{i8{9 zfqvX3xoNWA-+xI@?s?zyJLmm6=iCAY3>YwAz<>b*1`HT5V8DO@0|pEjNCu>L2kzk& zSF()z1;fE01;qa)lX=~orh^yK)knR(ViH(PVPH6jPmrQ)t_n4Ln6Br4fZI%`tic~D zf^Q{YFf$!vR^F}&`S<4}ISkMSTg4Mh^h+=-m;Ct@1m?S%eD9{x#|CS8DU-C3fm^!h z|2<)j)>NH55=yO)vDYiabpV%N)(4~Rf+fGU_T;{noFd0-K+(WbNM}xj;O93U_V<_|>X1%q% z9E>M>X=Vm!?Yfd{1|`!+B#)Q$LJR<02a)$w(u&A}I7V(?I-?+wLgrvq@o?x-yqt_kp

T`k|Fpt%4Flj1S*O;UT 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 IM6N<$g20*0$^ZZW diff --git a/vendor/gems/graphql/guides/graphql-ruby.png b/vendor/gems/graphql/guides/graphql-ruby.png deleted file mode 100644 index 52eb69de34ec6a33ff414c3597f3f04e893012a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5271 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 NswqJgOXbaj{tE^|(Y^ox diff --git a/vendor/gems/graphql/guides/guides.html b/vendor/gems/graphql/guides/guides.html deleted file mode 100644 index 893114c7e1a..00000000000 --- a/vendor/gems/graphql/guides/guides.html +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Guides Index -sections: - - name: Schema - - name: Queries - - name: Type Definitions - - name: Authorization - - name: Fields - - name: Mutations - - name: Errors - - name: Pagination - - name: Relay - - name: Dataloader - - name: Subscriptions - - name: GraphQL Pro - - name: GraphQL Pro - OperationStore - - name: GraphQL Pro - Defer - - name: GraphQL Enterprise - Rate Limiters - - name: GraphQL Enterprise - Object Cache - - name: GraphQL Enterprise - Changesets - - name: JavaScript Client - - name: Language Tools - - name: Testing - - name: Other ---- - -

Guides

-
-
- {% assign sorted_pages = site.pages | sort: "index" %} - {% assign pages_with_index = ''|split:'' %} - {% assign pages_without_index = ''|split:'' %} - {% for guide in sorted_pages %} - {% if guide.layout == "guide" %} - {% if guide.index %} - {% assign pages_with_index = pages_with_index | push: guide %} - {% else %} - {% assign pages_without_index = pages_without_index | push: guide %} - {% endif %} - {% endif %} - {% endfor %} - - {% assign sorted_guides = pages_with_index | concat: pages_without_index %} - {% for section in page.sections %} -
-

{{ section.name }}

-
    - {% for guide in sorted_guides %} - {% if guide.section == section.name %} -
  • -

    - {{ guide.title }} - - {{ guide.desc }} - -

    -
  • - {% endif %} - {% endfor %} -
-
- {% endfor %} -
diff --git a/vendor/gems/graphql/guides/index.html b/vendor/gems/graphql/guides/index.html deleted file mode 100644 index 3d7546c4dad..00000000000 --- a/vendor/gems/graphql/guides/index.html +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: Welcome -fullwidth: false ---- -
-
- GraphQL Ruby Logo -

GraphQL Ruby

-
-
-

The graphql gem implements the GraphQL Server Specification in Ruby.

-

Use it to add a GraphQL API to your Ruby or Rails app.

-
-
-
-

Install the Gem

-

- Get going fast with the graphql gem, - battle-tested and trusted by GitHub, Shopify, Flexport, Chime, and Kickstarter. -

-{% highlight bash %} -# Download the gem: -bundle add graphql -# Setup with Rails: -rails generate graphql:install -{% endhighlight %} -
-
-

Define Your Schema

-

- Describe your application with a - GraphQL schema - to create a self-documenting, strongly-typed API. -

-{% highlight ruby %} -# app/graphql/types/profile_type.rb -class Types::ProfileType < Types::BaseObject - field :id, ID, null: false - field :name, String, null: false - field :avatar, Types::PhotoType -end -{% endhighlight %} -
-
-
-
-

Serve Queries

-

- Provide custom data to clients and extend your API with - {% internal_link "mutations", "/mutations/mutation_root" %}, - {% internal_link "subscriptions", "/subscriptions/overview" %}, - {% internal_link "streaming responses", "/defer/overview" %}, - and {% internal_link "multiplexing", "/queries/multiplex" %}. -

-{% highlight ruby %} -# app/controllers/graphql_controller.rb -result = MySchema.execute( - params[:query], - variables: params[:variables], - context: { current_user: current_user }, -) -render json: result -{% endhighlight %} -
-
-

Harden Your API

-

- Confidently deploy GraphQL with GraphQL-Ruby: -

    -
  • {% internal_link "Testing helpers", "/testing/overview" %} to validate your system
  • -
  • {% internal_link "Authorization", "/authorization/overview" %} integrates with your app's permission system
  • -
  • {% internal_link "GraphQL::Dataloader", "/dataloader/overview" %} optimizes access to data sources
  • -
  • {% internal_link "Complexity limits", "/queries/complexity_and_depth" %}, {% internal_link "timeouts", "/queries/timeout" %}, and {% internal_link "rate limits", "/limiters/overview" %} to protect your server resources
  • -
  • {% internal_link "Tracing", "/queries/tracing" %} for integration with your APM or custom usage
  • -
  • {% internal_link "API versioning", "/changesets/overview" %} to roll out changes while preserving client experience
  • -
  • {% internal_link "Persisted queries", "/operation_store/overview" %} to guarantee approved API usage
  • -
  • {% internal_link "Caching", "/object_cache/overview" %} to serve repeated data requests
  • -
-

-
-
-
-
-

Integrate with Client Libraries

-

- {% internal_link "graphql-ruby-client", "/javascript_client/overview" %} provides integration with - {% internal_link "Apollo Client", "/javascript_client/apollo_subscriptions" %}, - {% internal_link "Relay", "/javascript_client/relay_subscriptions" %}, - {% internal_link "GraphiQL", "/javascript_client/graphiql_subscriptions" %}, - {% internal_link "urql", "/javascript_client/urql_subscriptions" %}, or custom JavaScript. -

-
-
-

Going Beyond

-

- Customize your GraphQL API: -

    -
  • {% internal_link "Language tooling", "/language_tools/visitor/ %} for manipulating GraphQL documents
  • -
  • {% internal_link "Type system extensions", "/type_definitions/extensions/ %} for customizing your schema definition
  • -
  • {% internal_link "Query analysis", "/queries/ast_analysis" %} for ahead-of-time query inspection
  • -
-

-
-
-
- -

- Add GraphQL to your Ruby app. Get Started! -

diff --git a/vendor/gems/graphql/guides/javascript_client/ably_key.png b/vendor/gems/graphql/guides/javascript_client/ably_key.png deleted file mode 100644 index 7e873f184e31ddd9710bd8ffd117856556b9c141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123820 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%0DlEL(PCrKI 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 j`2Te&{_ozdsdF+Wb*=CwiF diff --git a/vendor/gems/graphql/guides/javascript_client/apollo_subscriptions.md b/vendor/gems/graphql/guides/javascript_client/apollo_subscriptions.md deleted file mode 100644 index 0f468069eb1..00000000000 --- a/vendor/gems/graphql/guides/javascript_client/apollo_subscriptions.md +++ /dev/null @@ -1,248 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: JavaScript Client -title: Apollo Subscriptions -desc: GraphQL subscriptions with GraphQL-Ruby and Apollo Client -index: 2 ---- - -GraphQL-Ruby's JavaScript client includes several kinds of support for Apollo Client: - -- Apollo Link (2.x, 3.x): - - [Overview](#apollo-link) - - [Pusher](#apollo-link--pusher) - - [Ably](#apollo-link--ably) - - [ActionCable](#apollo-link--actioncable) -- Apollo 1.x: - - [Overview](#apollo-1) - - [Pusher](#apollo-1--pusher) - - [ActionCable](#apollo-1--actioncable) - -## Apollo Link - -Apollo Links are used by Apollo client 2.x and 3.x. - -## Apollo Link -- Pusher - -`graphql-ruby-client` includes support for subscriptions with Pusher and ApolloLink. - -To use it, add `PusherLink` before your `HttpLink`. - -For example: - -```js -// Load Apollo stuff -import { ApolloClient, HttpLink, ApolloLink, InMemoryCache } from "@apollo/client"; -// Load PusherLink from graphql-ruby-client -import PusherLink from 'graphql-ruby-client/subscriptions/PusherLink'; - -// Load Pusher and create a client -import Pusher from "pusher-js" -var pusherClient = new Pusher("your-app-key", { cluster: "us2" }) - -// Make the HTTP link which actually sends the queries -const httpLink = new HttpLink({ - uri: '/graphql', - credentials: 'include' -}); - -// Make the Pusher link which will pick up on subscriptions -const pusherLink = new PusherLink({pusher: pusherClient}) - -// Combine the two links to work together -const link = ApolloLink.from([pusherLink, httpLink]) - -// Initialize the client -const client = new ApolloClient({ - link: link, - cache: new InMemoryCache() -}); -``` - -This link will check responses for the `X-Subscription-ID` header, and if it's present, it will use that value to subscribe to Pusher for future updates. - -If you're using {% internal_link "compressed payloads", "/subscriptions/pusher_implementation#compressed-payloads" %}, configure a `decompress:` function, too: - -```javascript -// Add `pako` to the project for gunzipping -import pako from "pako" - -const pusherLink = new PusherLink({ - pusher: pusherClient, - decompress: function(compressed) { - // Decode base64 - const data = atob(compressed) - .split('') - .map(x => x.charCodeAt(0)); - // Decompress - const payloadString = pako.inflate(new Uint8Array(data), { to: 'string' }); - // Parse into an object - return JSON.parse(payloadString); - } -}) -``` - -## Apollo Link -- Ably - -`graphql-ruby-client` includes support for subscriptions with Ably and ApolloLink. - -To use it, add `AblyLink` before your `HttpLink`. - -For example: - -```js -// Load Apollo stuff -import { ApolloClient, HttpLink, ApolloLink, InMemoryCache } from '@apollo/client'; -// Load Ably subscriptions link -import AblyLink from 'graphql-ruby-client/subscriptions/AblyLink' -// Load Ably and create a client -const Ably = require("ably") -const ablyClient = new Ably.Realtime({ key: "your-app-key" }) - -// Make the HTTP link which actually sends the queries -const httpLink = new HttpLink({ - uri: '/graphql', - credentials: 'include' -}); - -// Make the Ably link which will pick up on subscriptions -const ablyLink = new AblyLink({ably: ablyClient}) - -// Combine the two links to work together -const link = ApolloLink.from([ablyLink, httpLink]) - -// Initialize the client -const client = new ApolloClient({ - link: link, - cache: new InMemoryCache() -}); -``` - -This link will check responses for the `X-Subscription-ID` header, and if it's present, it will use that value to subscribe to Ably for future updates. - -For your __app key__, make a key with "Subscribe" and "Presence" privileges and use that: - -{{ "/javascript_client/ably_key.png" | link_to_img:"Ably Subscription Key Privileges" }} - -## Apollo Link -- ActionCable - -`graphql-ruby-client` includes support for subscriptions with ActionCable and ApolloLink. - -To use it, construct a split link that routes: - -- subscription queries to an `ActionCableLink`; and -- other queries to an `HttpLink` - -For example: - -```js -import { ApolloClient, HttpLink, ApolloLink, InMemoryCache } from '@apollo/client'; -import { createConsumer } from '@rails/actioncable'; -import ActionCableLink from 'graphql-ruby-client/subscriptions/ActionCableLink'; - -const cable = createConsumer() - -const httpLink = new HttpLink({ - uri: '/graphql', - credentials: 'include' -}); - -const hasSubscriptionOperation = ({ query: { definitions } }) => { - return definitions.some( - ({ kind, operation }) => kind === 'OperationDefinition' && operation === 'subscription' - ) -} - -const link = ApolloLink.split( - hasSubscriptionOperation, - new ActionCableLink({cable}), - httpLink -); - -const client = new ApolloClient({ - link: link, - cache: new InMemoryCache() -}); -``` - -Note that for Rails 5, the ActionCable client package is `actioncable`, not `@rails/actioncable`. - -## Apollo 1 - -`graphql-ruby-client` includes support for Apollo 1 client subscriptions over {% internal_link "Pusher", "/subscriptions/pusher_implementation" %} or {% internal_link "ActionCable", "/subscriptions/action_cable_implementation" %}. - -To use it, require `subscriptions/addGraphQLSubscriptions` and call the function with your network interface and transport client (example below). - -See the {% internal_link "Subscriptions guide", "/subscriptions/overview" %} for information about server-side setup. - -### Apollo 1 -- Pusher - -Pass `{pusher: pusherClient}` to use Pusher: - -```js -// Load Pusher and create a client -var Pusher = require("pusher-js") -var pusherClient = new Pusher(appKey, options) - -// Add subscriptions to the network interface with the `pusher:` options -import addGraphQLSubscriptions from "graphql-ruby-client/subscriptions/addGraphQLSubscriptions" -addGraphQLSubscriptions(myNetworkInterface, {pusher: pusherClient}) - -// Optionally, add persisted query support: -var OperationStoreClient = require("./OperationStoreClient") -RailsNetworkInterface.use([OperationStoreClient.apolloMiddleware]) -``` - -If you're using {% internal_link "compressed payloads", "/subscriptions/pusher_implementation#compressed-payloads" %}, configure a `decompress:` function, too: - -```javascript -// Add `pako` to the project for gunzipping -import pako from "pako" - -addGraphQLSubscriptions(myNetworkInterface, { - pusher: pusherClient, - decompress: function(compressed) { - // Decode base64 - const data = btoa(compressed) - // Decompress - const payloadString = pako.inflate(data, { to: 'string' }) - // Parse into an object - return JSON.parse(payloadString); - } -}) -``` - -### Apollo 1 -- ActionCable - -By passing `{cable: cable}`, all `subscription` queries will be routed to ActionCable. - -For example: - -```js -// Load ActionCable and create a consumer -var ActionCable = require('@rails/actioncable') -var cable = ActionCable.createConsumer() -window.cable = cable - -// Load ApolloClient and create a network interface -var apollo = require('apollo-client') -var RailsNetworkInterface = apollo.createNetworkInterface({ - uri: '/graphql', - opts: { - credentials: 'include', - }, - headers: { - 'X-CSRF-Token': $("meta[name=csrf-token]").attr("content"), - } -}); - -// Add subscriptions to the network interface -import addGraphQLSubscriptions from "graphql-ruby-client/subscriptions/addGraphQLSubscriptions" -addGraphQLSubscriptions(RailsNetworkInterface, {cable: cable}) - -// Optionally, add persisted query support: -var OperationStoreClient = require("./OperationStoreClient") -RailsNetworkInterface.use([OperationStoreClient.apolloMiddleware]) -``` diff --git a/vendor/gems/graphql/guides/javascript_client/graphiql_subscriptions.md b/vendor/gems/graphql/guides/javascript_client/graphiql_subscriptions.md deleted file mode 100644 index 6b1279d61e5..00000000000 --- a/vendor/gems/graphql/guides/javascript_client/graphiql_subscriptions.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: JavaScript Client -title: GraphiQL Subscriptions -desc: Testing GraphQL subscriptions in the GraphiQL IDE -index: 5 ---- - -After setting up your server, you can integrate subscriptions into [GraphiQL](https://github.com/graphql/graphiql/tree/main/packages/graphiql#readme), the in-browser GraphQL IDE. - -## Adding GraphiQL to your app - -To get started, make a page for rendering GraphiQL, for example: - -```html - -
-``` - -Then, install GraphiQL (eg, `yarn add graphiql`) and add JavaScript code to import GraphiQL and render it on your page: - -```js -import { GraphiQL } from 'graphiql' -import React from 'react' -import { createRoot } from 'react-dom/client' -import 'graphiql/graphiql.css' -import { createGraphiQLFetcher } from '@graphiql/toolkit' - -const fetcher = createGraphiQLFetcher({ url: '/graphql' }) -const root = createRoot(document.getElementById('root')) -root.render() -``` - -After that, you should be able to load the page in your app and see the GraphiQL editor. - -## Ably - -To integrate {% internal_link "Ably subscriptions", "subscriptions/ably_implementation" %}, use `createAblyFetcher`, for example: - -```js -import Ably from "ably" -import createAblyFetcher from 'graphql-ruby-client/subscriptions/createAblyFetcher' - -// Initialize a client -// the key must have "subscribe" and "presence" permissions -const ably = new Ably.Realtime({ key: "your.application.key" }) - -// Initialize a new fetcher and pass it to GraphiQL below -var fetcher = createAblyFetcher({ ably: ably, url: "/graphql" }) -const root = createRoot(document.getElementById('root')) -root.render() -``` - -Under the hood, it will use `window.fetch` to send GraphQL operations to the server, then listen for `X-Subscription-ID` headers in responses. To customize its HTTP requests, you can pass a `fetchOptions:` object or a custom `fetch:` function to `createAblyFetcher({ ... })`. - -## Pusher - -To integrate {% internal_link "Pusher subscriptions", "subscriptions/pusher_implementation" %}, use `createPusherFetcher`, for example: - -```js -import Pusher from "pusher-js" -import createPusherFetcher from 'graphql-ruby-client/subscriptions/createPusherFetcher' - -// Initialize a client -const pusher = new Pusher("your-app-key", { cluster: "your-cluster" }) - -// Initialize a new fetcher and pass it to GraphiQL below -var fetcher = createPusherFetcher({ pusher: pusher, url: "/graphql" }) -const root = createRoot(document.getElementById('root')) -root.render() -``` - -Under the hood, it will use `window.fetch` to send GraphQL operations to the server, then listen for `X-Subscription-ID` headers in responses. To customize its HTTP requests, you can pass a `fetchOptions:` object or a custom `fetch:` function to `createPusherFetcher({ ... })`. - -## ActionCable - -To integrate {% internal_link "ActionCable subscriptions", "subscriptions/action_cable_implementation" %}, use `createActionCableFetcher`, for example: - -```js -import { createConsumer } from "@rails/actioncable" -import createActionCableFetcher from 'graphql-ruby-client/subscriptions/createActionCableFetcher'; - -// Initialize a client -const actionCable = createConsumer() - -// Initialize a new fetcher and pass it to GraphiQL below -var fetcher = createActionCableFetcher({ consumer: actionCable, url: "/graphql" }) -const root = createRoot(document.getElementById('root')) -root.render() -``` - -Under the hood, it will split traffic: it will send `subscription { ... }` operations via ActionCable and send queries and mutations via HTTP `POST` using `window.fetch`. To customize its HTTP requests, you can pass a `fetchOptions:` object or a custom `fetch:` function to `createActionCableFetcher({ ... })`. diff --git a/vendor/gems/graphql/guides/javascript_client/overview.md b/vendor/gems/graphql/guides/javascript_client/overview.md deleted file mode 100644 index 6e9b4f41d81..00000000000 --- a/vendor/gems/graphql/guides/javascript_client/overview.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: JavaScript Client -title: Overview -desc: Getting Started with GraphQL-Ruby's Javascript client, graphql-ruby-client. -index: 0 ---- - -There is a JavaScript client for GraphQL-Ruby, `graphql-ruby-client`. - -You can install it from NPM or Yarn: - -```sh -yarn add graphql-ruby-client -# Or: -npm install graphql-ruby-client -``` - -The source code is [in the graphql-ruby repository](https://github.com/rmosolgo/graphql-ruby/tree/master/javascript_client). - -See detailed guides for more info about its features: - -- {% internal_link "sync CLI", "javascript_client/sync" %} for use with [graphql-pro](https://graphql.pro)'s persisted query backend -- Subscription support: - - {% internal_link "Apollo integration", "/javascript_client/apollo_subscriptions" %} - - {% internal_link "Relay integration", "/javascript_client/relay_subscriptions" %} - - {% internal_link "urql integration", "/javascript_client/urql_subscriptions" %} - - {% internal_link "GraphiQL integration", "/javascript_client/graphiql_subscriptions" %} diff --git a/vendor/gems/graphql/guides/javascript_client/relay_subscriptions.md b/vendor/gems/graphql/guides/javascript_client/relay_subscriptions.md deleted file mode 100644 index d3df92ffa04..00000000000 --- a/vendor/gems/graphql/guides/javascript_client/relay_subscriptions.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: JavaScript Client -title: Relay Subscriptions -desc: GraphQL subscriptions with GraphQL-Ruby and Relay Modern -index: 3 ---- - -`graphql-ruby-client` includes three kinds of support for subscriptions with Relay Modern: - -- [Pusher](#pusher) -- [Ably](#ably) -- [ActionCable](#actioncable) - -To use it, require `graphql-ruby-client/subscriptions/createRelaySubscriptionHandler` and call the function with your client and optionally, your OperationStoreClient. - -__Note:__ For Relay <11, use `import { createLegacyRelaySubscriptionHandler } from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler"` instead; the signature changed in Relay 11. - - -See the {% internal_link "Subscriptions guide", "/subscriptions/overview" %} for information about server-side setup. - -## Pusher - -Subscriptions with {% internal_link "Pusher", "/subscriptions/pusher_implementation" %} require two things: - -- A client from the [`pusher-js` library](https://github.com/pusher/pusher-js) -- A [`fetchOperation` function](#fetchoperation-function) for sending the `subscription` operation to the server - -### Pusher client - -Pass `pusher:` to get Subscription updates over Pusher: - -```js -// Load the helper function -import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler" - -// Prepare a Pusher client -var Pusher = require("pusher-js") -var pusherClient = new Pusher(appKey, options) - -// Create a fetchOperation, see below for more details -function fetchOperation(operation, variables, cacheConfig) { - return fetch(...) -} - -// Create a Relay Modern-compatible handler -var subscriptionHandler = createRelaySubscriptionHandler({ - pusher: pusherClient, - fetchOperation: fetchOperation -}) - -// Create a Relay Modern network with the handler -var network = Network.create(fetchQuery, subscriptionHandler) -``` - -### Compressed Payloads - -If you're using {% internal_link "compressed payloads", "/subscriptions/pusher_implementation#compressed-payloads" %}, configure a `decompress:` function, too: - -```javascript -// Add `pako` to the project for gunzipping -import pako from "pako" - -var subscriptionHandler = createRelaySubscriptionHandler({ - pusher: pusherClient, - fetchOperation: fetchOperation, - decompress: function(compressed) { - // Decode base64 - const data = btoa(compressed) - // Decompress - const payloadString = pako.inflate(data, { to: 'string' }) - // Parse into an object - return JSON.parse(payloadString); - } -}) -``` - -## Ably - -Subscriptions with {% internal_link "Ably", "/subscriptions/ably_implementation" %} require two things: - -- A client from the [`ably-js` library](https://github.com/ably/ably-js) -- A [`fetchOperation` function](#fetchoperation-function) for sending the `subscription` operation to the server - -### Ably client - -Pass `ably:` to get Subscription updates over Ably: - -```js -// Load the helper function -import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler" - -// Load Ably and create a client -const Ably = require("ably") -const ablyClient = new Ably.Realtime({ key: "your-app-key" }) - -// create a fetchOperation, see below for more details -function fetchOperation(operation, variables, cacheConfig) { - return fetch(...) -} - -// Create a Relay Modern-compatible handler -var subscriptionHandler = createRelaySubscriptionHandler({ - ably: ablyClient, - fetchOperation: fetchOperation -}) - -// Create a Relay Modern network with the handler -var network = Network.create(fetchQuery, subscriptionHandler) -``` - -## ActionCable - -With this configuration, `subscription` queries will be routed to {% internal_link "ActionCable", "/subscriptions/action_cable_implementation" %}. - -For example: - -```js -// Require the helper function -import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler") -// Optionally, load your OperationStoreClient -var OperationStoreClient = require("./OperationStoreClient") - -// Create a Relay Modern-compatible handler -var subscriptionHandler = createRelaySubscriptionHandler({ - cable: createConsumer(...), - operations: OperationStoreClient, -}) - -// Create a Relay Modern network with the handler -var network = Network.create(fetchQuery, subscriptionHandler) -``` - -## With Relay Persisted Queries - -If you're using Relay's built-in [persisted query support](https://relay.dev/docs/guides/persisted-queries/), you can pass `clientName:` to the handler in order to build IDs that work with the {% internal_link "OperationStore", "/operation_store/overview.html" %}. For example: - -```js -var subscriptionHandler = createRelaySubscriptionHandler({ - cable: createConsumer(...), - clientName: "web-frontend", // This should match the one you use for `sync` -}) - -// Create a Relay Modern network with the handler -var network = Network.create(fetchQuery, subscriptionHandler) -``` - -Then, the ActionCable handler will use Relay's provided operation IDs to interact with the OperationStore. - -## fetchOperation function - -The `fetchOperation` function can be extracted from your `fetchQuery` function. Its signature is: - -```js -// Returns a promise from `fetch` -function fetchOperation(operation, variables, cacheConfig) { - return fetch(...) -} -``` - -- `operation`, `variables`, and `cacheConfig` are the first three arguments to the `fetchQuery` function. -- The function should call `fetch` and return the result (a Promise of a `Response`). - -For example, `Environment.js` may look like: - -```js -// This function sends a GraphQL query to the server -const fetchOperation = function(operation, variables, cacheConfig) { - const bodyValues = { - variables, - operationName: operation.name, - } - const useStoredOperations = process.env.NODE_ENV === "production" - if (useStoredOperations) { - // In production, use the stored operation - bodyValues.operationId = OperationStoreClient.getOperationId(operation.name) - } else { - // In development, use the query text - bodyValues.query = operation.text - } - return fetch('http://localhost:3000/graphql', { - method: 'POST', - opts: { - credentials: 'include', - }, - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify(bodyValues), - }) -} - -// `fetchQuery` uses `fetchOperation`, but returns a Promise of JSON -const fetchQuery = (operation, variables, cacheConfig, uploadables) => { - return fetchOperation(operation, variables, cacheConfig).then(response => { - return response.json() - }) -} - -// Subscriptions uses the same `fetchOperation` function for initial subscription requests -const subscriptionHandler = createRelaySubscriptionHandler({pusher: pusherClient, fetchOperation: fetchOperation}) -// Combine them into a `Network` -const network = Network.create(fetchQuery, subscriptionHandler) -``` - -Since `OperationStoreClient` is in the `fetchOperation` function, it will apply to all GraphQL operations. diff --git a/vendor/gems/graphql/guides/javascript_client/sync.md b/vendor/gems/graphql/guides/javascript_client/sync.md deleted file mode 100644 index 96a2c25b896..00000000000 --- a/vendor/gems/graphql/guides/javascript_client/sync.md +++ /dev/null @@ -1,329 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: JavaScript Client -title: OperationStore Sync -desc: Javascript tooling for persisted queries with GraphQL-Ruby -index: 1 ---- - -JavaScript support for GraphQL projects using [graphql-pro](https://graphql.pro)'s `OperationStore` for persisted queries. - -- [`sync` CLI](#sync-utility) -- [Relay <2 support](#use-with-relay-2) -- [Relay 2+ support](#use-with-relay-persisted-output) -- [Apollo Client support](#use-with-apollo-client) -- [Apollo Link support](#use-with-apollo-link) -- [Apollo Codegen Support](#use-with-apollo-codegen) -- [Apollo Android support](#use-with-apollo-android) -- [Apollo Persisted Queries Support](#use-with-apollo-persisted-queries) -- [Plain JS support](#use-with-plain-javascript) -- [Authorization](#authorization) - -See the {% internal_link "OperationStore guide", "/operation_store/overview" %} for server-side setup. - -## `sync` utility - -This package contains a command line utility, `graphql-ruby-client sync`: - -``` -$ graphql-ruby-client sync # ... -Authorizing with HMAC -Syncing 4 operations to http://myapp.com/graphql/operations... - 3 added - 1 not modified - 0 failed -Generating client module in app/javascript/graphql/OperationStoreClient.js... -✓ Done! -``` - -`sync` Takes several options: - -option | description ---------|---------- -`--url` | {% internal_link "Sync API", "/operation_store/getting_started.html#add-routes" %} url -`--path` | Local directory to search for `.graphql` / `.graphql.js` files -`--relay-persisted-output` | Path to a `.json` file from `relay-compiler ... --persist-output` -`--apollo-codegen-json-output` | Path to a `.json` file from `apollo client:codegen ... --target json` -`--apollo-android-operation-output` | Path to an `OperationOutput.json` file from Apollo Android -`--client` | Client ID ({% internal_link "created on server", "/operation_store/client_workflow" %}) -`--secret` | Client Secret ({% internal_link "created on server", "/operation_store/client_workflow" %}) -`--outfile` | Destination for generated code -`--outfile-type` | What kind of code to generate (`js` or `json`) -`--header={key}:{value}` | Add a header to the outgoing HTTP request (may be repeated) -`--add-typename` | Add `__typename` to all selection sets (for use with Apollo Client) -`--verbose` | Output some debug information -`--changeset-version` | Set a {% internal_link "Changeset Version", "/changesets/installation#controller-setup" %} when syncing these queries. (`context[:changeset_version]` will also be required at runtime, when running these stored operations.) -`--dump-payload` | A file to write the HTTP Post payload into, or if no filename is passed, then the payload will be written to stdout. - -You can see these and a few others with `graphql-ruby-client sync --help`. - -## Use with Relay <2 - -`graphql-ruby-client` can persist queries from `relay-compiler` using the embedded `@relayHash` value. (This was created in Relay before 2.0.0. See below for Relay 2.0+.) - -To sync your queries with the server, use the `--path` option to point to your `__generated__` directory, for example: - -```bash -# sync a Relay project -$ graphql-ruby-client sync --path=src/__generated__ --outfile=src/OperationStoreClient.js --url=... -``` - -Then, the generated code may be integrated with Relay's [Network Layer](https://facebook.github.io/relay/docs/network-layer.html): - -```js -// ... -// require the generated module: -const OperationStoreClient = require('./OperationStoreClient') - -// ... -function fetchQuery(operation, variables, cacheConfig, uploadables) { - const requestParams = { - variables, - operationName: operation.name, - } - - if (process.env.NODE_ENV === "production") - // In production, use the stored operation - requestParams.operationId = OperationStoreClient.getOperationId(operation.name) - } else { - // In development, use the query text - requestParams.query = operation.text, - } - - return fetch('/graphql', { - method: 'POST', - headers: { /*...*/ }, - body: JSON.stringify(requestParams), - }).then(/* ... */); -} - -// ... -``` - -(Only Relay Modern is supported. Legacy Relay can't generate static queries.) - -## Use With Relay Persisted Output - -To use Relay's persisted output, add a `"file": ...` to your project's [`persistConfig` object](https://relay.dev/docs/guides/persisted-queries/). For example: - -```json - "relay": { - ... - "persistConfig": { - "file": "./persisted-queries.json" - } - }, -``` - -Then, push Relay's generated queries to your OperationStore server with `--relay-persisted-output`: - -``` -$ graphql-ruby-client sync --relay-persisted-output=path/to/persisted-queries.json --url=... -``` - -In this case, `sync` _won't_ generate a JavaScript module because `relay-compiler` has already prepared its queries for persisted use. Instead, update your network layer to include the _client name_ and _operation id_ in the HTTP params: - -```js -const operationStoreClientName = "MyRelayApp"; - -function fetchQuery(operation, variables,) { - return fetch('/graphql', { - method: 'POST', - headers: { - 'content-type': 'application/json' - }, - body: JSON.stringify({ - // Pass the client name and the operation ID, joined by `/` - documentId: operationStoreClientName + "/" + operation.id, - // query: operation.text, // this is now obsolete because text is null - variables, - }), - }).then(response => { - return response.json(); - }); -} -``` - -(Inspired by https://relay.dev/docs/guides/persisted-queries/#network-layer-changes.) - -Now, your Relay app will only send operation IDs over the wire to the server. - -## Use with Apollo Client - -Use the `--path` option to point at your `.graphql` files: - -``` -$ graphql-ruby-client sync --path=src/graphql/ --url=... -``` - -Then, load the generated module and add its `.apolloMiddleware` to your network interface with `.use([...])`: - -```js -// load the generated module -var OperationStoreClient = require("./OperationStoreClient") - -// attach it as middleware in production -// (in development, send queries to the server as normal) -if (process.env.NODE_ENV === "production") { - MyNetworkInterface.use([OperationStoreClient.apolloMiddleware]) -} -``` - -Now, the middleware will replace query strings with `operationId`s. - -## Use with Apollo Link - -Use the `--path` option to point at your `.graphql` files: - -``` -$ graphql-ruby-client sync --path=src/graphql/ --url=... -``` - -Then, load the generated module and add its `.apolloLink` to your Apollo Link: - -```js -// load the generated module -var OperationStoreClient = require("./OperationStoreClient") - -// Integrate the link to another link: -const link = ApolloLink.from([ - authLink, - OperationStoreClient.apolloLink, - httpLink, -]) - -// Create a client -const client = new ApolloClient({ - link: link, - cache: new InMemoryCache(), -}); -``` - -__Update the controller__: Apollo Link supports extra parameters _nested_ as `params[:extensions][:operationId]`, so update your controller to add that param to context: - -```ruby -# app/controllers/graphql_controller.rb -context = { - # ... - # Support Apollo Link: - operation_id: params[:extensions][:operationId] -} -``` - -Now, `context[:operation_id]` will be used to fetch a query from the database. - -## Use with Apollo Codegen - -Use `apollo client:codegen ... --target json` to build a JSON artifact containing your app's queries. Then, pass the path to that artifact to `graphql-ruby-client sync --apollo-codegen-json-output path/to/output.json ...`. `sync` will use Apollo-generated `operationId`s to populate the `OperationStore`. - -Then, to use Apollo-style persisted query IDs, hook up the __Persisted Queries Link__ as described in [Apollo's documentation](https://www.apollographql.com/docs/react/api/link/persisted-queries/) - -Finally, __update the controller__ to pass the Apollo-style persisted query ID as the operation ID: - -```ruby -# app/controllers/graphql_controller.rb -context = { - # ... - # Support already-synced Apollo Persisted Queries: - operation_id: params[:extensions][:operationId] -} -``` - -Now, Apollo-style persisted query IDs will be used to fetch operations from the server's `OperationStore`. - -## Use with Apollo Android - -Apollo Android's [generateOperationOutput option](https://www.apollographql.com/docs/android/advanced/persisted-queries/#operationoutputjson) builds an `OperationOutput.json` file which works with the OperationStore. To sync those queries, __use the `--apollo-android-operation-output` option__: - -```sh -graphql-ruby-client sync --apollo-android-operation-output=path/to/OperationOutput.json --url=... -``` - -That way, the OperationStore will use the query IDs generated by Apollo Android. - -On the server, you'll have to __update your controller__ to receive the client name and the operation ID. For example: - -```ruby -# app/controllers/graphql_controller.rb -context = { ... } - -# Check for an incoming operation ID from Apollo Client: -apollo_android_operation_id = request.headers["X-APOLLO-OPERATION-ID"] -if apollo_android_operation_id.present? - # Check the incoming request to confirm that - # it's your first-party client with stored operations - client_name = # ... - if client_name.present? - # If we received an incoming operation ID - # _and_ identified the client, run a persisted operation. - context[:operation_id] = "#{client_name}/#{apollo_android_operation_id}" - end -end -``` - -You may also have to __update your app__ to send an identifier, so that the server can determine the "client name" used with the operation store. (Apollo Android sends a query hash, but the operation store expects IDs in the form `#{client_name}/#{query_hash}`.) - -## Use with Apollo Persisted Queries - -Apollo client has a [Persisted Queries Link](https://www.apollographql.com/docs/react/api/link/persisted-queries/). You can use that link with GraphQL-Pro's {% internal_link "OperationStore", "/operation_store/overview" %}. First, create a manifest with [`generate-persisted-query-manifest`](https://www.apollographql.com/docs/react/api/link/persisted-queries/#1-generate-operation-manifests), then, pass the path to that file to `sync`: - -```sh -$ graphql-ruby-client sync --apollo-persisted-query-manifest=path/to/manifest.json ... -``` - -Then, configure Apollo Client to [use your persisted query manifest](https://www.apollographql.com/docs/react/api/link/persisted-queries/#persisted-queries-implementation). - -Finally, update your controller to receive the operation ID and pass it as `context[:operation_id]`: - -```ruby -client_name = "..." # TODO: send the client name as a query param or header -persisted_query_hash = params[:extensions][:persistedQuery][:sha256Hash] -context = { - # ... - operation_id: "#{client_name}/#{persisted_query_hash}" -} -``` - -The `operation_id` will also need your client name. Using Apollo Client, you could send this as a [custom header](https://www.apollographql.com/docs/react/networking/basic-http-networking/#customizing-request-headers) or another way that works for your application (eg, session or user agent). - -## Use with plain JavaScript - -`OperationStoreClient.getOperationId` takes an operation name as input and returns the server-side alias for that operation: - -```js -var OperationStoreClient = require("./OperationStoreClient") - -OperationStoreClient.getOperationId("AppHomeQuery") // => "my-frontend-app/7a8078c7555e20744cb1ff5a62e44aa92c6e0f02554868a15b8a1cbf2e776b6f" -OperationStoreClient.getOperationId("ProductDetailQuery") // => "my-frontend-app/6726a3b816e99b9971a1d25a1205ca81ecadc6eb1d5dd3a71028c4b01cc254c1" -``` - -Post the `operationId` in your GraphQL requests: - -```js -// Lookup the operation name: -var operationId = OperationStoreClient.getOperationId(operationName) - -// Include it in the params: -$.post("/graphql", { - operationId: operationId, - variables: queryVariables, -}, function(response) { - // ... -}) -``` - -## Authorization - -`OperationStore` uses HMAC-SHA256 to {% internal_link "authenticate requests" , "/operation_store/access_control" %}. - -Pass the key to `graphql-ruby-client sync` as `--secret` to authenticate it: - -```bash -$ export MY_SECRET_KEY= "abcdefg..." -$ graphql-ruby-client sync ... --secret=$MY_SECRET_KEY -# ... -Authenticating with HMAC -# ... -``` diff --git a/vendor/gems/graphql/guides/javascript_client/urql_subscriptions.md b/vendor/gems/graphql/guides/javascript_client/urql_subscriptions.md deleted file mode 100644 index 25983705666..00000000000 --- a/vendor/gems/graphql/guides/javascript_client/urql_subscriptions.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: JavaScript Client -title: urql Subscriptions -desc: GraphQL subscriptions with GraphQL-Ruby and urql -index: 4 ---- - -GraphQL-Ruby currently supports using `urql` with the {% internal_link "ActionCable", "/subscriptions/action_cable_implementation" %} and {% internal_link "Pusher implementation", "/subscriptions/pusher_implementation" %}. - -## Pusher - -```js -import SubscriptionExchange from "graphql-ruby-client/subscriptions/SubscriptionExchange" -import Pusher from "pusher" -import { Client, defaultExchanges, subscriptionExchange } from 'urql' - -const pusherClient = new Pusher("your-app-key", { cluster: "us2" }) -const forwardToPusher = SubscriptionExchange.create({ pusher: pusherClient }) - -const client = new Client({ - url: '/graphql', - exchanges: [ - ...defaultExchanges, - subscriptionExchange({ - forwardSubscription: forwardToPusher - }), - ], -}); -``` - -## ActionCable - -```js -import { createConsumer } from "@rails/actioncable"; -import SubscriptionExchange from "graphql-ruby-client/subscriptions/SubscriptionExchange" - -const actionCable = createConsumer('ws://127.0.0.1:3000/cable'); -const forwardToActionCable = SubscriptionExchange.create({ consumer: actionCable }) - -const client = new Client({ - url: '/graphql', - exchanges: [ - ...defaultExchanges, - subscriptionExchange({ - forwardSubscription: forwardToActionCable - }), - ], -}); -``` - -Want to use `urql` with another subscription backend? Please {% open_an_issue "Using urql with ..." %}. diff --git a/vendor/gems/graphql/guides/js/search.js b/vendor/gems/graphql/guides/js/search.js deleted file mode 100644 index 7a9cedcd982..00000000000 --- a/vendor/gems/graphql/guides/js/search.js +++ /dev/null @@ -1,103 +0,0 @@ -var client = algoliasearch('8VO8708WUV', '1f3e2b6f6a503fa82efdec331fd9c55e'); -var index = client.initIndex('prod_graphql_ruby'); - -var GraphQLRubySearch = { - // Respond to a change event on `el` by: - // - Searching the index - // - Rendering the results - run: function(el) { - var searchTerm = el.value - var searchResults = document.querySelector("#search-results") - if (!searchTerm) { - // If there's no search term, clear the results pane - searchResults.innerHTML = "" - } else { - index.search({ - query: searchTerm, - hitsPerPage: 8, - }, function(err, content) { - if (err) { - console.error(err) - } - var results = content.hits - // Clear the previous results - searchResults.innerHTML = "" - - results.forEach(function(result) { - // Create a wrapper hyperlink - var container = document.createElement("a") - container.className = "search-result" - container.href = (result.rubydoc_url || result.url) + (result.anchor ? "#" + result.anchor : "") - - // This helper will be used to accumulate text into the search-result - function createSpan(text, className) { - var txt = document.createElement("span") - txt.className = className - txt.innerHTML = text - container.appendChild(txt) - } - if (result.rubydoc_url) { - createSpan("API Doc", "search-category") - createSpan(result.title, "search-title") - } else { - createSpan(result.section, "search-category") - - var resultHeader = [result.title].concat(result.headings).join(" > ") - createSpan(resultHeader, "search-title") - var preview = result._snippetResult.content.value - createSpan(preview, "search-preview") - } - searchResults.appendChild(container) - }) - - var seeAll = document.createElement("a") - seeAll.href = "/search?query=" + content.query - seeAll.className = "search-see-all" - seeAll.innerHTML = "See All Results (" + content.nbHits + ")" - searchResults.appendChild(seeAll) - }) - } - }, - - // Return true if we actually highlighted something - _moveHighlight: function(diff) { - var allResults = document.querySelectorAll(".search-result") - var highlightClass = "highlight-search-result" - if (!allResults.length) { - // No search results to highlight - return false - } - var highlightedResult = document.querySelector("." + highlightClass) - var nextHighlightedResult - var result - for (var i = 0; i < allResults.length; i++) { - result = allResults[i] - if (result == highlightedResult) { - nextHighlightedResult = allResults[i + diff] - break - } - } - if (!nextHighlightedResult) { - // Either nothing was highlighted yet, - // or we were at the end of results and we loop around - nextHighlightedResult = allResults[0] - } - - if (highlightedResult) { - highlightedResult.classList.remove(highlightClass) - } - nextHighlightedResult.classList.add(highlightClass) - nextHighlightedResult.focus() - return true - } -} - -document.addEventListener("keydown", function(ev) { - var diff = ev.keyCode == 38 ? -1 : (ev.keyCode == 40 ? 1 : 0) - if (diff) { - var highlighted = GraphQLRubySearch._moveHighlight(diff) - if (highlighted) { - ev.preventDefault() - } - } -}) diff --git a/vendor/gems/graphql/guides/language_tools/c_parser.md b/vendor/gems/graphql/guides/language_tools/c_parser.md deleted file mode 100644 index 820f3e59e64..00000000000 --- a/vendor/gems/graphql/guides/language_tools/c_parser.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Language Tools -title: C-based Parser -desc: The GraphQL::CParser gem is a drop-in replacement for the built-in parser -index: 1 ---- - -GraphQL-Ruby includes a plain-Ruby parser, but a faster parser is available as a C extension. To use it, add the [`graphql-c_parser` gem](https://rubygems.org/gems/graphql-c_parser) to your project, for example: - -```ruby -bundle add graphql-c_parser -``` - -When `graphql-c_parser` is `require`d by your app, the C-based parser is installed as the default parser (as {{ "GraphQL.default_parser" | api_doc }}). Bundler requires the library automatically, but you can also require it manually: - -```ruby -require "graphql/c_parser" -``` - -This alternative parser is faster and uses less memory. - -The library also adds `GraphQL.scan_with_c` and `GraphQL.parse_with_c` for calling the C-based parser directly. diff --git a/vendor/gems/graphql/guides/language_tools/visitor.md b/vendor/gems/graphql/guides/language_tools/visitor.md deleted file mode 100644 index f6cb3ce6bda..00000000000 --- a/vendor/gems/graphql/guides/language_tools/visitor.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Language Tools -title: AST Visitor -desc: Analyze and modify parsed GraphQL code -index: 0 ---- - -GraphQL code is usually contained in a string, for example: - -```ruby -query_string = "query { user(id: \"1\") { userName } }" -``` - -You can perform programmatic analysis and modifications to GraphQL code using a three-step process: - -- __Parse__ the code into an abstract syntax tree -- __Analyze/Modify__ the code with a visitor -- __Print__ the code back to a string - -## Parse - -{{ "GraphQL.parse" | api_doc }} turns a string into a GraphQL document: - -```ruby -parsed_doc = GraphQL.parse("{ user(id: \"1\") { userName } }") -# => # -``` - -Also, {{ "GraphQL.parse_file" | api_doc }} parses the contents of the named file and includes a `filename` in the parsed document. - -#### AST Nodes - -The parsed document is a tree of nodes, called an _abstract syntax tree_ (AST). This tree is _immutable_: once a document has been parsed, those Ruby objects can't be changed. Modifications are performed by _copying_ existing nodes, applying changes to the copy, then making a new tree to hold the copied node. Where possible, unmodified nodes are retained in the new tree (it's _persistent_). - -The copy-and-modify workflow is supported by a few methods on the AST nodes: - -- `.merge(new_attrs)` returns a copy of the node with `new_attrs` applied. This new copy can replace the original node. -- `.add_{child}(new_child_attrs)` makes a new node with `new_child_attrs`, adds it to the array specified by `{child}`, and returns a copy whose `{children}` array contains the newly created node. - -For example, to rename a field and add an argument to it, you could: - -```ruby -modified_node = field_node - # Apply a new name - .merge(name: "newName") - # Add an argument to this field's arguments - .add_argument(name: "newArgument", value: "newValue") -``` - -Above, `field_node` is unmodified, but `modified_node` reflects the new name and new argument. - -## Analyze/Modify - -To inspect or modify a parsed document, extend {{ "GraphQL::Language::Visitor" | api_doc }} and implement its various hooks. It's an implementation of the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern). In short, each node of the tree will be "visited" by calling a method, and those methods can gather information and perform modifications. - -In the visitor, each node class has a hook, for example: - -- {{ "GraphQL::Language::Nodes::Field" | api_doc }}s are routed to `#on_field` -- {{ "GraphQL::Language::Nodes::Argument" | api_doc }}s are routed to `#on_argument` - -See the {{ "GraphQL::Language::Visitor" | api_doc }} API docs for a full list of methods. - -Each method is called with `(node, parent)`, where: - -- `node` is the AST node currently visited -- `parent` is the AST node above this node in the tree - -The method has a few options for analyzing or modifying the AST: - -#### Continue/Halt - -To continue visiting, the hook should call `super`. This allows the visit to continue to `node`'s children in the tree, for example: - -```ruby -def on_field(_node, _parent) - # Do nothing, this is the default behavior: - super -end -``` - -To _halt_ the visit, a method may skip the call to `super`. For example, if the visitor encountered an error, it might want to return early instead of continuing to visit. - -#### Modify a Node - -Visitor hooks are expected to return the `(node, parent)` they are called with. If they return a different node, then that node will replace the original `node`. When you call `super(node, parent)`, the `node` is returned. So, to modify a node and continue visiting: - -- Make a modified copy of `node` -- Pass the modified copy to `super(new_node, parent)` - -For example, to rename an argument: - -```ruby -def on_argument(node, parent) - # make a copy of `node` with a new name - modified_node = node.merge(name: "renamed") - # continue visiting with the modified node and parent - super(modified_node, parent) -end -``` - -#### Delete a Node - -To delete the currently-visited `node`, don't pass `node` to `super(...)`. Instead, pass a magic constant, `DELETE_NODE`, in place of `node`. - -For example, to delete a directive: - -```ruby -def on_directive(node, parent) - # Don't pass `node` to `super`, - # instead, pass `DELETE_NODE` - super(DELETE_NODE, parent) -end -``` - -#### Insert a Node - -Inserting nodes is similar to modifying nodes. To insert a new child into `node`, call one of its `.add_` helpers. This returns a copied node with a new child added. For example, to add a selection to a field's selection set: - -```ruby -def on_field(node, parent) - node_with_selection = node.add_selection(name: "emailAddress") - super(node_with_selection, parent) -end -``` - -This will add `emailAddress` the fields selection on `node`. - - -(These `.add_*` helpers are wrappers around {{ "GraphQL::Language::Nodes::AbstractNode#merge" | api_doc }}.) - -## Print - -The easiest way to turn an AST back into a string of GraphQL is {{ "GraphQL::Language::Nodes::AbstractNode#to_query_string" | api_doc }}, for example: - -```ruby -parsed_doc.to_query_string -# => '{ user(id: "1") { userName } }' -``` - -You can also create a subclass of {{ "GraphQL::Language::Printer" | api_doc }} to customize how nodes are printed. diff --git a/vendor/gems/graphql/guides/limiters/active_operation_limiter_dashboard.png b/vendor/gems/graphql/guides/limiters/active_operation_limiter_dashboard.png deleted file mode 100644 index 96140ed17c677990abcf25c5daf0aaf6e1360f75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54581 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 diff --git a/vendor/gems/graphql/guides/limiters/active_operations.md b/vendor/gems/graphql/guides/limiters/active_operations.md deleted file mode 100644 index 0dd18308875..00000000000 --- a/vendor/gems/graphql/guides/limiters/active_operations.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Rate Limiters -title: Active Operation Limiter -desc: Limit the number of concurrent GraphQL operations -index: 2 ---- - -`GraphQL::Enterprise::ActiveOperationLimiter` prevents clients from running too many GraphQL operations at the same time. It uses {% internal_link "Redis", "limiters/redis" %} to track currently-running operations. - -## Why? - -Some clients may suddently swamp a server with tons of requests, occupying all available Ruby processes and therefore interrupting service for other clients. This limiter aims to prevent that at the GraphQL level by halting queries when a client already has lots of queries running. That way, server processes will remain available for other clients' requests. - -## Setup - -To use this limiter, update the schema configuration and include `context[:limiter_key]` in your queries. - -#### Schema Setup - -To setup the schema, add `use GraphQL::Enterprise::ActiveOperationLimiter` with a default `limit:` value: - -```ruby -class MySchema < GraphQL::Schema - # ... - use GraphQL::Enterprise::ActiveOperationLimiter, - redis: Redis.new(...), - limit: 5 -end -``` - -`limit: false` may also be given, which defaults to _no limit_ for this limiter. - -It also accepts a `stale_request_seconds:` option. The limiter uses that value to clean up request data in case of a crash or other unexpected scenario. - -Before requests will actually be halted, {% internal_link "soft mode", "/limiters/deployment#soft-limits" %} must be disabled. - -#### Query Setup - -In order to limit clients, the limiter needs a client identifier for each GraphQL operation. By default, it checks `context[:limiter_key]` to find it: - -```ruby -context = { - viewer: current_user, - # for example: - limiter_key: logged_in? ? "user:#{current_user.id}" : "anon-ip:#{request.remote_ip}", - # ... -} - -result = MySchema.execute(query_str, context: context) -``` - -Operations with the same `context[:limiter_key]` will rate limited in the same buckets. A limiter key is required; if a query is run without one, the limiter will raise an error. - -To provide a client identifier another way, see [Customization](#customization). - -## Customization - -`GraphQL::Enterprise::ActiveOperationLimiter` provides several hooks for customizing its behavior. To use these, make a subclass of the limiter and override methods as described: - -```ruby -# app/graphql/limiters/active_operations.rb -class Limiters::ActiveOperations < GraphQL::Enterprise::ActiveOperationsLimiter - # override methods here -end -``` - -The hooks are: - -- `def limiter_key(query)` should return a string which identifies the current client for `query`. -- `def limit_for(key, query)` should return an integer or `nil`. If an integer is returned, that limit is applied for the current query. If `nil` is returned, no limit is applied to the current query. -- `def soft_limit?(key, query)` can be implemented to customize the application of "soft mode". By default, it checks a setting in redis. -- `def handle_redis_error(err)` is called when the limit rescues an error from Redis. By default, it's passed to `warn` and the query is _not_ halted. - -## Instrumentation - -While the limiter is installed, it adds some information to the query context about its operation. It can be accessed at `context[:active_operation_limiter]`: - -```ruby -result = MySchema.execute(...) - -pp result.context[:active_operation_limiter] -# {:key=>"user:123", :limit=>2, :soft=>false, :limited=>true} -``` - -It returns a Hash containing: - -- `key: [String]`, the limiter key used for this query -- `limit: [Integer, nil]`, the limit applied to this query -- `soft: [Boolean]`, `true` if the query was run in "soft mode" -- `limited: [Boolean]`, `true` if the query exceeded the rate limit (but if `soft:` was also `true`, then the query was _not_ halted) - -You could use this to add detailed metrics to your application monitoring system, for example: - -```ruby -MyMetrics.increment("graphql.active_operation_limiter", tags: result.context[:active_operation_limiter]) -``` diff --git a/vendor/gems/graphql/guides/limiters/deployment.md b/vendor/gems/graphql/guides/limiters/deployment.md deleted file mode 100644 index 0b2d21c7be7..00000000000 --- a/vendor/gems/graphql/guides/limiters/deployment.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Rate Limiters -title: Deploying Rate Limiters -desc: Tips for releasing limiters smoothly -index: 4 ---- - -Here are a few options for deploying GraphQL-Enterprise's rate limiters: - - -- The [Dashboard](#dashboard) shows some basic metrics about the limiter. -- [Soft limits](#soft-limits) start logging over-limit requests to the dashboard but don't actually halt traffic. -- [Subscriptions](#subscriptions) need extra consideration - - -## Dashboard - -Once installed, your {% internal_link "GraphQL-Pro dashboard", "/pro/dashboard" %} will include a simple metrics view: - -{{ "/limiters/active_operation_limiter_dashboard.png" | link_to_img:"GraphQL Active Operation Limiter Dashboard" }} - -To disable dashboard charts, add `use(... dashboard_charts: false)` to your configuration. - -Also, the dashboard includes a link to enable or disable "soft mode": - -{{ "/limiters/soft_button.png" | link_to_img:"GraphQL Rate Limiter Soft Mode Button" }} - -When "soft mode" is enabled, limited requests are _not_ actually halted (although they are _counted_). When "soft mode" is disabled, any over-limit requests are halted. - -For more detailed metrics, see the "Instrumentation" section of the documentation for each limiter. - -## Soft Limits - -By default, limiters don't actually halt queries; instead, they start out in "soft mode". In this mode: - -- limited/unlimited requests are counted in the [Dashboard](#dashboard) -- but, no requests are actually halted - -This mode is for assessing the impact of the limiter before it's applied to production traffic. Additionally, if you release the limiter but find that it's affecting production traffic adversely, you can re-enable "soft mode" to stop blocking traffic. - -To disable "soft mode" and start limiting, use the [Dashboard](#dashboard) or re-implement some of the customization methods of the limiter. - -You can also disable "soft mode" in Ruby: - -```ruby -# Turn "soft mode" off for the ActiveOperationLimiter -MySchema.enterprise_active_operation_limiter.set_soft_limit(false) -# or, for RuntimeLimiter -MySchema.enterprise_runtime_limiter.set_soft_limit(false) -``` - - -## Subscriptions - -If you're using {% internal_link "PusherSubscriptions", "/subscriptions/pusher_implementation" %} or {% internal_link "AblySubscriptions", "/subscriptions/ably_implementation" %}, then you'll need to accomodate subscriptions that were created _before_ you deployed the rate limiter. Those subscriptions are already stored in Redis and their contexts _don't_ include the required `limiter_key:` value. - -To address this, you can customize the limiter(s) you're using to provide a default value in this case. For example: - -```ruby -class CustomRuntimeLimiter < GraphQL::Enterprise::RuntimeLimiter - def limiter_key(query) - if query.subscription_update? && query.context[:limiter_key].nil? - # This subscription was created before limiter_key was required, - # so provide a value for it. - # If `context` includes enough information to create a - # "real" limiter key, you could also do that here. - # In this case, we're providing a default flag: - "legacy-subscription-update" - else - super - end - end - - def limit_for(key, query) - if key == "legacy-subscription-update" - nil # no limit in this case - else - super - end - end -end -``` - -With methods like that, any subscriptions created _before_ `limiter_key:` was required will not be subject to rate limits. Adjust those methods as needed for your application. Finally, be sure to attach your custom limiter in your schema, for example: - - -```ruby -# Use a custom subclass of GraphQL::Enterprise::RuntimeLimiter: -use CustomRuntimeLimiter, ... -``` diff --git a/vendor/gems/graphql/guides/limiters/overview.md b/vendor/gems/graphql/guides/limiters/overview.md deleted file mode 100644 index 260560ed63f..00000000000 --- a/vendor/gems/graphql/guides/limiters/overview.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Rate Limiters -title: Rate Limiters for GraphQL -desc: Manage access to your GraphQL API -index: 0 ---- - - -`GraphQL::Enterprise` includes rate limiters built especially for GraphQL. - -For REST APIs, rate limiters often count _requests_ and block clients when they exceed their limit over a certain period of time. However, this paradigm doesn't translate well to GraphQL because the cost of serving a request may vary dramatically depending on the GraphQL query contained in the request. Instead, `GraphQL::Enterprise` implements two other kinds of limiters: - -- An __active operation limiter__ which allows clients to run a certain number of operations _at a time_. For example, if the limit is five concurrent operations, and a client sends six requests simultaneously, then only five of those incoming operations will be executed; the sixth will be returned with an error and it may be retried when one of the five others finishes. -- A __runtime limiter__ which limits the amount of processing time a client may consume during a given window. For example, a limit of 120 seconds per minute would allow two concurrent requests on average -- although in practice, it might be spiky: perhaps five concurrent, 20-second-long requests, followed by 40 seconds of no requests. - -There's some overlap in these limiters; both of them constrain the amount of _time_ a client may force the server to spend in handling requests. The active operation limiter puts an upper bound on how _many_ processes a client may occupy while the runtime limiter puts a bound on total processing time (regardless of the number of concurrent operations at a given moment). - -To get started, read on: - -- {% internal_link "Configure Redis", "limiters/redis" %} for the limiters' backend -- {% internal_link "Active Operation Limiter", "limiters/active_operations" %} -- {% internal_link "Runtime Limiter", "limiters/runtime" %} diff --git a/vendor/gems/graphql/guides/limiters/redis.md b/vendor/gems/graphql/guides/limiters/redis.md deleted file mode 100644 index 5528e1fc67b..00000000000 --- a/vendor/gems/graphql/guides/limiters/redis.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Rate Limiters -title: Configuring Redis -desc: Preparing the rate limiter backend -index: 1 ---- - -Rate limiting requires persistent Redis instance, just like [Sidekiq](https://github.com/mperham/sidekiq/wiki/Using-Redis) or the {% internal_link "Operation Store", "/operation_store/redis_backend" %}. Set `maxmemory-policy noeviction` in `redis.conf` to ensure that Redis doesn't silently drop keys when it reaches its memory limit. - -## Memory Usage - -Estimating memory usage depends on the string used to identify clients, since those are used in the Redis keys. Using 100-character client keys, the runtime limiter uses 400 bytes per client (two keys). Memory usage by the active operation limiter depends on the limit because each concurrent operation uses some memory; a higher limit permits more concurrent operations. With 10 active operations and a 100-character client key, the active operation limiter uses 350 bytes per client. Additionally, the limiters use up to 35kb for dashboards (2 limiters, for each one: 2x 60 per-minute keys, 24 hourly keys, and 30 daily keys @ 72 bytes per key). - -By those estimates, 1 gigabyte of memory would support both rate limiters for over 1.4 million active clients. diff --git a/vendor/gems/graphql/guides/limiters/runtime.md b/vendor/gems/graphql/guides/limiters/runtime.md deleted file mode 100644 index 971f95d63ef..00000000000 --- a/vendor/gems/graphql/guides/limiters/runtime.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Rate Limiters -title: Runtime Limiter -desc: Limit the total runtime of a client's GraphQL Operations -index: 3 ---- - -`GraphQL::Enterprise::RuntimeLimiter` applies an upper bound to processing time consumed by a single client. It uses {% internal_link "Redis", "limiters/redis" %} track time with a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm. - -## Why? - -This limiter prevents a single client from consuming too much processing time, regardless of whether it comes a burst of short-lived queries (which the {% internal_link "Active Operation Limiter", "/limiters/active_operations" %} can prevent) or a small number of long-running queries. Unlike request counters or complexity calculations, the runtime limiter pays no attention to the structure of the incoming request. Instead, it simply measures the time spent on the request _as a whole_ and halts queries when a client consumes more than the limit. - -## Setup - -To use this limiter, update the schema configuration and include `context[:limiter_key]` in your queries. - -### Schema Setup - -To setup the schema, add `use GraphQL::Enterprise::RuntimeLimiter` with a default `limit_ms:` value: - -```ruby -class MySchema < GraphQL::Schema - # ... - use GraphQL::Enterprise::RuntimeLimiter, - redis: Redis.new(...), - limit_ms: 90 * 1000 # 90 seconds per minute -end -``` - -`limit_ms: false` may also be given, which defaults to _no limit_ for this limiter. - -It also accepts a `window_ms:` option, which is the duration over which `limit_ms:` is added to a client's bucket. It defaults to `60_000` (one minute). - -Before requests will actually be halted, {% internal_link "soft mode", "/limiters/deployment#soft-limits" %} must be disabled. - -### Query Setup - -In order to limit clients, the limiter needs a client identifier for each GraphQL operation. By default, it checks `context[:limiter_key]` to find it: - -```ruby -context = { - viewer: current_user, - # for example: - limiter_key: logged_in? ? "user:#{current_user.id}" : "anon-ip:#{request.remote_ip}", - # ... -} - -result = MySchema.execute(query_str, context: context) -``` - -Operations with the same `context[:limiter_key]` will rate limited in the same buckets. A limiter key is required; if a query is run without one, the limiter will raise an error. - -To provide a client identifier another way, see [Customization](#customization). - -## Customization - -`GraphQL::Enterprise::RuntimeLimiter` provides several hooks for customizing its behavior. To use these, make a subclass of the limiter and override methods as described: - -```ruby -# app/graphql/limiters/runtime.rb -class Limiters::Runtime < GraphQL::Enterprise::RuntimeLimiter - # override methods here -end -``` - -The hooks are: - -- `def limiter_key(query)` should return a string which identifies the current client for `query`. -- `def limit_for(key, query)` should return an integer or `nil`. If an integer is returned, that limit is applied for the current query. If `nil` is returned, no limit is applied to the current query. -- `def soft_limit?(key, query)` can be implemented to customize the application of "soft mode". By default, it checks a setting in redis. -- `def handle_redis_error(err)` is called when the limit rescues an error from Redis. By default, it's passed to `warn` and the query is _not_ halted. - -## Instrumentation - -While the limiter is installed, it adds some information to the query context about its operation. It can be accessed at `context[:runtime_limiter]`: - - -```ruby -result = MySchema.execute(...) - -pp result.context[:runtime_limiter] -# {:key=>"custom-key-9", -# :limit_ms=>800, -# :remaining_ms=>0, -# :soft=>true, -# :limited=>true, -# :window_ms=>60_000} -``` - -It returns a Hash containing: - -- `key: [String]`, the limiter key used for this query -- `limit_ms: [Integer, nil]`, the limit applied to this query -- `remaining_ms: [Integer, nil]`, the amount of time remaining in this client's bucket -- `soft: [Boolean]`, `true` if the query was run in "soft mode" -- `limited: [Boolean]`, `true` if the query exceeded the rate limit (but if `soft:` was also `true`, then the query was _not_ halted) -- `window_ms: [Integer]` the configured `window_ms:` for the limiter - -You could use this to add detailed metrics to your application monitoring system, for example: - -```ruby -MyMetrics.increment("graphql.runtime_limiter", tags: result.context[:runtime_limiter]) -``` - -## Some Caveats - -The limiter will not _interrupt_ a long-running field. Instead, it stops executing new fields after a client exceeds its allowed processing time. This is because interrupting arbitrary code may have unintended consequences for I/O operations, see ["Timeout: Ruby's most dangerous API"](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/). - -Also, the limiter only checks remaining time at the _start_ of a query and it only decreases the remaining time at the _end_ of a query. This means that simulaneous queries may consume the remainder at the same time. Use the {% internal_link "Active Operation Limiter", "/limiters/active_operations" %} to limit behavior in this regard. This implementation is basically a trade-off: more granular updates would require more communication with Redis which would add overhead to each request. diff --git a/vendor/gems/graphql/guides/limiters/runtime_limiter_dashboard.png b/vendor/gems/graphql/guides/limiters/runtime_limiter_dashboard.png deleted file mode 100644 index d79626e7a372f79b710728f2bb23ff1f9e64a4f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53130 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�Rwqo|}Lm*i@bBkB0JXrZtfaGcFXDZ3kcV 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 diff --git a/vendor/gems/graphql/guides/limiters/soft_button.png b/vendor/gems/graphql/guides/limiters/soft_button.png deleted file mode 100644 index 3c162fd563e3eed0b763b74a5fa234ce9ab85175..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4735 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 diff --git a/vendor/gems/graphql/guides/mutations/mutation_authorization.md b/vendor/gems/graphql/guides/mutations/mutation_authorization.md deleted file mode 100644 index 7beca36881a..00000000000 --- a/vendor/gems/graphql/guides/mutations/mutation_authorization.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Mutations -title: Mutation authorization -desc: Checking permissions for mutations -index: 3 ---- - -Before running a mutation, you probably want to do a few things: - -- Make sure the current user has permission to try this mutation -- Load some objects from the database, using some `ID` inputs -- Check if the user has permission to modify those loaded objects - -This guide describes how to accomplish that workflow with GraphQL-Ruby. - -## Checking conditions before instantiating the mutation - -```ruby -class UpdateUserMutation < BaseMutation - # ... - - def resolve(update_user_input:, user:) - # ... - end - - def self.authorized?(obj, ctx) - super && ctx[:viewer].present? - end -end -``` - -## Checking the user permissions - -Before loading any data from the database, you might want to see if the user has a certain permission level. For example, maybe only `.admin?` users can run `Mutation.promoteEmployee`. - -This check can be implemented using the `#ready?` method in a mutation: - -```ruby -class Mutations::PromoteEmployee < Mutations::BaseMutation - def ready?(**args) - # Called with mutation args. - # Use keyword args such as employee_id: or **args to collect them - if !context[:current_user].admin? - raise GraphQL::ExecutionError, "Only admins can run this mutation" - else - # Return true to continue the mutation: - true - end - end - - # ... -end -``` - -Now, when any non-`admin` user tries to run the mutation, it won't run. Instead, they'll get an error in the response. - -Additionally, `#ready?` may return `false, { ... }` to return {% internal_link "errors as data", "/mutations/mutation_errors" %}: - -```ruby -def ready? - if !context[:current_user].allowed? - return false, { errors: ["You don't have permission to do this"]} - else - true - end -end -``` - -## Loading and authorizing objects - -Often, mutations take `ID`s as input and use them to load records from the database. GraphQL-Ruby can load IDs for you when you provide a `loads:` option. - -In short, here's an example: - - -```ruby -class Mutations::PromoteEmployee < Mutations::BaseMutation - # `employeeId` is an ID, Types::Employee is an _Object_ type - argument :employee_id, ID, loads: Types::Employee - - # Behind the scenes, `:employee_id` is used to fetch an object from the database, - # then the object is authorized with `Employee.authorized?`, then - # if all is well, the object is injected here: - def resolve(employee:) - employee.promote! - end -end -``` - -It works like this: if you pass a `loads:` option, it will: - -- Automatically remove `_id` from the name and pass that name for the `as:` option -- Add a prepare hook to fetch an object with the given `ID` (using {{ "Schema.object_from_id" | api_doc }}) -- Check that the fetched object's type matches the `loads:` type (using {{ "Schema.resolve_type" | api_doc }}) -- Run the fetched object through its type's `.authorized?` hook (see {% internal_link "Authorization", "/authorization/authorization" %}) -- Inject it into `#resolve` using the object-style name (`employee:`) - -In this case, if the argument value is provided by `object_from_id` doesn't return a value, the mutation will fail with an error. - -Alternatively if your `ID` doesn't specify both class _and_ id, resolvers have a `load_#{argument}` method that can be overridden. - -```ruby -argument :employee_id, ID, loads: Types::Employee - -def load_employee(id) - ::Employee.find(id) -end -``` - -If you don't want this behavior, don't use it. Instead, create arguments with type `ID` and use them your own way, for example: - -```ruby -# No special loading behavior: -argument :employee_id, ID -``` - -## Can _this user_ perform _this action_? - -Sometimes you need to authorize a specific user-object(s)-action combination. For example, `.admin?` users can't promote _all_ employees! They can only promote employees which they manage. - -You can add this check by implementing a `#authorized?` method, for example: - -```ruby -def authorized?(employee:) - super && context[:current_user].manager_of?(employee) -end -``` - -When `#authorized?` returns `false` (or something falsey), the mutation will be halted. If it returns `true` (or something truthy), the mutation will continue. - -#### Adding errors - -To add errors as data (as described in {% internal_link "Mutation errors", "/mutations/mutation_errors" %}), return a value _along with_ `false`, for example: - -```ruby -def authorized?(employee:) - super && if context[:current_user].manager_of?(employee) - true - else - return false, { errors: ["Can't promote an employee you don't manage"] } - end -end -``` - -Alternatively, you can add top-level errors by raising `GraphQL::ExecutionError`, for example: - -```ruby -def authorized?(employee:) - super && if context[:current_user].manager_of?(employee) - true - else - raise GraphQL::ExecutionError, "You can only promote your _own_ employees" - end -end -``` - -In either case (returning `[false, data]` or raising an error), the mutation will be halted. - -## Finally, doing the work - -Now that the user has been authorized in general, data has been loaded, and objects have been validated in particular, you can modify the database using `#resolve`: - -```ruby -def resolve(employee:) - if employee.promote - { - employee: employee, - errors: [], - } - else - # See "Mutation Errors" for more: - { - errors: employee.errors.full_messages - } - end -end -``` diff --git a/vendor/gems/graphql/guides/mutations/mutation_classes.md b/vendor/gems/graphql/guides/mutations/mutation_classes.md deleted file mode 100644 index 57eaee6d9a4..00000000000 --- a/vendor/gems/graphql/guides/mutations/mutation_classes.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Mutations -title: Mutation Classes -desc: Use mutation classes to implement behavior, then hook them up to your schema. -index: 1 -redirect_from: - - /queries/mutations/ - - /relay/mutations/ ---- - -GraphQL _mutations_ are special fields: instead of reading data or performing calculations, they may _modify_ the application state. For example, mutation fields may: - -- Create, update or destroy records in the database -- Establish associations between already-existing records in the database -- Increment counters -- Create, modify or delete files -- Clear caches - -These actions are called _side effects_. - -Like all GraphQL fields, mutation fields: - -- Accept inputs, called _arguments_ -- Return values via _fields_ - -GraphQL-Ruby includes two classes to help you write mutations: - -- {{ "GraphQL::Schema::Mutation" | api_doc }}, a bare-bones base class -- {{ "GraphQL::Schema::RelayClassicMutation" | api_doc }}, a base class with a set of nice conventions that also supports the Relay Classic mutation specification. - -Besides those, you can also use the plain {% internal_link "field API", "/type_definitions/objects#fields" %} to write mutation fields. - -## Example mutation class - -If you used the {% internal_link "install generator", "/schema/generators#graphqlinstall" %}, a base mutation class will already have been generated for you. If that's not the case, you should add a base class to your application, for example: - -```ruby -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - # Add your custom classes if you have them: - # This is used for generating payload types - object_class Types::BaseObject - # This is used for return fields on the mutation's payload - field_class Types::BaseField - # This is used for generating the `input: { ... }` object type - input_object_class Types::BaseInputObject -end -``` - -Then extend it for your mutations: - -```ruby -class Mutations::CreateComment < Mutations::BaseMutation - null true - argument :body, String - argument :post_id, ID - - field :comment, Types::Comment - field :errors, [String], null: false - - def resolve(body:, post_id:) - post = Post.find(post_id) - comment = post.comments.build(body: body, author: context[:current_user]) - if comment.save - # Successful creation, return the created object with no errors - { - comment: comment, - errors: [], - } - else - # Failed save, return the errors to the client - { - comment: nil, - errors: comment.errors.full_messages - } - end - end -end -``` - -The `#resolve` method should return a hash whose symbols match the `field` names. - -(See {% internal_link "Mutation Errors", "/mutations/mutation_errors" %} for more information about returning errors.) - -Also, you can configure `null(false)` in your mutation class to make the generated payload class non-null. - -## Hooking up mutations - -Mutations must be attached to the mutation root using the `mutation:` keyword, for example: - -```ruby -class Types::Mutation < Types::BaseObject - field :create_comment, mutation: Mutations::CreateComment -end -``` - -## Auto-loading arguments - -In most cases, a GraphQL mutation will act against a given global relay ID. Loading objects from these global relay IDs can require a lot of boilerplate code in the mutation's resolver. - -An alternative approach is to use the `loads:` argument when defining the argument: - -```ruby -class Mutations::AddStar < Mutations::BaseMutation - argument :post_id, ID, loads: Types::Post - - field :post, Types::Post - - def resolve(post:) - post.star - - { - post: post, - } - end -end -``` - -By specifying that the `post_id` argument loads a `Types::Post` object type, a `Post` object will be loaded via {% internal_link "`Schema#object_from_id`", "/schema/definition.html#object-identification-hooks" %} with the provided `post_id`. - -All arguments that end in `_id` and use the `loads:` method will have their `_id` suffix removed. For example, the mutation resolver above receives a `post` argument which contains the loaded object, instead of a `post_id` argument. - -The `loads:` option also works with list of IDs, for example: - -```ruby -class Mutations::AddStars < Mutations::BaseMutation - argument :post_ids, [ID], loads: Types::Post - - field :posts, [Types::Post] - - def resolve(posts:) - posts.map(&:star) - - { - posts: posts, - } - end -end -``` - -All arguments that end in `_ids` and use the `loads:` method will have their `_ids` suffix removed and an `s` appended to their name. For example, the mutation resolver above receives a `posts` argument which contains all the loaded objects, instead of a `post_ids` argument. - -In some cases, you may want to control the resulting argument name. This can be done using the `as:` argument, for example: - -```ruby -class Mutations::AddStar < Mutations::BaseMutation - argument :post_id, ID, loads: Types::Post, as: :something - - field :post, Types::Post - - def resolve(something:) - something.star - - { - post: something - } - end -end -``` - -In the above examples, `loads:` is provided a concrete type, but it also supports abstract types (i.e. interfaces and unions). - -### Resolving the type of loaded objects - -When `loads:` gets an object from {{ "Schema.object_from_id" | api_doc }}, it passes that object to {{ "Schema.resolve_type" | api_doc }} to confirm that it resolves to the same type originally configured with `loads:`. - -### Handling failed loads - -If `loads:` fails to find an object or if the loaded object isn't resolved to the specified `loads:` type (using {{ "Schema.resolve_type" | api_doc }}), a {{ "GraphQL::LoadApplicationObjectFailedError" | api_doc }} is raised and returned to the client. - -You can customize this behavior by implementing `def load_application_object_failed` in your mutation class, for example: - -```ruby -def load_application_object_failed(error) - raise GraphQL::ExecutionError, "Couldn't find an object for ID: `#{error.id}`" -end -``` - -Or, if `load_application_object_failed` returns a new object, that object will be used as the `loads:` result. - -### Handling unauthorized loaded objects - -When an object is _loaded_ but fails its {% internal_link "`.authorized?` check", "/authorization/authorization#object-authorization" %}, a {{ "GraphQL::UnauthorizedError" | api_doc }} is raised. By default, it's passed to {{ "Schema.unauthorized_object" | api_doc }} (see {% internal_link "Handling Unauthorized Objects", "/authorization/authorization.html#handling-unauthorized-objects" %}). You can customize this behavior by implementing `def unauthorized_object(err)` in your mutation, for example: - -```ruby -def unauthorized_object(error) - # Raise a nice user-facing error instead - raise GraphQL::ExecutionError, "You don't have permission to modify the loaded #{error.type.graphql_name}." -end -``` diff --git a/vendor/gems/graphql/guides/mutations/mutation_errors.md b/vendor/gems/graphql/guides/mutations/mutation_errors.md deleted file mode 100644 index 6d1f74cd363..00000000000 --- a/vendor/gems/graphql/guides/mutations/mutation_errors.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Mutations -title: Mutation errors -desc: Tips for handling and returning errors from mutations -index: 2 ---- - -How can you handle errors inside mutations? Let's explore a couple of options. - -## Raising Errors - -One way to handle an error is by raising, for example: - -```ruby -def resolve(id:, attributes:) - # Will crash the query if the data is invalid: - Post.find(id).update!(attributes.to_h) - # ... -end -``` - -Or: - -```ruby -def resolve(id:, attributes:) - if post.update(attributes) - { post: post } - else - raise GraphQL::ExecutionError, post.errors.full_messages.join(", ") - end -end -``` - -This kind of error handling _does_ express error state (either via `HTTP 500` or by the top-level `"errors"` key), but it doesn't take advantage of GraphQL's type system and can only express one error at a time. It works, but a stronger solution is to treat errors as data. - -## Errors as Data - -Another way to handle rich error information is to add _error types_ to your schema, for example: - -```ruby -class Types::UserError < Types::BaseObject - description "A user-readable error" - - field :message, String, null: false, - description: "A description of the error" - field :path, [String], - description: "Which input value this error came from" -end -``` - -Then, add a field to your mutation which uses this error type: - -```ruby -class Mutations::UpdatePost < Mutations::BaseMutation - # ... - field :errors, [Types::UserError], null: false -end -``` - -And in the mutation's `resolve` method, be sure to return `errors:` in the hash: - -```ruby -def resolve(id:, attributes:) - post = Post.find(id) - if post.update(attributes) - { - post: post, - errors: [], - } - else - # Convert Rails model errors into GraphQL-ready error hashes - user_errors = post.errors.map do |error| - # This is the GraphQL argument which corresponds to the validation error: - path = ["attributes", error.attribute.to_s.camelize(:lower)] - { - path: path, - message: error.message, - } - end - { - post: post, - errors: user_errors, - } - end -end -``` - -Now that the field returns `errors` in its payload, it supports `errors` as part of the incoming mutations, for example: - -```graphql -mutation($postId: ID!, $postAttributes: PostAttributes!) { - updatePost(id: $postId, attributes: $postAttributes) { - # This will be present in case of success or failure: - post { - title - comments { - body - } - } - # In case of failure, there will be errors in this list: - errors { - path - message - } - } -} -``` - -In case of a failure, you might get a response like: - -```ruby -{ - "data" => { - "createPost" => { - "post" => nil, - "errors" => [ - { "message" => "Title can't be blank", "path" => ["attributes", "title"] }, - { "message" => "Body can't be blank", "path" => ["attributes", "body"] } - ] - } - } -} -``` - -Then, client apps can show the error messages to end users, so they might correct the right fields in a form, for example. - -## Nullable Mutation Payload Fields - -To benefit from "Errors as Data" described above, mutation fields must not have `null: false`. Why? - -Well, for _non-null_ fields (which have `null: false`), if they return `nil`, then GraphQL aborts the query and removes those fields from the response altogether. - -In mutations, when errors happen, the other fields may return `nil`. So, if those other fields have `null: false`, but they return `nil`, the GraphQL will panic and remove the whole mutation from the response, _including_ the errors! - -In order to have the rich error data, even when other fields are `nil`, those fields must have `null: true` (which is the default) so that the type system can be obeyed when errors happen. - -Here's an example of a nullable field (good!): - -```ruby -class Mutations::UpdatePost < Mutations::BaseMutation - # Use the default `null: true` to support rich errors: - field :post, Types::Post - # ... -end -``` diff --git a/vendor/gems/graphql/guides/mutations/mutation_root.md b/vendor/gems/graphql/guides/mutations/mutation_root.md deleted file mode 100644 index 08141ea8b32..00000000000 --- a/vendor/gems/graphql/guides/mutations/mutation_root.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Mutations -title: Mutation Root -desc: The Mutation object is the entry point for mutation operations. -index: 0 ---- - -GraphQL mutations all begin with the `mutation` keyword: - -```graphql -mutation($accountNumber: ID!, $newBalance: Int!) { -# ^^^^ here - setAccountBalance(accountNumber: $accountNumber, newBalance: $newBalance) { - # ... - } -} -``` - -Operations that begin with `mutation` get special treatment by the GraphQL runtime: root fields are guaranteed -to be executed sequentially. This way, the effect of a series of mutations is predictable. - -Mutations are executed by a specific GraphQL object, `Mutation`. This object is defined like any other GraphQL object: - -```ruby -class Types::Mutation < Types::BaseObject - # ... -end -``` - -Then, it must be attached to your schema with the `mutation(...)` configuration: - -```ruby -class Schema < GraphQL::Schema - # ... - mutation(Types::Mutation) -end -``` - -Now, whenever an incoming request uses the `mutation` keyword, it will go to `Mutation`. - -See {% internal_link "Mutation Classes", "/mutations/mutation_classes" %} for some helpers to define mutation fields. diff --git a/vendor/gems/graphql/guides/object_cache/caching.md b/vendor/gems/graphql/guides/object_cache/caching.md deleted file mode 100644 index 1d39b94b14e..00000000000 --- a/vendor/gems/graphql/guides/object_cache/caching.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Object Cache -title: Caching Results -desc: Configuration options for caching objects and fields -index: 2 ---- - -`GraphQL::Enterprise::ObjectCache` supports several different caching configurations for objects and fields. To get started, include the extension in your base object class and base field class and use `cacheable(...)` to set up the default cache behavior: - -```ruby -# app/graphql/types/base_object.rb -class Types::BaseObject < GraphQL::Schema::Object - include GraphQL::Enterprise::ObjectCache::ObjectIntegration - field_class Types::BaseField - cacheable(...) # see below - # ... -end -``` - -```ruby -# app/graphql/types/base_field.rb -class Types::BaseField < GraphQL::Schema::Field - include GraphQL::Enterprise::ObjectCache::FieldIntegration - cacheable(...) # see below - # ... -end -``` - -Also, make sure your base interface module is using your field class: - -```ruby -# app/graphql/types/base_interface.md -module Types::BaseInterface - field_class Types::BaseField -end -``` - -Field caching can be configured per-field, too, for example: - -```ruby -field :latest_update, Types::Update, null: false, cacheable: { ttl: 60 } - -field :random_number, Int, null: false, cacheable: false -``` - -Only _queries_ are cached. `ObjectCache` skips mutations and subscriptions altogether. - -## `cacheable(true|false)` - -`cacheable(true)` means that the configured type or field may be stored in the cache until its cache fingerprint changes. It also defaults to `public: false`, meaning that clients will _not_ share cached responses. See [`public:`](#public) below for more about this option. - -`cacheable(false)` disables caching for the configured type or field. Any query that includes this type or field will neither check for an already-cached value nor update the cache with its result. - -## `public:` - -`cacheable(public: false)` means that a type or field may be _cached_, but {% internal_link "`Schema.private_context_fingerprint_for(ctx)`", "/object_cache/schema_setup#context-fingerprint" %} should be included in its cache key. In practice, this means that each client can have its own cached responses. Any query that contains a `cacheable(public: false)` type or field will use a private cache key. - -`cacheable(public: true)` means that cached values from this type or field may be shared by _all_ clients. Use this for public-facing data which is the same for all viewers. Queries that include _only_ `public: true` types and fields will not include `Schema.private_context_fingerprint_for(ctx)` in their cache keys. That way their responses will be shared by all clients who request them. - -## `ttl:` - -`cacheable(ttl: seconds)` expires any cached value after the given number of seconds, regardless of cache fingerprint. `ttl:` shines in a few cases: - -- Objects that can't reliably generate a fingerprint value (for example, they have no `.updated_at` timestamp). In this case, a conservative `ttl` may be the only option for cache expiration. -- Or, root-level fields that should be expired after a certain amount of time. The root-level `Query` often has _no_ backing object, so it won't have a cache fingerprint, either. Adding `cacheable: { ttl: ... }` to root level fields will provide some caching along with a guarantee about when they'll be expired. -- Or, list responses that may be difficult to invalidate properly (see below). - -Under the hood, `ttl:` is implemented with Redis's `EXPIRE`. - -## Caching lists and connections - -Lists and connections require a little extra consideration. By default, each _item_ in a list is registered with the cache, but when new items are created, they are unknown to the cache and therefore don't invalidate the cached result. There are two main approaches to address this. - -### `has_many` lists - -In order to effectively bust the cache, items that belong to the list of "parent" object should __update the parent__ (eg, Rails `.touch`) whenever they're created, destroyed, or updated. For example, if there's a list of players on a team: - -```graphql -{ - team { players { totalCount } } -} -``` - -None of the _specific_ `Player`s will be part of the cached response, but the `Team` will be. To properly invalidate the cache, the `Team`'s `updated_at` (or other cache key) should be updated whenever a `Player` is added or removed from the `Team`. - -If a list may be sorted, then updates to `Player`s should also update the `Team` so that any sorted results in the cache are invalidated, too. Alternatively (or additionally), you could use a `ttl:` to expire cached results after a certain duration, just to be sure that results are eventually expired. - -With Rails, you can accomplish this with: - -```ruby - # update the team whenever a player is saved or destroyed: - belongs_to :team, touch: true -``` - -### Top-level lists - -For `ActiveRecord::Relation`s _without_ a "parent" object, you can use `GraphQL::Enterprise::ObjectCache::CacheableRelation` to make a synthetic cache entry for the _whole_ relation. To use this class, make a subclass and implement `def items`, for example: - -```ruby -class AllTeams < GraphQL::Enterprise::ObjectCache::CacheableRelation - def items(division: nil) - teams = Team.all - if division - teams = teams.where(division: division) - end - teams - end -end -``` - -Then, in your resolver, use your new class to retrieve the items: - -```ruby -class Query < GraphQL::Schema::Object - field :teams, Team.connection_type do - argument :division, Division, required: false - end - - def teams(division: nil) - AllTeams.items_for(self, division: division) - end -end -``` - -If you're using {{ "GraphQL::Schema::Resolver" | api_doc }}, you'd call `.items_for` slightly differently: - -```ruby -def resolve(division: nil) - # use `context[:current_object]` to get the GraphQL::Schema::Object instance whose field is being resolved - AllTeams.items_for(context[:current_object], division: division) -end -``` - -Finally, you'll need to handle `CacheableRelation`s in your object identification methods, for example: - -```ruby -class MySchema < GraphQL::Schema - # ... - def self.id_from_object(object, type, ctx) - if object.is_a?(GraphQL::Enterprise::ObjectCache::CacheableRelation) - object.id - else - # The rest of your id_from_object logic here... - end - end - - def self.object_from_id(id, ctx) - if (cacheable_rel = GraphQL::Enterprise::ObjectCache::CacheableRelation.find?(id)) - cacheable_rel - else - # The rest of your object_from_id logic here... - end - end -end -``` - -In this example, `AllTeams` implements several methods to support caching: - -- `#id` creates a cache-friendly, stable global ID -- `#to_param` creates a cache fingerprint (using Rails's `#cache_key` under the hood) -- `.find?` retrieves the list based on its ID - -This way, if a `Team` is created, the cached result will be invalidated and a fresh result will be created. - -Alternatively (or additionally), you could use a `ttl:` to expire cached results after a certain duration, just to be sure that results are eventually expired. - -### Connections - -By default, connection-related objects (like `*Connection` and `*Edge` types) "inherit" cacheability from their node types. You can override this in your base classes as long as `GraphQL::Enterprise::ObjectCache::ObjectIntegration` is included in the inheritance chain somewhere. - -## Caching Introspection - -By default, introspection fields are considered _public_ for all queries. This means that they are considered cacheable and their results will be reused for any clients who request them. When {% internal_link "adding the ObjectCache to your schema", "/object_cache/schema_setup#add-the-cache", %}, you can provide some options to customize this behavior: - -- `cache_introspection: { public: false, ... }` to use [`public: false`](#public) for all introspection fields. Use this if you hide schema members for some clients. -- `cache_introspection: false` to completely disable caching on introspection fields. -- `cache_introspection: { ttl: ..., ... }` to set a [ttl](#ttl) (in seconds) for introspection fields. - -## Object Dependencies - -By default, the `object` of a GraphQL Object type is used for caching the fields selected on that object. But, you can specify what object (or objects) should be used to check the cache by implementing `def self.cache_dependencies_for(object, context)` in your type definition. For example: - -```ruby -class Types::Player - def self.cache_dependencies_for(player, context) - # we update the team's timestamp whenever player details change, - # so ignore the `player` for caching purposes - player.team - end -end -``` - -Use this to: - -- improve performance when caching lists of children that belong to a parent object -- register other objects with the ObjectCache when running a query. (`cacheable_object(obj)` or `def self.object_fingerprint_for` can also be used in this case.) - -If this method returns an `Array`, each object in the array will be registered with the cache. diff --git a/vendor/gems/graphql/guides/object_cache/memcached.md b/vendor/gems/graphql/guides/object_cache/memcached.md deleted file mode 100644 index 7ab0b9af266..00000000000 --- a/vendor/gems/graphql/guides/object_cache/memcached.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Object Cache -title: Dalli Configuration -desc: Setting up the Memcached backend -index: 3 ---- - -`GraphQL::Enterprise::ObjectCache` can also run with a Memcached backend via the [Dalli](https://github.com/petergoldstein/dalli) client gem. - -Set it up by passing a `Dalli::Client` instance as `dalli: ...`, for example: - -```ruby -use GraphQL::Enterprise::OperationStore, dalli: Dalli::Client.new(...) -``` diff --git a/vendor/gems/graphql/guides/object_cache/overview.md b/vendor/gems/graphql/guides/object_cache/overview.md deleted file mode 100644 index d04d93e648f..00000000000 --- a/vendor/gems/graphql/guides/object_cache/overview.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Object Cache -title: GraphQL ObjectCache -desc: A server-side cache for GraphQL-Ruby -index: 0 ---- - -`GraphQL::Enterprise::ObjectCache` is an application-level cache for GraphQL-Ruby servers. It works by storing a {% internal_link "_cache fingerprint_ for each object", "/object_cache/schema_setup#object-fingerprint" %} in a query, then serving a cached response as long as those fingerprints don't change. The cache can also be customized with {% internal_link "TTLs", "/object_cache/caching#ttl" %}. - -## Why? - -`ObjectCache` can greatly reduce GraphQL response times by serving cached responses when the underlying data for a query hasn't changed. - -Usually, a GraphQL query alternates between data fetching and calling application logic: - - -{{ "/object_cache/query-without-cache.png" | link_to_img:"GraphQL-Ruby profile, without caching" }} - - -But with `ObjectCache`, it checks the cache first, returning a cached response if possible: - -{{ "/object_cache/query-with-cache.png" | link_to_img:"GraphQL-Ruby profile, with ObjectCache" }} - -This reduces latency for clients and reduces the load on your database and application server. - -## How - -Before running a query, `ObjectCache` creates a fingerprint for the query using {{ "GraphQL::Query#fingerprint" | api_doc }} and {% internal_link "`Schema.context_fingerprint_for(ctx)`", "/object_cache/schema_setup#context-fingerprint" %}. Then, it checks the backend for a cached response which matches the fingerprint. - -If a match is found, the `ObjectCache` fetches the objects previously visited by this query. Then, it compares the current fingerprint of each object ot the one in the cache and checks `.authorized?` for that object. If the fingerprints all match and all objects pass authorization checks, then the cached response returned. (Authorization checks can be {% internal_link "disabled", "/object_cache/schema_setup#disabling-reauthorization" %}.) - -If there is no cached response or if the fingerprints don't match, then the incoming query is re-evaluated. While it's executed, `ObjectCache` gathers the IDs and fingerprints of each object it encounters. When the query is done, the result and the new object fingerprints are written to the cache. - -## Setup - -To get started with the object cache: - -- {% internal_link "Prepare the schema", "/object_cache/schema_setup" %} -- Set up a {% internal_link "Redis backend", "/object_cache/redis" %} or {% internal_link "Memcached backend", "/object_cache/memcached" %} -- {% internal_link "Configure types and fields for caching", "/object_cache/caching" %} -- Check out the {% internal_link "runtime considerations", "/object_cache/runtime_considerations" %} diff --git a/vendor/gems/graphql/guides/object_cache/query-with-cache.png b/vendor/gems/graphql/guides/object_cache/query-with-cache.png deleted file mode 100644 index 1fc156866c710aa07406c0963a8cc14f9faffcf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13044 zcmd6OcT`jDv#yHLrB~@qst^zaf>c33dI`M*5R?+71nC`>qBJQXbdbg2=Q^()UYb_wQFL{>dK0S-d39#o{3DVO-ECM@9Z4jrKYAb+B!Hyq;fbo zyc%>tCQiHkge_0IQ@y2r`|8-iYrECY!>ui7J?%$EBD#2TP;F1`S!rP4kB7Xd3D5+) z%He_e%i600UL`_~aEkx)rEB;~cADoeED~uKYsv_<0+p5}_MF6z}tA2L34G z5aG11lDHMf?!pma#2L?lxD~x45ZG)bUN{-VBu&aB8);Q;_Y)dnE*5w%N|a# z#PQ$ob9pVKj`39Sv6p5&_H%3X(=5}hsEPKV8m%pqSBg>Nz`|E)OIi8GZlfvmhdh$i zOZaL<{NEcsLP3ARWOREBV-4)19v2CkBc?IZmH_$&c@5+%kCc41OpC)8mIFpphWnUR zP$sjf%jzZJ8~e*%?Yccc^k3GkUDHC24&R>0y!EI>yvSN3Ne<$a1Oz=fJov?FZq7T9 zf@MHgwqe9d+RMs|j73>a-0cnSZM4`3l2`w|3$jt@Pjhi}9eLrYWz-L)(jn2myu?ewEt`5OvVM-UgMasK2=_h z-k7OE0Be0q%VOk#tq$pM$=tltsabQ#*@CH@{ypXi(~IrA`KtH+?}uI<8d$=OJ}leD zS&KJCpANjgz4f3?lK>&BU3zDE;`muXO7U)hS>auY{^IMK8$~)X_YNF^m#>_w&17op z*U=5d%+*>KQR|zLvJ>(zUdCTYy-W{695{k@LmOAOV`GI~>#vTMW}Pq{5B8)N{oSmB zu3W39M5*+|S#bXPP?i)E^&}^!NeU{;TfNqHQ2g&`XvM-9dd?teCW`z&^Q_1cZ zET7gn3P9Ek1)fd5E7sY-6gv*NS6Q;_yb?iO=Q5HKDCmz9e!o81(!^Zt+sHXCe@Sxx zU@DEN=iRrKs%OLOLG8_sRW+(g&R-53SAeePTSLyh&-8M^tcS}<5_Nv@gnvg2kWv$A z{KL4gDd|b?amM}=OIcamus38Rg!8fOS;D=e|hx0VlVvQtkuNhdY;`q)s>qUorz$kF=ChHUz5HD z1U1QfCFXV?oT$ZE0K_W$KN6TpHw3cZ-(N_4WrPZG)=U;kZE79yQz3Cz);->()KUJN zZguiw9x`s+r{sgK*e>%*`Fw6a9H;H+V$oAFqD=MMc`3-puEWV5zF&4U7xX$B)F(TH z7?>jrb+696jEn39RZHaCw^N;h0k?yjU`k{502YQdPjf;9p3VlX23;cmb;}t3+scdw zuiidR0yT*jO|;MNW|Ey`sWyMq(UdnCxS+)MV!$gQ>YyR!BmnSCe>p%d+E?Na6nkvg zYSAKWKV`&W8@yc0A?p2cXh zp}#<$3nU7$>)mWw?lL3zLZGuQMQ#9Kd7bM%eX6AvyO1h^w2+m4u(u~7DH(ZPDY3_l zTrVoE+2M(yt5TtHA;T?8{B?SsNZLnhkn*tBgT$sePA%B^$dV(YmA&p;qIptB(1` zf9Hg}_qanLMhpdIkL~=4*9Cq@NX#kpoB$+f-p!)emvrw#e}ZVnEMaPwG%VnhYvrxs zJ#&-G1)yIr^Koa6&(?*=urM{=8}}6zLw*C3+t|5|4C|!|>@=XY;-};gjd-cXBN0>x z_5`lx>(R}rHeZD*cmUd;w(h59}?Ir35JH%1jOa_2B5`vfg107PiBii(GgQo%$IZi{; z$y(vJl(b1O*tz6zO#-7zlMVCNv;sz`>mTsKJR!4&6}}qI@{%}!O;FRPy!Yid@cCBg*5O48R?r^HX*yAWa-vf z9MZQgenriJK@>+eGnK*vSu$zHVnT!X>n0wKHLfoH`0WkQqyTemHzQm_h~{ClEtEs^|mHOTA;LZZ`&?Z#hwbHsgl9qiGH^ctXe z*CJySxc1$a$1Y4*?ETKq@Mn!L{JW|QHE(y)baa~>bL@poNTf(2zuU5J+imT{*z&o~ zY280l_fbON!N=ua^$eIl!W4a#o93QE=W6>j>R_PkeRL;)h9C@hF&zL2^?hDpJgE?I z&1Rj5l1ABC7oR$GL-By<`|V?Dq_TO-Y`1SfB=_I^|L7?YP{_f115yJfjVWc|kBs&v zc%7NNV-IA95tM%WbQH4oxyo%pP5~)%!v!wJX4gDd%i`9 zX4+hydb(9^nd42GBFm|9i{Fb zp#r75e}l~O6j=D_ZyoaAtg`-N4R?9gH(YoX@Tr{;CmJtzf2#jzm%ZBJQPDGOI7r)= zz5FhvO6Ybi=xcExfA{uI$Be_x;F{1ni$2Zgo;`JrHLh!Wrof|Lei97@|jy`@Tsz&(X}v zt(}fl3;67<6uX}gZ;8QU!f_BtYyE*NNDEoP7L+6QZ1CgW_x`fWy#wdjz%qWWFH!s_ zhkYqkWm*L83a6f%MNjAjbBf#a)Q6a5)K6>bE`|=vNIUM`5!bu5Ppj)$GG(ZN7|{#5 zToIgg1H1axY?t<@2$%FH^5wrd!dA#NWBVd;A^xfiQ|Rlp{@u77nK_XL8hE1x+U-(V z_5Yy*Uu?TuhPP2)M0U??U^kjhKypX z2%!wCYZQ^|go68Tov<=MRgaj2u2ifoXQ4aAvjrYr{4Y(KGfBr8ObifeIdC*2`|@y)@?DY&TPWi15#73FC-< zlL66&yz{vQuJvg1*lr3FEy9}~!G&b+B80=@yDDuk&>8aMERAklHlD>m4fzbTkFdTZ z1;Yt@n}U;lN5{vO0vd?H1mpeX&m(h(7Tfc*%VXINEEW4Xg&zfehip_k!frV?N$i?% zhUiqCh@2xThl|22f-KuYKBNBSf?2+HQol1`qU)PQt3K_?*$%>ToJ>P=$QQupHEa>z zUW@H8BqqI)dw24=y2YK{@9bDMyvEw|0dlsI0OxwO6OmZ!UYfVk`Pc7*N_8nkZ%tpO zJo{Q$np&;4^5x-h(xU=&`z}L{Jw% zlZ00Ooj!J_ps@IxLAOvLMYpP%DX9;`~Amq1c+CI%N8>3kIT|)bpryCm81vAN z;i+jNbs+wZgtHUd6hScJj`?%esNGIBY1TGPQiRv1;-0|_oTu~s_Vg$02)ywDv&z#O zUlvEdWH_Og>Q`B4`BYzoWSWKG@)Uzmp8Jn@EU}PuPrGWUvWD_)u-H!hAv3@-w3F?n ziV7DBji;VL&t9=4>{l3+y=n5_`PdY2T2(r#=+~-`v_@U;G|VAINvpwo z4sFeD*%L7eL_HDs^I~s00RfgFt0lP9H=t1Pnp+okeP4a{_Z`p0Pr2wHkvzUBvR+l! zGtjluMy`3d=IgEcb^`VbS1=L)BmkPbTGe0>G1Zf@A9~g964LTNk+L0BX3BI2feg1{ z6{2`=`;Nk*x0}yoYHZ$4U39f}QQ5Y_?^56jhjhIQ0*yrF>@Td%Oy6HU&FIa2(E-2U`+%KT9P z5#pAh6&2Ujjbo<1nufqR0uNji+5d?ms~()hP`SnRUn3#@>y(KS)k?+GFywNe)*~v4 z-BLbi{5*YmS!8OY&3+>+cWErEBBS|fU@RdAx8idkr>;1QVFZri35kf|F@!W)1hlp| z>y$!65nMNLU%xiNF@IckHa0?G464iVTO@Jw{mH}YVcNi{}ZEE#w_1&B&qm%8rstCnIT~CF>dY(R|Xe{`nZ>9 zVo?LHGJno=E0}K3NA{OEC5qT>@S^*Z?v*%Mltq<2ti1U-+Bs7$I~I~vBpF3-U>{9W zhugDusY&4-qq3)hJ~# z&ED?K?E2k83V4jUjAT@2Z@OsM!__Nq?8n4qex~^HLB{bCQtWtfvtNDF>?pK5aK!i> zlcS)H&_s!$B5i*f(uh^ykBe~XqQ-I*Eoa+|DYaBft>{tL_v1Ky+&yhJiD%`WZ=a{S zFEp@PK1Nac{{FcYiIO8QMkqr>{>_ZoGyO(8{NN)|$jfca&jtCH(+VJ=AUSNkZQoL{ zGwO{Ml4-0qPar|$NxsI1$4U3hl<~nURN(dpEZ)B9(*?}}aQP>hRnEyGf;kTR7Mi!W zMjvC!GNm4`4#HJVgSzA1J(6Mqm z_0lh6H-el;)9RLw9VaY4)|VlccM=+l^^2(70~&-6&JJg|>g1LTBXGS*kZo~Ea;-Dw z$W)?8hrHq}TZ>@J_|V;+TVKCLAyA^$TI3MbT+oUM^gLxrHR=kS^V3upx+AT8;lBFL zSXSA{NQwK*Yvm{{)zVH5i12rk-d0NOsNxyyxyHTVU%UFkV+LZU@}dmvuBSj_UF`?tlM@t| zpJ7Gv(8|I$t6bUP3E$hDzbTN4CP$2gfg*uN15upJpPwAdltUN8AcW6p*96_8i3O^a zJL{D+9YnLO=I6atKPf$OKI*=$gHuh?*7-rVXC&qPZ^O`&p{9+aF`wYtd9PhSM3*QX<$6F!;eLw(Y+YdLn|j0)KnZ&S|m@?F{ti?ZIu@gsnjWR?K|sz1yCZSwFQuIqE$fR`Ip<4F(~iO( zzN_P>NmXp|51pGc@4ad=UIBfv z6iJ2byyp4x<=nt4u0ALj!-Z%99v<2Lg~fJ+=|XuRi)g$r6Y5$7UFsqDCx!heiaO%v zw}+($o)o!?5el7iwoHgTz1n^%OT_?X<-xW9qA$}^PpFkyb}l*+$#4*K1c+`d-A77q zIEt2FJA63r5G>o&eU``>NyW1(SOKiU1nh}LJV+MLBCej*EI!a87{2{gHeK-2+6VN( z=C4oM3d17`OxKh96yR*mR00+n z?3Ou?%Y#^aA(mg#Dn`Dkp$tV{i#(+`?MjSbau;OeC0GS0-dWtNtrhP(`Xd_wLeG+F+C{6hO+4B^LPN<0ct9N+0Dhj3=9IcpE#XPR6JDRS#9#&G^eE85>)LV$74 zY$iWL36JLIDiv{MFW%#uXQL@T@6DPoy~nIxQ}uA~Jv(~YpMni`ZCxY(>ILbDlcd(Q zts?)6D`^mTM4pAt!$Xg74fI*%!#YPGJ9qmDe5oVq8h7%-*LGQM1m4{nM8T7d-uINj zfZjDM18IU3xpM~jE0@IUbn;ukUFx8-re_UbriP&G%CTyo1!n0bO_!c^O64ekO3wb% zb5RxwBk-@&IgUBMi|}#}jRTqtx)_2-AB~6Cu(am^&yML*wLe3{8mYd};OZqGRii~r ztxv?uJ|QabGTcfrQAqfH6!!$dnODOI{k13la3%#yb6 zYrtkWzd(BR$(2_2A6cZrr%4d_Z8t9}iGR}lJZ?#q4e$0KLI2J{RG_C4bNHxO;HNSX zSZJJ7guBo!(%0Bvv#By3WJ6;>tFd%u^4NCxLZy1HxBHiCE^S9l->>Hc$r8kdkn1E* zuGRtjMC4E*%Xws^>9s-aG@Q61hDTYGIyDfmZv!TVPrb9W52w{=!qrb9Yjj%~C&P55 z?n+qzrs>C=f_0t?VRXq)^89xTbE+W1sS^gQ4*Cdw|0`G4$q8PHK~fA?BsH%8@NK`A zZ0lFhtucyHmvMUsPJTohcvw!ZW?+Ls%Il!W*nLsX)~{JK|8=-Y;_?ExIdC<~_TV6} zrH}4xCokW> ztI2$DQ0z@g>J~}7$M{z&8M>)t;dzx7bldYCPK3+^+tiATneMfw<0ZrZWGq`C`g0T` z1KkF-P^PyYIi8Tx_g+B-?Q8fEzci86d1oIjf1U~X4tyx{DR&bHN8o*_1ZBY8Uzps9 zvhVR0xj9VPNWh1zHPK3LN8rVC`ENDIGeF&RY?==IbX|3H$pg6R@%6Y%X_RD1hYQ2( z9Jz2!U}Y!Q5x1;e2hhHM&CV;!^sqyjV1GJ)0@wFyM#=jZpP?NO!J|k&wO1n0fZamO(0>BK}B_OE_8h&@NsiPr!FM>#kUiWIEgI80mL} zk@*ZAWq4G9hLlQgayrm&0&TmqgB{7v<8trevzD5T+WgR9T1eg(&03*2Qf-`={zYY^ z4bBD(a40kGY@4h}yPnhQ>=62yZ!9_*@Lpm)x={_R0QMHY8({!^YINttw3uC0_!pZl zs4OX+w`6#25a}9>szbIzOmF|;vLT@sLHGzAN{+$e>Ht~$NGgc*(jk=h9pfHgFc8c> zapGOV-1Fr=1M?pezk2~k25E2r}$KmE%0Z}T*huZ-=sxZ2o=QlBZ$ z?7UzIU2o~+w!g^ogh}z+vL-KaMLS2++lbT`1oy+YqaGNWN!?9& z=0?`Ux-gS7L>^wG+G34=<|#+hhU{e;yI5vjyyDlyGdm-H{HJQ$nS!=N?c_NI#456f z@^^kHFl8lM>|;R22nH#VjFEd}18&{7AF?MZQgQ*7D!m}t8Y_{LPE#VoJFUIb&~P)) zMwnkqXi+8@`>=X0#Z~0M-O|7W8(zNvUyZwrNkf4U;K_@kXT--8n}aQJxFXgC{ed3bhm95xbosiVSkuQBkVHSxq0ce;h6LPDHH-2ewag%T$j zJmw=mu024=K}n#cI*u!@afdJ>!Vp}JzsXxkty>WlFaFzp4(_FFtQ!1xGNqt$8-;Rf z{kEMjQtCM{0r6W0hAT7wCzf5Tz&4zIBav|0bICGmv~NG%Ink+m5VDTdX)jUzdO2^D z8CNcUX*RsP(?bq9voiksVYouuPsiF=bYj`C*sy4#+f>74^L(O5W(1V)REzHPkc6C3 zjpvZmuP`r|RZZ4y|9P)CgSd22qAusq3U_6N$t_@U6paYa8Sjvllk-OG_p&*mO-G>Z z-$r_aP&tR4)hPnTnS4oiAwbXl8S=V|QC|7Wx4wIy+h_YZf;5qN+m|+iVg9Kx4Ka2C z<#J`d=NIjmet+yT+vSiEUFqL&MA`$h`S+7GGEQE;bW54PT9=#%>b{vIX?^|JP%|dI zaUy%{!(5p7>0rXW=AQ$pG8M&U)#c3>!^_*n>z;Rd4{je7sHN(I%0N0Sz^jnvm18c1)A=v=o_4UxxgjvVYo%tDK3T=46_scvNxKlVA`}mz`{ug;P>k0cQh8QVz}P@T z@~4qGc^0)@Jcr%mBp&AnqT+=GS_JRZUl&WD*IC|9Bpk|sLX=bt*To$t#st|KM+hF@ z@N7P0DShx$rfBl#!(7ZOERX$W(~;ZMhV1i?%zlIDMyDT@8cxGe?*(37h6-4o9;~;< zw#Nh2{2}PvvL~4j8_~#_g^dTh70zcDH4jc55D#8MI1eU?0$3-Ta5G@v-JP`d#tn|I+>oY&Vw>v8O60}S zwkUGw;~jCx>I9o%)lVh?9mBxm2b}k7*Clt0t2chxtG}A}TZg-RuKCtrS1@v1>~(TU zA}SM+2}AB0{}o~}-Qp~gnSmmnrC}1e*U8&r-m5*&Xg-WozEO-WTo&HPO^R8DMw`xh ztS3g@P#=X6R=Nl+{yFtG` zSJKWrC9a}7|9GhNl*I7gpU7Jw9|tUxIdo0o*aP^@_)g?n(t{BGJdv*1WQL8t5UP0st$SO^_2PsB@cU2JfV~oxnXw|B+^IiSV*l}dM_}AYFwo$;@Q2Md2jJD>{nR$KUTC&3hEu&7^!A0*`QU4(fzJC*-*YGQryPJsnV&KY zt`?W=znSlsKJStcWcb4K!ml^#SKItTF$pjQv)8Sp%fQaz@ zJb*We3JaSrdT-54BJ41sNp<^5fkdYvi_`w!YS0^!F6m`f*gaa%cOlc&m$3JC#@odt z^_EtipWJT=+EFZaJjmq%a7fCX3{SEpIO%-#b`d=OUmgHpQDcXo8Vi1^8Y-l0@O^H( zzwI-jdh^=qs(_Ja$~}`-t^T9*7J=D_1n?}}h1jME@jP<8iu~>kdki#@&vJO9l?k>L zqCQ&U_$iV8uvEi0|KfzaTd0%J=4^{TDBhoJf4VZVka0e;%A5br>BfEU8sk<$aw|1! zn08e%0q5U9WFzo=$h^m=`i-;W9&XyP8ND_k2+$15Zt*Uvs#@{9$Xu?fp}`YFXpW|o zM>~?DRcby?bx`WR!O|bR_wofx3s_ygZiBuc7$jN8&6T>fly$%Yafa}Fs@gu=Z>^S2 zw^~PfG_y&4?gH+a^pkLf$hirzfI{LGvW76P^rI)YbV-aLNgJ=FJ88(*dlP29Lq1yV z*JVs}Nz6{5PXt$*KKUCn6@qaCso!jaQwHh&+c_ySpNjq3bDU6YdISal{MS)*DIttu zGWb6Xw&nHj(^tgTVJL93PdzFaEL0^*-SD~(!w?2a^C9#IzzkFBbc(;@$@CCS6x0h3BVCa>P>#n~=_qV!G0rnSiAz?wnk5+19ZA&hfdfo+rJPl~8(V zW~n!|@|t)yO{(qqtcN?SRQo)(A%8O?j>59L{!TXkqulu)QgY_y`5ES!{MD3(fFt%K z_DaYp&}ea~N2L8}(9xbfrNDvj-g2zo_X}Q}0$>i?Kw8&ztLfQ{J=(vd#tDI#P8AfT z_8-D^6BdTYxc;#>!ozJ08vd7RNRN(2aHao|0!tPa;w+1QWCO*IrCcmw!Jh;mMNhA# zD)om_yBiyx1H1mx7{fjPxuvheV@T>>lw5v(Q{q(XRHCF3T@3f!C|X?REp#Slomp=y zey}-T0erQc^Wx!UH*Ux(d%nlySnvP&u9sKVBpW zfCTh<1VErejHh8EF|A?M(>_BC6pOBVv^|Y!iTW}EE3(zr%V_o)z^yp%bJ$p< zETm(Y$iQ9w0``wt=i)38tWW!rwNzF`vdia#)@rxAlbi*dkACJ7So=eqnMz&LWsy4l zl|uo$eJNNe=ur#M=dh{le_MfIB>h8(|6C1YUBGB}YW35r4GS?1-=hbH&qkS5>l z5L`1oO((NxwtU286#*7nT1TZ|y+MoVt}V@30XDPr!G9y3l7(14IAjuwlYB9zBu!J%kQh+-C$e0k_JTk6S_TFxlzQ;b&6>1;lUYO{$L7ah$c$Z zu9H)mHCK4jcb1$j-nTkMIAt?j^R3A3I}&CX&8+rO%Spd)?4tJpOp{GAvCJQuq}Zyn zZvM$%IIq6}U@@!DY+C7Se^kFE@Urdv1>SgripJq?y}Vk+iJ-AMV{$8elXoOj@C?8b z27!^0ZXH|sV?iV0ENwSE?t5fYFi`g&d_~W$bv}DFx^K z!#fhV`O053H{3%Tjs?7)5S(zDObB5@3G*?D+T_l@>*mpn&lc8HeM8C!xi}SL*|uol zMoHxNCmUezKaIO^97o*7KwYF=)i}itnIzp*t!hdsS*)W5;<|rN4GB4hdsYju!Kr|P zC#79zjdgUlx~$;_}h34X83hkxEWxkeo`hfhO55haiXmhq zUYIX$w-Uj>In=c=$$f8wE zaib^bEBJbhirl*R^J!UaU!hFbe-1+Yt|K!I&I2j>PcHalylguKQlXV>t4V{SRWKy-tXI zI2iW?K84c<95ITUtn)kt>XM(0)oCHMZOux%oS4y{6HJtiU5CmrUb=P#+Qu1vNBX!L zA#r0g9)9g#{E>o_)!T=T^Hzkf!tFGP`qmu{=dXSD#Uji%Y$Ho*$r7~nUexu_;A;kF zUIyndN(Q`H0cVS>n1g2oIN2aFUV59&uRi#CD>7n+EE=Zg>G$+@V%rv9!WdD{adUAB ztBn}D!FkYq_RH=5hmYlP|7N+SuA;46t^^MKHyj1ks{jB1 diff --git a/vendor/gems/graphql/guides/object_cache/query-without-cache.png b/vendor/gems/graphql/guides/object_cache/query-without-cache.png deleted file mode 100644 index 5a070097e3d9b503905b4660dda1cd6efa6b1954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18208 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 diff --git a/vendor/gems/graphql/guides/object_cache/redis.md b/vendor/gems/graphql/guides/object_cache/redis.md deleted file mode 100644 index 9d352ed84a1..00000000000 --- a/vendor/gems/graphql/guides/object_cache/redis.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Object Cache -title: Redis Configuration -desc: Setting up the Redis backend -index: 3 ---- - -`GraphQL::Enterprise::ObjectCache` requires a Redis connection to store cached responses. Unlike `OperationStore` or rate limiters, this Redis instance should be configured to evict keys as needed. - -## Memory Management - -Memory consumption is hard to estimate since it depends on how many queries the cache receives, how many objects those queries reference, how big the response is for those queries, and how long the fingerprints are for each object and query. To manage memory, configure the Redis instance with a `maxmemory` and `maxmemory-policy` directive, for example: - - -``` -maxmemory 1gb -maxmemory-policy allkeys-lfu -``` - -Additionally, consider conditionally skipping the cache to prioritize your most critical GraphQL traffic. - -## Redis Cluster - -`ObjectCache` also supports Redis Cluster. To use, pass `redis_cluster:`: - -```ruby -use GraphQL::Enterprise::ObjectCache, redis_cluster: Redis::Cluster.new(...) -``` - -Under the hood, it uses query fingerprints as [hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/#hash-tags) and each cached result has its own set of object metadata. - -## Connection Pool - -`ObjectCache` also supports [ConnectionPool](https://github.com/mperham/connection_pool). To use it, pass `connection_pool:`: - -```ruby -use GraphQL::Enterprise::ObjectCache, connection_pool: ConnectionPool.new(...) { ... } -``` - -## Data Structure - -Under the hood, `ObjectCache` stores a mapping of queries and objects. Additionally, there are back-references from objects to queries that reference them. In general, like this: - -``` -"query1:result" => '{"data":{...}}' -"query1:objects" => ["obj1:v1", "obj2:v2"] - -"query2:result" => '{"data":{...}}' -"query2:objects" => ["obj2:v2", "obj3:v1"] - -"obj1:v1" => { "fingerprint" => "...", "id" => "...", "type_name" => "..." } -"obj2:v2" => { "fingerprint" => "...", "id" => "...", "type_name" => "..." } -"obj3:v1" => { "fingerprint" => "...", "id" => "...", "type_name" => "..." } - -"obj1:v1:queries" => ["query1"] -"obj2:v2:queries" => ["query1", "query2"] -"obj3:v1:queries" => ["query2"] -``` - -These mappings enable proper clean-up when queries or objects are expired from the cache. Additionally, whenever `ObjectCache` finds incomplete data in storage (for example, a necessary key was evicted), then it invalidates the whole query and re-runs it. diff --git a/vendor/gems/graphql/guides/object_cache/runtime_considerations.md b/vendor/gems/graphql/guides/object_cache/runtime_considerations.md deleted file mode 100644 index 0487e782754..00000000000 --- a/vendor/gems/graphql/guides/object_cache/runtime_considerations.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Object Cache -title: Runtime Considerations -desc: Settings and observability per-query -index: 4 ---- - -With caching configured, here are a few more things to keep in mind while queries are running. - -## Skipping the cache - -You can set `skip_object_cache: true` in your query `context: { ... }` to disable `ObjectCache` for a given query. - -## Manually adding an object to caching - -By default, `ObjectCache` gathers the objects "behind" each GraphQL object in the result, then uses their fingerprints as cache keys. To manually register another object with the cache while a query is running, call `Schema::Object.cacheable_object(...)`, passing the object and `context`. For example: - -```ruby -field :team_member_count, Integer, null: true do - argument :name, String, required: true -end - -def team_member_count(name:) - team = Team.find_by(name: name) - if team - # Register this object so that the cached result - # will be invalidated when the team is updated: - Types::Team.cacheable_object(team, context) - team.members.count - else - nil - end -end -``` - -(When the cache is disabled, `cacheable_object(...)` is a no-op.) - -## Measuring the cache - -While the cache is running, it logs some data in a Hash as `context[:object_cache]`. For example: - -```ruby -result = MySchema.execute(...) -pp result.context[:object_cache] -{ - key: "...", # the cache key used for this query - write: true, # if this query caused an update to the cache - ttl: 15, # the smallest `ttl:` value encountered in this query (used for this query's result) - hit: true, # if this query returned a cached result - public: false, # true or false, whether this query used a public cache key or a private one - messages: ["...", "..."], # status messages about the cache's behavior - objects: Set(...), # application objects encountered during the query - uncacheable: true, # if ObjectCache found a reason that this query couldn't be cached (see `messages: ...` for reason) - reauthorized_cached_objects: true, - # if `.authorized?` was checked for cached objects, see "Disabling Reauthorization" -} -``` - -## Manually refreshing the cache - -If you need to manually clear the cache for a query, pass `context: { refresh_object_cache: true, ... }`. This will cause the `ObjectCache` to remove the already-cached result (if there was one), reassess the query for cache validity, and return a freshly-executed result. - -Usually, this shouldn't be necessary; making sure objects update their {% internal_link "cache fingerprints", "/object_cache/schema_setup.html#object-fingerprint" %} will cause entries to expire when they should be re-executed. See also {% internal_link "Schema fingerprint", "/object_cache/schema_setup.html#schema-fingerprint %} for expiring _all_ results in the cache. diff --git a/vendor/gems/graphql/guides/object_cache/schema_setup.md b/vendor/gems/graphql/guides/object_cache/schema_setup.md deleted file mode 100644 index 9d28e46c1b6..00000000000 --- a/vendor/gems/graphql/guides/object_cache/schema_setup.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -enterprise: true -section: GraphQL Enterprise - Object Cache -title: Schema Setup -desc: Prepare your schema to serve cached responses -index: 1 ---- - -To prepare the schema to serve cached responses, you have to add `GraphQL::Enterprise::ObjectCache` and implement a few hooks. - -## Add the Cache - -In your schema, add `use GraphQL::Enterprise::ObjectCache, redis: ...`: - -```ruby -class MySchema < GraphQL::Schema - use GraphQL::Enterprise::ObjectCache, redis: CACHE_REDIS -end -``` - -See the {% internal_link "Redis guide", "/object_cache/redis" %} or {% internal_link "Memcached guide", "/object_cache/memcached" %} for details about configuring cache storage. - -Additionally, it accepts some options for customizing how introspection is cached, see {% internal_link "Caching Introspection", "/object_cache/caching#caching-introspection" %} - -## Context Fingerprint - -Additionally, you should implement `def self.private_context_fingerprint_for(context)` to return a string identifying the private scope of the given context. This method will be called whenever a query includes a {% internal_link "`public: false` type or field", "/object_cache/caching#public" %}. For example: - -```ruby -class MySchema < GraphQL::Schema - # ... - def self.private_context_fingerprint_for(context) - viewer = context[:viewer] - if viewer.nil? - # This should never happen, but just in case: - raise("Invariant: No viewer in context! Can't create a private context fingerprint" ) - end - - # include permissions in the fingerprint so that if the viewer's permissions change, the cache will be invalidated - permission_fingerprint = viewer.team_memberships.map { |tm| "#{tm.team_id}/#{tm.permission}" }.join(":") - - "user:#{viewer.id}:#{permission_fingerprint}" - end -end -``` - -Whenever queries including `public: false` are cached, the private context fingerprint will be part of the cache key, preventing responses from being shared between different viewers. - -The returned String should reflect any aspects of `context` that, if changed, should invalidate the cache. For example, if a user's permission level or team memberships change, then any previously-cached responses should be ignored. - -## Object Fingerprint - -In order to determine whether cached results should be returned or invalidated, GraphQL needs a way to determine the "version" of each object in the query. It uses `Schema.object_fingerprint_for(object)` to do this. By default, it checks `.cache_key_with_version` (implemented by Rails), then `.to_param`, then it returns `nil`. Returning `nil` tells the cache not to use the cache _at all_. To customize this behavior, you can implement `def self.object_fingerprint_for(object)` in your schema: - -```ruby -class MySchema < GraphQL::Schema - # ... - - # For example, if you defined `.custom_cache_key` and `.uncacheable?` - # on objects in your application: - def self.object_fingerprint_for(object) - if object.respond_to?(:custom_cache_key) - object.custom_cache_key - elsif object.respond_to?(:uncacheable?) && object.uncacheable? - nil # don't cache queries containing this object - else - super - end - end -end -``` - -The returned strings are used as cache keys in the database -- whenever they change, stale data is left to be {% internal_link "cleaned up by Redis", "/object_cache/redis#memory-management" %}. - -## Object Identification - -`ObjectCache` depends on object identification hooks used elsewhere in GraphQL-Ruby: - -- `def self.id_from_object(object, type, context)` which returns a globally-unique String id for `object` -- `def self.object_from_id(id, context)` which returns the application object for the given globally-unique `id` -- `def self.resolve_type(abstract_type, object, context)` which returns a GraphQL object type definition to use for `object` - -After your schema is setup, you can {% internal_link "configure caching on your types and fields", "/object_cache/caching", %}. - -## Schema Fingerprint - - `ObjectCache` will also call `.fingerprint` on your Schema class. You can implement this method to return a new string if you make breaking changes to your schema, for example: - - ```ruby -class MySchema < GraphQL::Schema - def self.fingerprint - "v2" # increment this if there are breaking changes to the schema - end -end -``` - -By returning a new `MySchema.fingerprint`, _all_ previously-cached results will be expired. - -## Disabling Reauthorization - -By default, `ObjectCache` checks `.authorized?` on each object before returning a cached result. However, if all authorization-related considerations are present in the object's cache fingerprint, then you can disable this check in two ways: - -- __per-query__, by passing `context: { reauthorize_cached_objects: false }` -- __globally__, by configuring `use GraphQL::Enterprise::ObjectCache, ... reauthorize_cached_objects: false` diff --git a/vendor/gems/graphql/guides/operation_store/access_control.md b/vendor/gems/graphql/guides/operation_store/access_control.md deleted file mode 100644 index f4a714fea9f..00000000000 --- a/vendor/gems/graphql/guides/operation_store/access_control.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - OperationStore -title: Access Control -desc: Manage authentication & visibility for your OperationStore server. -index: 6 -pro: true ---- - -The `OperationStore` has a built-in mechanism for authenticating incoming `sync` requests. This way, you can be sure that all registered queries came from legitimate sources. - -## Authentication - -When you [add a client]({{ site.base_url }}/operation_store/client_workflow#add-a-client), you also associate a _secret_ with that client. You can use the default or provide your own and you can update a client secret at any time. By updating a secret, old secrets become invalid. - -This secret is used to add an authorization header, generated with HMAC-SHA256. With this header, the server can assert: - -- The request came from an authorized client -- The request was not corrupted in transit - -For more info about HMAC, see [Wikipedia](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) or Ruby's [OpenSSL::HMAC](https://ruby-doc.org/stdlib-2.4.0/libdoc/openssl/rdoc/OpenSSL/HMAC.html) support. - -The Authorization header takes the form: - -```ruby -"GraphQL::Pro #{client_name} #{hmac}" -``` - -{% internal_link "graphql-ruby-client", "/javascript_client/sync" %} adds this header to outgoing requests by using the provided `--client` and `--secret` values. diff --git a/vendor/gems/graphql/guides/operation_store/active_record_backend.md b/vendor/gems/graphql/guides/operation_store/active_record_backend.md deleted file mode 100644 index 8b36454343b..00000000000 --- a/vendor/gems/graphql/guides/operation_store/active_record_backend.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - OperationStore -title: ActiveRecord Backend -desc: Storing persisted queries with ActiveRecord -index: 2 -pro: true ---- - -GraphQL-Pro's `OperationStore` can use ActiveRecord to store persisted queries. After setting up the database, it will read and write using those tables as needed. - -## Database Setup - -To use ActiveRecord, `GraphQL::Pro::OperationStore` requires some database tables. - -### Rails Generator - -With Rails, you can generate the required migration then run it: - -```bash -$ rails generate graphql:operation_store:create -$ rails db:migrate -``` - -(You'll have to run that migration on any staging or production servers, too.) - -Now, `OperationStore` has what it needs to save queries using ActiveRecord! - -### Manual Setup - -You can also create the required migration by manually by generating an empty migration: - -```bash -$ rails generate migration SetupOperationStore -``` - -Then open the migration file and add: - -```ruby -# ... -# implement the change method with: -def change - create_table :graphql_clients, primary_key: :id do |t| - t.column :name, :string, null: false - t.column :secret, :string, null: false - t.timestamps - end - add_index :graphql_clients, :name, unique: true - add_index :graphql_clients, :secret, unique: true - - create_table :graphql_operations, primary_key: :id do |t| - t.column :digest, :string, null: false - t.column :body, :text, null: false - t.column :name, :string, null: false - t.timestamps - end - add_index :graphql_operations, :digest, unique: true - - create_table :graphql_client_operations, primary_key: :id do |t| - t.references :graphql_client, null: false - t.references :graphql_operation, null: false - t.column :alias, :string, null: false - t.column :last_used_at, :datetime - t.column :is_archived, :boolean, default: false - t.timestamps - end - add_index :graphql_client_operations, [:graphql_client_id, :alias], unique: true, name: "graphql_client_operations_pairs" - add_index :graphql_client_operations, :is_archived - - create_table :graphql_index_entries, primary_key: :id do |t| - t.column :name, :string, null: false - end - add_index :graphql_index_entries, :name, unique: true - - create_table :graphql_index_references, primary_key: :id do |t| - t.references :graphql_index_entry, null: false - t.references :graphql_operation, null: false - end - add_index :graphql_index_references, [:graphql_index_entry_id, :graphql_operation_id], unique: true, name: "graphql_index_reference_pairs" -end -``` - -Then run the migration: - -``` -$ bundle exec rake db:migrate -``` - -(You'll have to run that migration on any staging or production servers, too.) - -Now, `OperationStore` has what it needs to save queries using ActiveRecord! - -## Database Update - -GraphQL-Pro 1.15.0 introduced new features for the OperationStore. To enable them, add some columns to your database: - -```ruby -add_column :graphql_client_operations, :is_archived, :boolean, default: false -add_column :graphql_client_operations, :last_used_at, :datetime -``` - -## Updating `last_used_at` - -By default, GraphQL-Pro updates `last_used_at` values in a background thread every 5 seconds. You can customize this by passing a number of seconds to `update_last_used_at_every:` when installing the OperationStore: - -```ruby -use GraphQL::Pro::OperationStore, update_last_used_at_every: 1 # seconds -``` - -To update that column inline each time an operation is accessed, pass `0`. - -**Note:** It is recommended to set this to `0` in test environments, to avoid delayed updates in another thread that can cause intermittent test hangs and failures. For example: - -```ruby -# Update immediately in Test, wait 5 seconds in other environments: -use GraphQL::Pro::OperationStore, update_last_used_at_every: Rails.env.test? ? 0 : 5 -``` diff --git a/vendor/gems/graphql/guides/operation_store/add_a_client.png b/vendor/gems/graphql/guides/operation_store/add_a_client.png deleted file mode 100644 index 02d8c92287474704b5d2449d682d4540a1ebc8ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51797 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 i+TZ{GnQ+@9{K-3#Z$ z{=mmrgHhdU)m$}a)$|7<6y+t*QQo6KK|!HQNs1~%LBT#j#*;`0kl$jh8Ro1QSEUHzlV^XM zl7SGz-zK3(N)r$ey!E%)y-I|^A}lm0L^=SFc_D;nVHnjw*Cvsjz#6iXf1VJ!!zI%p zIe_`S86jrqI$^XscK*RRoUXlQ;ludlCOkk5W;EX>Rs z#)e!M=un;C4Z#k=FyA1u%(hh3bkUTPT z7@3*=(1uLq`|DdCMN6QWji#ujt(l!OA$sG85&krQ zkvhO4%M&^1yr&~mYYQwbC5trF__2;^4C@8`h@;etF%5UvdB4sp<^PKu3eHr>SD znIVL7*W)o{WhLwD8ffEet;>~_-DY;PTv3wjD;z>)l=L1_S%KHHo{aKfzy(0EM*?Qe z{(R3whJzCN4cWi1Apjf<>qx{EpOinGp83MCg36YTe% ztKL&AFIwDnoiRbt*^d3rM@}@p#I~Wyz}5 zn`Qp^a);JHeB}0&x|iI;Y>IMnG$KZ4_b7#4|G5_W|6KwUvZ*PI>J8gf$@!o&lVWqR zDJp7;GC!|OmsJY&*{TPVS2x{K6kcja%1DH6Cmp#O{m3xGO4)%|-p}!WEQ(4P%22=jI+W0Bt?*s^;!wHR~9AX{8*{O z_BNU7i{Eds2-0&&HU7D=P{`qmQ~PS08ZblSahct!`KgtOeGK0uHT!uIsl7N#OpqAk z6|U53DE2AycaoK_l>ED{zv15DK{hwh8}vE)?NP53X4+c%g%j zMV17)d}5Q&=G0JT%krG=gT#_)Nc|qI)lZ~STQx>h~9@Rbz0G?O6p0(Z~)X79J zo>EKHQY6yKn0J#3x=a4^Se1Q+!UfH~92RbyMDhCR$9~kJ<^B0WoABWzJVdWWPSf5#z75;)%9x9UlADY1W5!y=f z-CaTVBMk|@^XJ$qO-jPhxOh*}jt`G&wO#qA+6PuIfaU$_P5rS1lRDQ~CZ6c-kr_-G z2Qb!f^4Dxo#%shC(m#kQ!$N=HE(uj2hb8UbU6gV12AOMJ_VG?1MBP()B?Q))+}J0w zf6Bo)bPq#_1Pxd5Qgy7@PFt?S@xyNfBIZQo)G35Ks{DhfBw4{5$>vC3f0X^M{Kyt1 zWp3!AWCL~FM+KYZ4_|`9UlGP#|_o6Z**?K>ZfiJ}Bj=`?Ro zS2Re@1ci89&j5pQqzbbvw}&$tXD1#dJHts5tn@XP)a(Y&Qw0)ci{InSE)xfkf4=9u z)6iG)8k-6;JFl&>5fN6h;{rC2_}z>hf1(CeFCUNWfVn#IZd z+?Tt_GfR2>+A@USsaLtx<*tK=!JXtuHnH6WncvnxaeY9t5;CXNstca2QqcfETI6?J?j5r_?~Uyc zDMa2dXcv~!t!GO)?T(}X#7L#RfWVemI4x56)Gc2h9 zCE@c?OEcDu*>W|RIqGE<89gtdgbbH?$RyYKO+*CUq8A_YwmDU)pD2yhcyH*}U8i;Lto9-`bA#u> zWv-9qtiG8V@TiW&WIWW3$oXc!Rd+<=Bm0;G@9`ORv2;toZ-^vhWutZE%`>`VX(-QW zG{-X?wcaiMW~5=rN9bnwI@?`%r-0#gLb$+Mo%TagQStkapZYFGiR{+u5$!ncDii&O zLJy(;J{)8sZ!e^#FwxMGIraTi=!wodo||N|c;=9`F^gO zNHW=z_yYaaueLhWi{fUhrW2$cLqj$S!LL_{^Pyq@oTt8l(rq2GLP@f;X)Jgj!xL)7Zf zdiBs>pQBhUyrnt?g&#|j7VZ&M)F;weq#7q*cb;>|BfJ?|4jrEghYes(%@Xigc3k9k z+WCb|0*{L4QrW(QYCT_>px5p;oaMfL$d$Z1nl`#Oo>5d2Svs6JP*Q^@vGIeEtPVbN zBD*tt9a_G5-LoP%B!oMZkkc-+!FoYH5|80Xb1)>C(tjhg&GksC%lnZiNLXp3&aD1q zqkE%vA}z#9t-?BJJh2Fwvc&r^CzB(EPF-32*x@S&$e`D~re^wSe!k9lz=_-Qq_kkP zs!Cf%^oNmFr+K3ZG5+5g-?IVd?Sw0EAtV@(21CB>Moo(NvY+EOw6Xmjb?=r5 zO9C@BiNtp@f3JyYZ#EE#TV$P9&BNN0bw7PX9XtqbTCq#&3qj4u4YuUj%i z6L_E2XW?-@5+FW;cDX5p=o`BMW0pXL$R%Ymji5334efeuWAJ=US@F8cg3<>KiSoXu zlzT!HKiM7ZC5|hawI^I{wU2`CH<%c7nr1UUC}f}Pq==cqQJ@1j zLxn(|=v406t981x?K33`NkyZnBgkfzfz$fTA00!>{GoOyH8v1AMcmGp7zjCSWs?)_ zW7){+uO|^s?+69d zo`EM*gK*MS*uf~>H&;=0E{DqXBbUqNX7m9Geaj@zep>_kDfs3g1;o*q4=Z_A=%S*c zT=P2k;HOk!21}W3HmNLWZ%HW7pS2G#39eN;-fpJf9haKbDIhgG>SWl6p3eqN zceagGJ8tiubiuZDtK_IYn6I_DYSW%h6@*c~5bJvb>IGk4>buM{IBbs=C#s?cwO>)X`1KY?i=w5FZ4C?GR1Z??aFNvLd;NTQTO=tGx%44Ox z;Zz&SXPjPu4Gp#{^3s3;<5}$en*umkDiio<80MYAU@|%Ljjm2wmSiFx7l7E$EwaD2 zC7uXIfc9x13V#7}UP&pz?mi^nm8s()aOTbsH!;Rx`GefIa;z1$I^d=8>Eq{uR0i#l z0%=Q$?sse!L**&-@Xv>3C|l@2U@i)1m2NcI9qCPRB$RNr?~!E+<~w@7S5WiGQoDp0 z)NI1uxP8J_zd(X7rU02vv)$b3m>)BR5IOiUEywk9r}zl$dZiW{ap*>)4t^(wt@T2A zRr|?eZNH1_W26JPFceBe%k!~FUQW9$Z;q3+(5j0H226er~UyPj9PPXH0E|jm9|>yM=gq#Cfk`%#G4A$vcyO-%TnBm zT9d(}0~n&gI6-hJI<)(mYnrHpgmn-^errTr*z0k%hM9d_5c4#%?ryRwQV0`E_$=Ij z0w*R2p_D=PG>B%!cjE>tDk&M>gB1v7{#L11VZ{j$bGSQMdLl3iF4d^XCP4`OeDd}l z3oU@l;^Ncq*eK5IVn1Er60-OlyJGGqF%d+N^Uv~@B`z4_$uR0#DiPpB} zYE;^*<-2>>J-39TF#<4;bE`ES(a-l0V?mtIpoTRVx+t zS8BCuzq;irZHvpfcxq{WNeBBZcll&w^0-RBD=Q!B6GOe>LO1T22 z?#w9YAyG!)X*egV=>~`Ly{=wMFP&R_JJR4wcWtTar2GNwkY+mYQLy)Txun@C!Z%3$ zJSkAHva6zm7!R`A$cC#&+3dxXj#T2{YizcoS1pal_&V5@<4aP|8CyNC_1vOE!uP-p z!s_(xe}a?Kt@X9Lxc=t4f%tS(3O|D^{XP;PaHIH|u|21u)b`oqT2eD;nYDXkMmivl zJ!}sca^+_;j-Yit;ZSzFOr~`Y#=p$=DdN({2j0#a;$m%qR=apLIMh-CA?P0`kyk1LOe zv`EhZO3-x_{ihHRppkF@1Xx(a$_3)+Yj|aJ5F_fNc^6nvF@zB_G%F>RtmM>38@77v{+lCj<;ML-{dIOs+ttjbzj$OAlY zkRz#M-~Nt_V&Ze%N4wc*b+#yqRPHdD1%2sw;_?CB#lV%75{t@ivDNQ&Ta2da(JoJf z5MJ*LSIiF#o2P919V%D0HH}&hc+a1F)$V1))tBC%rkn+*k3?ajG5}KO)l#pIe1&eH zrdth>xO|?Hg)S#j*;g7_-jA+&INy@@KA;s~MNjg&pTl%`J^&SpL{R#g!J>EFb14)tC_qU`Nu|B^cB@>jv3!n7Tevxzv9nALa33vUi$ED!k$>#SrkchO|Yhnzl-h9FhNIS*@K#mi*3-_onkSM)y zXzzGhrhoI`CJftGIGnV@UDUAMJIc6C6YUOoG$#~3%2Cet<-=Mw8%rl8mhM`*8(lOo z?eP!L=4L&qxgR8~`lpAuv*Yc~%uk#D~DcH1kJG2q$^_s$T zDQV00d=>nl*G~tA*6E(>zrV5S3qC6ub+&@XH~{71Wnmgb3%ih{ z+Ne(hrKSEyb5>u9RtPG$K8BYN9$7z@QN7G{1^SgdQ6Xtmw63R~+f|xTjgK?; z@XMkVI1$3|s749Ge-|K-ksb;U`e6^Gi{&ytQ1{F5aLkqINrXV-LpX#HcB{fYsUp1m zHP@nKVp+Bq%~TWFkEpq2mljsWu`J%f<+a)l_jCQznBX_)abeg%V_jsvxQjM| z-ekpipYI(&EBb1R$4aqeHv@($QSS?s)UMLm^2z zN42I#jk=ya2jDi6IHz7EBkgsx3{G18fRT&sDZJB4ps>cb0zWh4zT7Jko|+f*-i zv58tf^GZ<~rC|;Yim$Z#>cfR-)UDDf@%dF5GF|W0Zu)ty?q`w+#8xxk;-~|r6#JAs zNFrJLZmI$~kaoic-`^9$N!O^AYs7V%JMj0S#$CR#UhD%Z4xNPhU+s<_n~1suD%~X1 zncx!OL;x!dxN z<$4c2fsM|or_FvA7K$@-#!eoM~_)T|zqt(1JJ2poJNW!xJMnL9E zw;6yj@lRRQA2@GQ106_%J<_Zv@bn;3q85e6sID)uAP!v;dL(CgEA;Sak#n4BYSSNt zg@YnH8FI8%y(6jW-AnV_ltiO&pf+UtlR&&u#r~@+aGtS0hP0Q(hQ?S7JG$S5#;3*<;Gp*F-xmEkFLR{{rTo zINGv$h&8-F``Lp*E7pkVcW3&wes3A=mkkXr`;>(fm?h>vNzXool;_%lc2AV$yI%|5O;hG@zK*f5?%6HZFM#t z-dVT?T0hDsrx^FusxSa!SUcO4=wy5y$&sIEt7>Ebcf7NnNY%)}?JEOM?f$loN1Qzw zE${aOpD<7+-k(%en0Vzr#q#*_f$xWFl^J?AgWKrDFMQ&>#^@vknMu3|h?onYaX@X6 z^y}O1n1?!NzWbFXj$i-OI<~PPo+VD1JG%4>IwlbbSTABYBSw)sv}>sJT>S96e(<{A z_4tDP`r$xSY70@m&=+WK7cL_)6SgomzHljQU?cf1ee(Sx@#Ba({D8?e&SH32?@pxA z8|}@f)!*|hGb>N|ofl}@iB^4M1q zjcv>cQi?oRE6H^_TqH#4EdkAvzpX=Bp0Xl2@;<kR>3%YKoOi|ABNDXT5k-JIK1A*UdJG5WQIrFQyT*X zMI8l5LUYa`fFGQLSUL%*exN+LBxu9--dcmQS;QqfFPHuHu4}7Cz!`qHuIOXgCXSS%M1R0cL{`^D6vSR&UQQEXHa@S8Jxk*U6B`8wYzD? zVfpdhTMSOrNVfw)v^dob zRK6J1kXJeUj zks#x=@^T*L@+A@Scl=L?&wFs^a!M>f)5^fGRQHW{#(KLAtMw8JSzb1JOP$ZBPkENd z?Dny)I>xCBm3jrFJ)u5)tA}}LCYyOD1(<^K{drD)%J6FAmln7h$CWEd?a>a30Q;fw zrt|jFD-?FiY0X0XA3H?AL(w`-CLEHQFEx6c+ox|K|hb3`8e6VlJoN$9O~;) zi+Wr&dp{SP5L}04t~Y8_KVW4aA98^@$7Rn$4=EFvFhU3;pB^r^UC9Mhi<(iKW>|`- z&UkmGDP>8e1^9g9zp-0e!9tf`7NGZ~vEY?Yw7&(m<`EU}2;Rjh-XG4C&?p|&lG;c> z^SWpI7H;+w#DVhAlYMbuf>~`Tq*jT;N^Y6kJ}-UVfsPL5;sjimD)|5MqNh1Q&Fn$E zhv_oOH8h@w%A|ZEUnN}-w1NdD7GB(Z(%0SyrF%AvlfHphQ{n>jg@+5lNf;H)p0BlQ z-PKi$4Rct%{XHl`|BMx@)=&dePv=;NQ*{Z3tuwqRuG zx@IWo1`B`JXnim5BgkI5p-6wkrH;e&gDah4UZeLx4hJ82>9}sFd8!S<9U~F?kH#7= zbdK=-B{Toi;MDYafbZ3D%SHn}Jm%@=kx?k>4Oz?Efiwe1m-miXYokDtghXC($!jM( zp$<4~JY19Pt2+I&KZQ3yV|!8%JUx(eS4P`yMOV~nx8?~sH3!ckYp&OpAo`k)k4ZFg zQ)L3kKfAwx7VCr2+(QCk2ESjRHl2uCy`B22Ea?|zrZx#26} zu7`#KIypW8sr&}Tlb??al42&=cY)RMKBm{uUlcfb9?#p}h)@b}`bo#W8KA0=?&qrW zmdj6e3_i9{7Z3cnR9~j}5~(bTyG*ax-j*pykXMqyb8&9N9kwef@ay?{mH!lVL2@vb z__pJ`6viG71r>#@Z_^Zar&z?1Ba)XdttBkFrv;W06K5%fUgKzvpg<;#u{c8AxDo}0 z?-o)MO>TAGFULB0P&R5gkDtNV{ejd0QcTyV)FdBGV_KLJ;F7myzzm)g=?zjw4}ybx zZYH5j2AHHXn{49_?pX0Rk-vw6oGQ}dMYceng({teGDfIK?ELkfLa8K|0YBN}u&*eF z9X++%n1Rl(-@IGeNbcJnS{~Y(&<_Rz|8=XH^dP}cS^()c| zlG?el-SFp@PvUoR?2@YsV+$4)m!YDeCE_t?AGr}&gF;=Mo@6^H;`nZ#tYW>aUGSf5 z7F#|yLlWPx?B(hPR=Q2hK%}p@^xJ|K9TM+_GDjksQKE%@LfOt2Eb~VrP)jGzR$Skb>rS@{qgs7lU@cKrHR1PqyPwFnustNXI?X^`ALQb&Np7k z*UnSY3=-tCct*c(^_%8?dq4T@8Y!r^J}74{TdY1wz~^biKMe7}*zJx&_6yrl`|IIj zDz97HxXV;c`t*4arhw{tuC#pK_{AIHLw}0PA8+GErElZSP46nXm$yyV+dX}@2JmlS zmu6{Sj~v@wPAmBn6vs}Ef4#ORY)C;ewFb+K_>79n+KIQ;HiJQz8TK8AE|owbqUv<{ z2Md^QR5B1$!PTOmL8G*C*Tx#8ZBOcA=M!rjV)l&LEiF5H+kmbBn;R)m{2TS$Hv`9N zS!jZUiwaToi|-Bv?o*AEzeu@SGLL;X8NEJGB7ebddJ4w8W7Vv`=&As7C^iQif54uN z`r5eB_`{{WtOnR4(aQ%s%l)DQzakz-!r;E@7!7siUwo%3 zI7}@yHObwg7MW$8u&}g3lw`dzvFP#(q)C@sMROcb``KC19{zd>Zd~L@MSw@x7E3F3 zAeM?Mn#3X-pD=@VLww$OXH80^+v&=BxZabQEP#Ep)92akvu$AV8v|S)zzQ`_Q-ZU! z(%_XEX8ot0pd7t-a3~wol97$pWy$xN5$WxkxZ64(Al2Xnl{3AF7|g-(N))#c)ryaE z2!wJ8Pr{h!x=tnm;5oHxo^ zbAf-p(f#_$%EOa@z^Y?H&A2Ns;9kAPGoR;qk)SpEfsIuogthzSN+!0od;F>44h8dr zGPPZ5u&R<#$w)s@72WT4m8*JOWshd|a>=|sbofyk4x5CSoalH|iGtnV!!vK(?=p2; z@usaND9`%D_~kZ=nYz=Q?(dWRbA`C;TvBo!3_RZgbGB+5!ct3`uXi5fd&ViC;yf~0 zP5+}aPFD3sGBH0#U4f%@kl*4ga|a`#_L+XS;k)vBfocorEB`_a0BLl=M$&ncj25%W z?rHKl2|bzIR1kavPq#qHH!Dn(r-7srDrvb^cj<8tKd?h#HXRd-1rY>$;BlovALkp7 zW234u8@%2oWMKSL64mx?l1et-MA)DtOj!g}%FFDH<%Q;-K4GFTnH=D$)aAT<*+*GE z-Nz0i04hZ!r$SXKi`S3+tS*?nnw~ofKP8hovRwK}OKrlZL3BcgqQvb)eWddn>YB(| zS2J6<%D~$KJjNth>0zeDmh4Pk99k_W(*+V=d!N>Oc8Xk!aFi>@Ycz5Qb3PeP$7ghu$z!t z61Sxi<#xA1Uuzc}Hd4$4b}w)naQr`=f3?5xO>_dGH-1>@XN(QLwo6yHn9CKheI8&e zoE_9E1C=^lv^h&y@FLCq=f4X+|Faa%BnaXpLrM_#)Js2y#(sH;M6Xty!|%3VZbi^t z_McCKsjxODvm6cn$Fv&<|0!k22HSmvrhDQ{ zWs;!z2LS1>kKJ!_^UpO_SKs|RA^JlT3VBr%f}FDdXb>y^&xJx3=u0gmwo%j4%Jz3r z^$%XGr527=OjK02v$lQn;J-2hrVxtj>XvjC{4aMm5h1SyL49riL+?KZe%O%pGEJ{y z{+IPW1VG{X2ny{n{yEiu4|)tB$?8Loe1klR-pVe5jRdJF z;2L-eqqv`M&UmOy-6OvxO!$ve^Ou}@VrZbKyN8(!WF*JwIDIWvMZS?c7@vO7ntfKv z!Sx9}EBP9iiYWd6Zbke$a!-;1wP2^;NQ9uE?%RlnD~RR+f1dmwt%4kyvcI%J^?B-| z=kGoKw`GweyM$~uP4+vl|FUO7<w7AP6F%_<62cPv_oxx-jD9W=7!+KK_1PPIC&I z#l&9-2`NY~Gl?&>*~fwRznt(wbr=D;8c00O;`8Ehz30jk_m)OpvP3@1^!{v3xz1F| zEIXnS7xI?wD+GR!H@f-kB|E~)H12Z?L6*hnGhI_R+Ie(qvFkZ=e<5M z21aU;Oxj3?2gEh*=cGYE6;>hvjX5(DYaQ=cjDs`zz0^5u7ReyMrUVY#WBK#_Vccal$S74Url}AN3*KR433(Pt4O}p#{((*Cc2!`*fI;IcM zn-`j3qyyQ?_F;Nrg{#ZvUVWiI+VS>C&F8#T=9cBuZk|4tiGyR_@?Mgu52WxXYJzZc zff=y)mImH4fxfj;d4wtJ5^UqPtDN~Go9EOj+@*1XM$8YA<9eXGt?N%9(=elbz_?e&=zre;$MVwq&%; zV%=r=wAbJ@Vd<6!2?yNp0RnP1{Zg?Hm94jTDMi1kfZ8Z zug)~W`)WUM~Q}7J%Z2jW_yAYcm`+=OhobsG!4h{$w5o4&!=NYa>yvcT@7$4H+HdJr* zu7cnl^y9p}DUqteXlxe08zCoK&LBj(L;AMkIyE)U`x7Z9gRxQ&L=zQ6<(!VInvY7M z0I$^@!HAe7v-@z9uM(s_ROIC3tiTJy&+lPS(7GP?GM+kJ2c-^8PIDG_4+iMX`cXv+ zRbSVqjeFtB1n=nie>_=iIPv0i+|#Eh`$=h)eUl}jwB;C2F@FWoeGLjSU0REyy2x!O z9>OFvlxw$&QUz}3IQh#pU^s0owIqcB0 z%61n$i2m4-!?>OSlfm7aJH5K|`v_$DvxjdnPKw;KWR5J^7IHGbxN&Qc6xlbf=E@i! zkLMkTcGn%Z&9q*7oyd>Ulf4S#R6X5t;r529Gg9*H)rKZd1*x3dZxWISnY$@9d#pXBi>5plbQw7hAv@CrFN4F&^t` z-p(Ej9;V5e6?@+F37&?oDP|f;Fj2%uh2$zx`YQzzn4tFr1f?m61b|cqoxS|QTeVUe z|K9-+q*rCf?Ys`F$xk$5T>n|Pou~H1!W&2S*b-MnzYM_R``P(O-+2o7Tc^W-`{4++ zW9s?w0bCZ3;>?wL11leXRVOt%ny>W;GO?`GML#Xh5A=u?Nu1M+qx2=62=R%r^Uo{b zaEEB}s=hre^BM7yPQcQl(^4yf!7tYeS`A+kR(+pO6PXMIYD7dVgYQY%M1-2`){Yt# zq&b9UQ6nwZZM5pmW4g}I+s_4PwHrz|vl%qM=ca!l!r8xRUMv9eoeFAH>nayX<0{Ct z#QYuD*Z~5na-vnUt@oEkntUz=C!cS&JhU)3Wq9pvzpv>Ko{WWYe62H0O;zztMHrz) zYR#Xfnul4dzWdHubSF_G zQ}h{jCly&xGJ+(_C8M-8>ekKu_4Jijps2$ohu1YJD9NJ5`hE9$*!2{;kL|vPJ!A8e zYV@RT|5jnl^{zR&k52Y| z&|hLZ!sq7j`?>>A6OMGTXDUiz|ESXAN{AH=>m?C0hY-A~Mkm>7fhc*vaLHFwo*ZH3 z&QDP^%N6$em2v-`Bz~DvFwsT~br_uUn&?yDw*accXDVO5N&W5aKBGh=`I)QPE9XbK z;#V=^^S49>AcL;NYebCU?1XN~MofbkL3Y>6b5dUYySPV{iL+J8R=U&_Wz4mL*!rOD z+2#&V#E!kL1jUE(~Tht5@l%KbMO~2~tgh2=)N*4tQUMLK%$+&|HGx+5%X;At2D!SMamzEV8n> z`S`b@{_CK?z(ViGt7T2|PR#rBjhYxtiCChkMmFYM>NE5tx*%p3;3!K9HJUEhS)1!I z4>3pI8MduqqyvR4u^;o$?nIW(LJ#v4&q4S(>{v219v;Y7~*1pPCPp1)B<(9r_91jnEP4@ED-e75U_ zc;E!sf{#o-V2OeuShM5F73MxL%S0qDy-O$bEKM)L?a^HS2ZrszxCL5aX0mVBB5hda zsTEyq1}K-hIuE8Je6h2y>KToFfSZCSND=kSM?08WHJ|;Anjmqr z7k`u@$5wT&>i6t1tPYT^!$WheY-WN0;y*t7K1+}qG%jQ(tO_OGYo`gr4TW*}=W&Lt(mqZg8jp60AFlnu#NwW9bi_>$7D%@tH0>HRr^=&Oz z=D|%V-InTcYDfP zz6TD44&Fk-w0XJMT?mMmmlnlb1cI!0?Rte6-!ZyDYvm^{9vWllyr&S}CbuI;{g5o7 zy0njb4eK4BNqnnO%*J#|7BY8btj*rh*XXx?oI*_-jeO2Y?l?lQd)RkZ-mC4Zv-mMt zcN}$249eZH_d0j}O!c|HTU<&rW3R4!gN(b@Da#HP^|ur*NyA-DKP<#X+6M-?0Kf(%flO#`5nONfn%2 zcijqWk{JfCk|>|-$y_D;=*W+vOhKE?yBEQhu^c7y_Vet>zbCTNj`}y^Sru-QO5a_Z zf$Hsfmx;b|8*p^9%SxknXuMY-Vi-6-Dvw(t5FkQ-+X`idUoVqR3-CGY^1QXuLxn-H zJzZ{W?6Y8iK@Pzg;Xi}MX$)qK^29I2h8AP_^huTQhjx=qSpl@eh9*2V>p>V2>?*AJ zvh>UzBAI&J62$a6{P^yAN`R|oW?24Zm2DwJIC3zEou%*?Bd+U%k#fsuL zx!GoJu`gQlr9`fjCC%sak^4uz_QN5R5JWXNqLI*SsxxkMih3c(V!rDp+XiHljFEt; zG9d^!Mm_Uh+~OZ(<96Cg5XdcvN-@URB^bnGx2RppLMhmwbLLsEh%1wPPHl@{zpIfL zy$YD*U0%dDtmJC7tkCyS7h@3-wxWvuKBmK;jUEk+6?`Z%-|v|VLiJE2^BwXfo{!9KxqPyFaE<|llzr1&Us+337eBB#d{uK!7C96C3Gcj}A zC?Uc|-NzLFA}>krpGdk4skf;;5hlOd=6FT?a5x!nhWr)TO1jM6rad(n0+}!DJg1b1oh7NqH}F=<_LL5|BvXWm4~q+P*x6lyH|o9k-s>Fk;?v%9 zeIUQd&^V9BdSA?;NdsCd1a+z~sGme+KY@rK4F*D2hm)k%AV z;e44F+CM*kx46pD3?Fd9?{zmjp0FGU25efy@dU80ba?Oye)>6^!2D4t zfHUxzB$N?&uw@x4gw09&@k`wE+k2rXJcb*$V?WoU*{MiAoL>}Tkq2^sc*KD=l@j?U zejLr68%h{x7zKVDHNX`l#l{nz%@vHa`0X3oqk^f>!k#E$|NSM*WFleP^GAqTlD>U@ z=$I$)BH;+Om!;=gz-$Dk%DaFuta_F`+GP8y@0OEA=Oy0fsf*j%~VZEFtznsI&z zl`Y#Nq!%t!EGbc`)>sVG8MIv|F1vYsu)qZN6gMbj=pl%>L>FZH@Sh%wIG{4`(N+E_ z)o5d6%g=(=tSOL4k$ewcheO@tguRi%@+p6lDA@_vc1WLLZtU&|Sj(j@tWgx!bma!a6*QIV>*q{Ctd9)XamFm0z6E#ZK-3e}x< z$o)TmrA~O`@v1j9Hwe{JYbyQ{_UsFElt>fUl&&^1MlC**(!FO4lw1(3OsBE z$1WWu~{}aFxLgF%wa+%6qIgAa#PI%**$=o8Gpp9vZH3{XG;zjDhWPrmxPQAA-L;Qw zutp4=>al4z$R)I0=wH8q}VUI%y{P{D6u!KKWkqUzBj4JJm=>s0bU8uVU271JYha){+^kc5E z&(OkLd|M{{kyqG~1Tk1GV{G!$r0&lbX8d%AHGXHrxD?%=!xnX}Cr&}I$?gOrjr6VF zjNR592>@4ElMX&Q zZ76s+=Tru>HlPo?IJ6fj&bwg4?6I*Uguum>O}_6FvfRprI*7JZOL-`%HlDYz#GQ)5 zYO5b%U;|F0gfM|Y;Gj1VUW4TipRvORi6fmg7hU^d`R3IFrjSuu^iHn$R0&xIp16=>ZE8b?$4*Mkh4L8t#U$n^A6pApLt>wY|4PDaVsH zi>b{=u9%$4cGn9vyPrr<7OFh9^thYPb7vGaYGR!AI|2JpT%Z=lb^rAW~2D;{{@g4N5{mEhVx+>dPO*S!UUI?KTrLK zsHnC^loSIqy-|38Zg@0nMIz{%jTos|9go||0qZ0&3RLlAAelXGAhQ9^mWU(0YPCBJ z@K@+&?@AV#1Ri+}QkvL97w_k6{;Q*eQ5%gLB_mQxShs*g%=2s|AO!#PSN)G=^!uC7 zEc`syc_Rt0@^uwN_zHomp!)KmEPC!{ubLfzUZ17P>Jo6mochXZ(<2N9y#TuKv2w!| z++i8CAdB?Yy@ZS&wr2z}JE2mnufd2Sj?Ob6!>b*IK62@N{kt(S=_vF;eEM-y;`t|Qk z3`PCZPrG6V-TqXrs5a@LgO^37!pJ^tq*SULcBe;TkXS%^ndJ=Y~aqQ~Xfolyub~ z{OCqgY88RT39sz-bikO)*Ih8asZtjJX3 zB;FhRAy-9zR*~rk08Nb~?lT2YU{YhXp4lQeAh;}?;O1L_-GtTh?a$7ua-t>Y%4*>75SaJ!`` zDS_z~!$MJr4wQd4H(xd)QDNS~6(B3}pyY2aHVo}qq?<#m3@NrxkR*B0Hlc};<#p!@ z{#g~fY+dGV2e-~2KPz&g2A>_RooDmA@*$gO_(ie|?fzc7E_jizfAvE?`%?{8Gsl|UxBhPs!)@G9 z&kzwe`^Rlb7rH7W7%>bj^u#_d>xMgLk*+)$H#K^Wl^)uxl;0i?;B*CkHHw_P%qeuu z*k0;iCfVY>R9A4Fl{tj*fn;(O#Rte(p%!3-Z=}oGxU%kA$zI{LjA>+xQTgQ zrbC7(M@IAP@b|Vn08!W-ov1tz_ZKmzS&z8OeU~KRe^cj+MMF^n>@?k=ms@OW_j)AO zgx&{{Wf2pzk1NinEnm`Q!tLtDpLbZt4Ok3}_iFaY zQL)nZX*p9t{e`sP!PF4x>e^6gn`BW(j~N%uT7ivwg8A8q_8EiuHBusojR$mk7Lbl6 zZf(C_f}rJ36xF?{FIOHvVGB?d^H?sxi$67T1*0KgVN{R7kEg@X{mjs zMywn)elB%YIxnkg#5lZ4CzG+j-#Fr{#FjzfhZ7CWE_Bua===WMQI6%+kSqpmbLjs5 z3YMo>@C)EDRrg#qG!NWF*uwg>z2P1jkfkbe_W~@}on2KYxC3aRR&ZKr){iBr+*Ri# zDUnaC+d~7QtTjRlT+Zq!uplE^XWOg?Icpba-Y?|)z`lRYKY_!v&7uITF6J2k`}ZEx zvPg)O9%|-3|8H(M-;MsN&(x(U^{?JvTtH%789niZ~M&u{u z*K2JPpVvO0JD@{EQ}SE%xA6D})((5K-)iS-s_YTq9zVYV_dzSYodFtSphQriOG8&Y z?b?9DP(Tjp!l#T7*J6u5!RGb`IKc%(!-e8MUANmR|-jdM-@Gy=TVmRMhEVr9wpK2pw0gsonOf5k9jU zCL7km*+$<4b+wnlQaw$M8@Y%Y;1t_}#MSerr4M^w_Z|rru(!4j!5NDo8CZ7uzk_?& zW;$GIs{AP19cQ3+rL;jZ6T86^WkwP)V-zb`g8Xi7i+v2J>^wX?RS2v<)?ke+oISN6 z{G5II8ix6^S}ovOv5ue&Eos6Qb^dZD6E-cS=B2fkN-B6!qxi`&b{IDKHMYPLElP9$ z+KfWAKk!zbIcD$fUG|9U7W7LV{p z&oG_Lhj)h;6>(&=7X=8;>^0_vB)_C(xK2oY$+AJ5sXKY z``Vq`zotNPu$a&0vy@|op_dAi^3|aKaJi-S%Ys}4>QWCH6h)pO z4M{w&;)&c9TFFwMw{`!rJ$}iT8dibcI>;^ZC=9ff*D!(a66)P${-;d1@8g;MPKoR| zC19@td(cBZHL?YS6#>9$TFcK^hApZ=`=+(u@reJ-1PsXhe%*Jk@G@Ct;m8epVHYEb z9w7Ouq#(QLePn#`Kq0`CD%_?OE8 z(NW}9h0wvwt&ti+&z7gl!Ycf$pEVA#AMcZxq4siTEA{KtFv@RtJS|7t$d(}dU51Zq zc`eiA;krHBp!fG450yF;PtbST&Q}#oLLC@C*`Xw>zJFKoLLf5wX}uPFkmXfr2(|W5 zQ7f>+3ZHi4Ce8#!yU!!9uBjwXj6FIF_C?|7`hP^2bMrUe|ND;+_NK%a1}yINK*%kR%b2BQ4i+ zEWFEXJ8HFh9neaUrhJT;TLEcMS~uswv{EPp1g6k=ojzAtB0w3-R;xSiikK)K=tbb8 zBniIwzCD^wiUYn9@oW^S5lHgP6~kj_gcJ$h%~O#hH>Vu0|sFu@-oia%5# z4)F+Sx7$vWH))cbrgky2>(>>)uBI^IUvZGP+9&HFj>bXYTl|uz$bDez&d==)gga0%TtO!HW_)na6PI|^uI*6u zJjSD|B`kjMQte>da^idxhVd2SYs@Mrp;uRexDyDt+;jdT!EwCO@uF7tvk`z|S4~T= z=U40GD-&;ErTJbyFq~0l5YG4C$c@~bV0tM$N=cI_Y!z>R)M@RU#IU`+T8@2ZL?#JR zDoCQX-Fo07PI?qO1rMy(X^^XtF(D=?$(rri`rj_AE%{&bBpf6UE_C9<9q0HQzSJL$ z*)Kh=ceL2+UfVaJNTAvNuY=W^5B-P3SUNbw?29yGFIaaZ9!AFbuTav$2SL7aEnRZY z!KZx0tK|LVF*Tgp(H~>-&B#HR$cRWRs!v7d?M0M% z{Lg0pi)CW}=uePRm|I_864}sY!TVqlOU8$Tg zZ~mQ*0l*R|EP#tDjfnM!`%g#}ohT4THfp#1KXOd|*)Tmox+!3hyhHs@sGD*K0AnR> z3@iPw@k@~|LI+%i4u>~oCJ3UyN$Eq|$*nJ)sR<}t?H$s7`ycwx|Jj$gZ%}V&8TPf0 zDzikc)Qtuq=`)&NLabGv`UoaEy8Z0@;D7iQFv_@}2&_&ZXI~+K|8cwi0|sFN3}1Nf zz~Fy6WIx|n(bZhk*ngRf{>72`L4N=sKA!@+dbWRNH2-4CifjN*QRQ~8ukb${Y=1ET zRLrH-`Sx#+CoqQcOyGF^dA>jU`@av^8)Bve{oT!~!{^z2>^6 z@XV70)5v}k`$tCm*Zx@q0{@}$ocEmepGC&McB`8P7`S(Dzr?>j{?89_AKy+iK`usY z!awzue~r?M8~`Q%KmVoQe8r>17N=X%Kb!l%7tp?c_x)nz&XK|v)yeigr!Sx8#coR0 zt#j&8HU8tZ4uEE2al1x>#N5(qm2xW87c=MIOW@F?6~ zq-yT?CSRT(i-E|gi>}w_sU7C7hXaOm0r&4?);gb|Lb?~~jdp-wr;oO_3szk|&&B{8 zMm#jc#qWHenrgi}nax1rbC*A6P4~m${$l51l%dv*=z~P#T8AgUV5BDhL^_A$n(wRk zAw4s|7wb^OF^a#Ar*nL_-;zid^7eqrJM;R=9}KO`0jX2SVLJZn%MOr(MaiT3)U6@N zb$4zBq|gMo==}F%#V*?qsIMI|Wv3p2e@tp`fqOD@KM;HC52f=umaLrvW+0stoGqQ7 zP9wAV4nzn%@Bm^s$1*h##(M7JyN*ZXTLXPt(`H?>MB|)Ydpq%FTnBdUJ78rl=nfC%dJ061k?s8VmSY<@g-uxfn?0G#{u7_MD$QviV8h~1Sv={I-zO-# z&!i_uygw?YK{_KlX^0N@z2fSb)8jvl)mKj!>I=WEwQGhpl0|{DNm1bp9U9E0<|O<9 z2mDwcDu@xN>&)n);ok!6o3w^T+SB7{mxnHZ?=?b&5O_Y_oi8P*x4BpobsDKP8|**s zX4*o7x_f$h9>ySyfNAsU>V};lf;p6%pVdrBG^g#FjzQg0gIR16z1k=fk&fBfUjQ9d zYSC=5$$kRlWPtO-9!X)!bo%>Ctn8mqd*SQ#<)~`FNX-gf?#Cniirii*bKT_ueG~hyK)J*-Boi#q3!~U99l*A zb*JvzqrzArdhtkXF1h|xd%|cYL?FFw-UuK`RRJuC{_KR?{FiVGkJI0{_RC5B4|sUx zxuCjgfP}r+PE^p6SSLrglZTk!cLjUXoPFK8ha6?9F%cjT6%bT;vzV7Q<{^Jt1P%~= z3)rU(h=iKEeP{Q3dW9dLd1t41TUHz*Nd-w2as*=L+-zPir$9UFfOoC=o8~j2YOS8& zkjwTjielH_1>ymZO@@6T4|?{Tb`5^C@Ikw%BtkkdBp~L#5abynvylXGf={rh#8tQ9 zkHjbhSsdgn0MN1$H9Wf?+&$Ymb^&Hu-Tw54&7#XG0vcyHwMZp%uCDOU@8v{JH~POx-(r0X4Dtk?uVya5zON%8$OJ1n(_!co(DyvCf;VX|Q1mJ# zQR}eLF)?%AXAvY8w1>4FHyLXop?+pL?h%cY@ovgUXk&_E(^}mwpPCfndBu@cq0a%`k{nFe6KDxV@ zpx+=O_U_dI}dtDBA@Br(@mgYNYsQ!8Tai8j1spC&R zLP&_L7n}TRMO2%Qgzr&v-V{~}RDm-;r-MHuOW9+T>l~1n$)5YgsZKvha~y%Hnwp4V z$GpDp>c;Brm8?>ShqE_w@4nr#b<4g9&rkw`ZO3&AG1TBY0kf?aiDzUA;K3giyLSap zc(VB1HdV1iU`ycm6OKpaVC9ZX7u1x&F43pE7%w-7iQ9j~}vn|+it)>c}XW-s7>Gp&4z+1~B7WCk$(bvgz zBjud0u<3t&dxw(}p`Y!vVJUfp!M6i+)^)mih3e-|qN3Nf?IMs0dcblh+Xwvn1mJ7A zGO8%5(eu0q#6iKdY4gu4s($9Q5OnGn+;AyD8|{vtkxO#6EZhc~hf4a{H38NBMN6lv@=CscY_EIy8bl}l`& z8h9o>UXD?vC)ufX?kcW7%(dsMy6+<_-c6Q%znw=&;I*nt>g+02#phIHHwpKh8+zbk zL5`37O;effG7hKCxU3a3{)#h4Yl2zui_hO?}@3*GeXa9~QDc{99O@=FXMJl;Ev76-n~M zxi}01Z&l1(xzpJi?fu_xNml?(!!!6P^X$xyh(0v=?z=yFcLmSJ7>YiW=k1B})fNk!K?&1RtAwXZ` zGC0x2dKLfcV(36WyKci;6Y|2*f*D2d*@_0Jdy1Y@eoWuPcO7gG1XLv6=Yo-YP+lye zUU-t+jn8nlN6MtrGPtNPkg8Cym)T%+3tEy14Xlc!$s+K%-xeY;Rl z)#I?5?>qKyrq&kHumfi*)+e&rffujXX4V4t4`Q37?{Od5rYxiq0U)J*^C0Jzlendr z1S+(2BA$`gQK}~;no1aN@Zw7)B^WaSNyTaNbW0Z39bKm2%t`lPWbi9zQ6-zP-jzfOp#&_(R=*X%jO;3l1> zdU_PMzmiuZ7iq^|3*LF*4Xtt0(@RT|kVDyT_J`k0ELEDzkxBjXV~`?^ht6v+ScA4y z>7n!c>S`AzvcZ+0JcW5yvxVYbYM%iC}JLA@1Z(rF20BU$cO|O6K)o6UV zi6LT1&QaUAo1boVEF<@jSl^h^&5v?t2wrZoV>BZ3&AuyRV%)3L7DY?3P5rvcHw++~ zqxCyh$z%rM7whv0D?4N6;vi~rX==3y1vc(yX2S3}ve(_`2@g3XaS|H;b`M&G)_cH2 zRX&Bdaa}h|b~+ri(Dy!!mrbcyfHxq^%nP9a4Zl+{Dvvc!@I&{vEvrFlh*P8jVOHIQ z^y?n9o>)Xo$ES6VO4YV+i|o2j1Nh%VLO)U9B9d9PN}yN7dT*c_7PZop{UuQLq5|#+ zROkO)0SvuDZf1tkEg9?Hd74cJ!K2@M)DLb&P4pgqu;mv*@+<7Ke zVEUeZi=rQI6be`#R2}6j_TWZ&c^#exYKE|fnBjBGpyk-dO`*Q4puzAL(H|Exd^>YNPYT(5Y9Ipb8ToM@H}MStTIH-#N5@Ke8 zasAcMU@m3dI2m2$?0g#ofnER-o@*RS={|MXo00ETyw~#~16VMh{)$=@#`$GKl$iRn zpIW-WHx8h#uyj2Y_(s|3W>_IFSIrX_sK>Rr0M3W6i64nclQ7Ya@Ko7b|)68_q$eU1cSVg^{}9@?-=V=x7tPBKq*#GpQ%cc1c4XOp_w_(9n$ zktU#G^{`DfBNPugzU$1DLub(+{2i$Oh&lvlyyfIJt05)=%cS3MCE))+eKIC5_<`mS z?Y(ge7~)-^T1^cb9d@MrzDDhR5G%+V?L>E*LobOrbJfqKTmRO_!CGLBPULqu&5U~p zEH9^3Rt#UqJk&iNXo8 z&1i}bCG~SR8qbQi-wL|BAk|!z!hzMwkIRJ5;`s&<9Z4ybqvu4P7md)N+xC^)uRvV_ z6Awf#?s9C^b8qZ^h2dG$dakRsCVRwlvgBF0zF$K!p9t*A-9_L)Z`=skcqcIU{bUGC z2RHZ*gd{XuD7XM_ngEt9w`J6pL_my#Fv9NrBluZbNTpk3*V?ypr4)BatAA?tyiY@t z%s*3{R3?VN^*JA&^x{q>v&b#R$C6)!_&?djjVS>clw-Pd!V!4A{7WnT?;n7;7Cy#;%A5 zH7mAjpjPen+1p`-7O=nDK-}3&@f`+N4?BP zCxf5r2l|=Ou#_;xr-kw9_R;oFPTEhsa>p1wZ%%m4Sk7NMSf}Hqb~ zVO)3~36RlMqE+#K-VhHoIoUey|8KVuLksP4y2g~rZ7alh>cn#>#E9&Y%m?iSPdIt* z$#Zzq5y0;Z9J`ly@W#)mjT?Cxb!I~-BT2js#*-;by4OdmKU7?-m&kb|0J!eQtHo{- z=3=L1^fs-Q2Ke503H$c!l@N*{G&&{vbv#8@vq7r3IW^LF6tg_q3sJ_Gh*2AdDcLJ| zRAz)j+iger<|H}$kXw3A;0L8l458jn!Nn%~2IH~Zc|GFzAzgxx;l`7cix&gFQ%S3y z#`7sm2_3>O?*>V$NIp(xQB+DkTklp(D8GzGe%-C?0D3RvSo0@rl5pv_hp6P(qs_g( zus5=yOT4s)K0F!Zb~yG)jHGba8jo%ogF}DXewe)l1k+JKIN1S&K!v1ZzmwouYrL!g z7)0tS$LELZH`C-u0+q~)m(szKRc9tLkglHp3C}zm{WiMK_8Z%!o@oQ@7?7Oh2Ak?3 z3?CE?epf+Tq^Pl~G{~Vw!~`A$Y`&BORRBjbNfcncstu57dZ5||@t2Gfk;4G1Utzsq(=3_Fs*56{6 zPUP_7=blJs>G~+BKn;5ISaCIM=a~HX!cSw9BWiEdcja3=WzS@Mw0b+K zg8M2B=}$T*(0ERhT;FXrRcnvdblY}PE9A(%4_PB-Cb)+$`*vY-hFtkxmvh@6&~@TK zN0Q5TC4DFH&42eG^#ob{DQep!4{6vmJDS&z6uRQ|k`)s4E4Y&ND>&nQuBYOHK{rh; z*TZd5t-Ch+nkAiAH7?(Gb1&Un(ga>2VZgU@LRY1Wmj9kiy;vpqp$LNC(Zb$xK#QJUI;#aEWYq+%~zpzCDBcF?9URod^a73o+)AI zvvIc&W&TFc($&nUv$?|`mq>=wIP2475~}N?{kNgJ|3hzjIZH>Sw*>f_QCESs#d`65 z<^a)5csH={WW2xJLw$Y*gmt#29u2Rj^C1Ty)?ldU_HbSSh@qwedTrcHYgPr%0wtN3 z`{A)~@&V2)+Rd`e9aqY9I<0kto=5dsps5=SlN`?z-VRCPG{>Aa zppx`fY%2P(rx2EU3dAob@i`wz0hf8P+xg~nnGyvauR~FndB!E6u?kM`&z32P+Wg!v zNelJ_G6sqP-QFJD03-1o%L*Vd^jJ>ogib(uH5@Ouqyil|Jnr3V0VR8|4JGGI z1a}yrN`+43*yfUbL7>SZeM_a7Cb=K>ZJZIYxN@xllkPC{>stzVg>JTbj(ySObxIn4 zEh&x4a>~tG+GDV__h|K7vwGn*Tlz6p>9eY)j850UjbLDm#> zDag9eJpkRZ?=>Z?n8}EU5|5?F6o(CaN>tj?#t;?CpVdtXFjov+XM38gW2gla#ez*U2NXRQ}E4f zJkUbqXmu+Yk>5YO^my!*u%-o_78-#J0?9ocQOmO>f{Rx~D|Y`>cR6uGeT|tn5<8yW zwK>^n;%SSf@$}tP>-Y*w)rd9@OES9pYVjvrM#DVg>jX3VQ?T*$OzkMbX$NJoRxZ8g z?NNL%_?g`3)Z^1dntP#C;e&CaQj1X4kcx*F{XKnZYVq@HQuwi6ikBMwDBA@7q2MDN zti#u~)S?>>)TN>{qdJmZmwUYn^=e8Q@AH&YRKy67Rx|o;?O?7;Qq^F_*AbSkkvqwp zRKj3Hk2~>+`MU=0p!O78jy~o#wNZWhd^7X_VVZ$ zMMUf~n$pKCv(hvdttD3L62ez`I69B%^N%CLo$UKrwiqw&w;bsfK60I2>JeOmB6v^s zw;-dzyNPK_4Hh#l6MyNU^UlYU#LAkV^d->v-750D;y~pOJMrbMU4_f#YYPyXqUQnR zrEI?$`>i-PQVjlrp3X#h@3#}J6K-gb+;L$JJ6|3Sxt4(i61iH1YCI6&F#1-RQ>*-v ztfC;OcG_`1NYGmgXt2|O)!F3iwEa@vuwKw+g=bbPkxg26UdJ(-6b<*|w=~rn9nCSY z+g9|FX=Axknbmv^TckUu2IJrI%=)Y-z#GZfvg(p)T8R1tz2&fztX{F)e?D~jD)Hpb z6Ps##cedfshs;ge2T9By-3v|K31pJIMW@hdH-D?6?+6(&^3vFV+5yJOvOiayndLai z3)Po2uQB%)kun%d!g95hZkB_mf}=H;s^{@}xpPc|zywnU3b=)R%tAMp&rsnzO{~xx zEfVWHWrRm|(FcjZxLqurcb~fM-{MmC{D5QiIi6g6`cKXpd)LcD<(V}NM#Om@m(?8n zpR{qou{|oRYEe76q>}HdM_dqHRr#Dc+ytrAm9TU7=Ni@yTt^XSQR@YN*vC*B# z{Qb%bEGiA|@agYtp1}7fZKnYvlB?IiO)kQq{o<9dw=n&{pja58`r}xu{B=pC;vR9< z?qucz)hGO8aE~GoBG~Z}t3q3bUT0ZC{^dqg(;^Lc>nT(D@J3+8Czu`S8{FllUfrXw z_2}#Wbx+{yU5ew?{zB*cWvr;=XZ}<}INX_z_Enf*hynW)e5J;jdfTH!`TcNqer?PT z7Fp?4l-SR6?e3p0<8bFvE!aaFdjK=&kZv-1`SqRPi}9(~{GeP2UDaP*#{flA_3{sE zEe2gGUGDCqIe%=KY>O}0Oj1z^she`Wr6O_`gyotYQ<>?I`Gg8T#UgXuScx0G57zmn z|3fQ8$EmO?lT%LX=9QWk|- zT#IB*Vx_UY$ywR$Eq1EZTEviaVlAI|O==XpJH4K7I^puJ&apOYrPm>sa>35Xcguc_ z>d1R!IhSghlXxR}UZq^^{XvFkgKZwo8*MG*?mJ07sh+Kh&%svH(sO+ehNp;eHa{H(k0ga^oFz>O6!$6`2#JO#adv-%+8MCPV z@=WpGt%x_8r6P-dgaeBv*plmE+^yk0-dCzcxD&f+LY?yJdogfD$Kk7Eb?OIkAcMW+ zhj5+`XrJK?b|YYyUo(M}q9kTrzL3wQrr3L|1!+TlXFc#e+iz?{2B*cQAmODe_IGoi z=j%bEi$LeX9ReW9!Lt3=hZ*cf3-iHWNTQxmq=$(LLQ1e#M-5Y7OdGVCEGyVD1U;MC z3%4Vcaq#VMpMdULGC1?@7u!mPu%|#gOfEvni4uGy)MIB_PC>oVMuZD9UJp_r&G@A_ zjxJ^*(Xmi3Vq+d)o{s#n*Cmw{>j}KG=3uzTQMJ4q&sg_0&?(6|Yi~Nxe(C^SYDQZP zc-h>}*3$}rBCXaq7Y+a#Bz;b#F8=&lOVesA`X^#r(TkwD*3j$i#pVvo37n4>uj-7F z+@X(I*hJB^h=9~eZ(z!`_Iqu>pAFYpz4T;7V;Tw}27H`M}nI zU*YOUrFw152vMW*<}MHggPAy_OXv+x$XQ4Ns~L}>+@!ml!hCDfV2AJZa(!Vm@-x8w zwTr|l+|aKMwp#!{QE5DwO>TgDR>3@zwPzqJg|DRZP988mlGFTAIbL|MD320~bZU%B zaTChmIoQ27M-DX2VWkjAb?wd~aMT>@uV$NrGTdF2d3YjA>(u&wDoiABcnY^XhLRhC z-LUCzrc-0xt%`dKUrDzWWWG2TG+Hol?JANzx|cC$3u zW}Vaa>r1{Qc1P0Q>Tg`;V{X>nmv|S*l=BA_yt~k)vGdbT+S$`Cs*_3ui`S5CW)*4r zRXcI>95sr;vLF0u)ALcNU5`=)AMY6Yy&+(a?pksc3h#BURD6$Fa8)jTxw`1sa*v~o zYYP+z`etQ}I>of^GBZVD<869&vw#))d;G|j8oWb6nsf4LmW{iv73G{@m{u+7=z6$5 zAIn#pgivzno)zfFoi>{~G{OR@R z8w<_>!)Ia8@^O+n?8tl_#_w;ED^+n78}0kjoPtcQ*zS1w zIN`g6`!pV(SAWMmzdW8+sO{33$2?x`7b3lZ0K}IDm5Zn|B?HDQuIN<+)fzxxhEWil zvAB#W&M1%>tZo>f-z`i$+S4wd#Sau15-GAlHw#8@t;|fq zfLp*OEF2qA&i$7Mgv7gQ5~LoASb-0>iz64&#DYr=L3PRNrLhvPr(H(#);B%{6ai4s zELYi74{*q@zw+xH;m2P!&9Y4zO#T)=epGHd74AX~GSi4%agcd(&LARH+C_irLKnK2 z(56?+^UH|fhz(Nm7$vPL>@36IuiJ~C`)(rB(PYpA#exWi2E&4Ds2I)bl=+l9aUS(f z(uANBUWcUgy)!(wKn+)6i>zXPtZBdTQCFFrmq3h}rW48G3Y}kZ7gm*z*D|?KBX!f} zJXl&(n6ON%M48(~CewKo9a3mZ#*gzHYm*D23CdhhLM)cXvZF$LLexXQB(0<3=UB@q z2czE2)!?N*Ol6YK=zD_idSjA8Yz0eYp-LLQ*g%KzIu=zcESMq0Lb_9r3(Q5Ft!?LSN`LtBy|nOz(xM%2i;&fv8sbO#S0L@>4zfyiHW z+hA44d51v(g5%@i!_(vn*ENG6C~@dYLihRVBk&XGU5+Cx9N8d~mo z$T8EFn}GH!PG>ZpdtRvUd-5kY`6XP3_wKzyjWB%&f`jYBgfJ)u5sIe2r=Ek|5jecz zsn4VGL5r5{TP2#8SNz`*G`P@)Ao0D&`T`K;_Z{WuNi1G`${d!R2iZxRUa<)hy>=_< zU3E%(BpT9fb9@+y$N%hMc2-V(hZt>lY zA75lYx$h=B%z?W+AmI(C?a4?LI!9xzzCl7mO?Y}+1d)~Z>ic#?%C-jr?T!&XX@u3s zEJIv!GQ&hQ@#;Hq_pW;GG=e(SArZdd_tpNSxb_XN=c(VCV0jYrgV@0KGnie_mnJj9 zQ>t*5In`b@-PJ0K!?R^GmMxHJwmf;dG7{!JL&P;6H#y(C!JsY$>=^YAE{P~A+$N(* zzq-{^@JKk#!z$cVTVNJJ}NRi|!k7_XF%9=_Y91mc)cuBoCXi zDjN6H{4U?1Lupho=05Ti_Y>(L;V(b{ddFU#DKZ)VYyV=*EmS39 zT{Q92a+}A*#ULX;U`Q=>b3#GdJB2MUa6}T`;Iz-6zE#YKSfSABuvsz<4>ukI9%8%{*LGO-N7f;0LBD#>OUS= z%vf;?Pw?1iX~L|Y&^Q#+dR9%K zL}I#-d2cjr^4voUb}3jc6Plnz;=UU$bt#{WB2J4fN_HE%(veB@kPI?GMYsFD1d6b% z6(G#MH-hYWXG3Fw>&@}9m-dR0Y}=`Ic@f%OMkUWLz9mbl6MTV(%rAo9!lGZkWeCi$ z%4LFYyZQwv)b~HoQ%*ze#zq_ZYa>6y8`dY)I+)e>k_3%kSuNo9T=&_!7h<)7;DR7J z%hJLN>zf+;;Ez~okTcvu*>aP5;IMneZX+0!t4n`LZ6!*t3;F(VbGCx%1hEc-p05%! zekI6~3Z>ck>VD)OH!aAs?fH2|fO@I4ryT-ROT(ni$xd6hP$pt3-^mIJO#%q84+%6Q zw9&YG^#F9n1He9x0zsVw4{3F-loU~DkXvDy5QQWxBsP#}{-Xh6pi-K3Qs<4CSOy3- z!n5cbj5)Y=#0>8sR@UdV2O5Xvv(#6X+%{Vy-_xSoVbrKzqUidNmAYJn= z!>bvN*tDp*c z8`Ob$n2A0z0M(2UgyQu&j@L@Ro*9c6pH1*h&sE?j8jraIiCip5`QurS-JGXymoZYq z<`!6P)?Cvqvc-}03aR2*DzTt#LqbB3Frg<-?@N!D^pJrIONVdaf#1R8;!+Fii9i<;`nVJ7L z3jnk>GPvCq;;z35N7`N|^6TOYW385#&Ifv(>;vs_7qfGCM5xABA9^24m5vpIDtbnv zse+Xp$3&(&R^K%Nv(DWK_2PhTzk+;5>oq)U%U?xm9K>Y4zjk}vh|GQExiV~6t6UTq zZ+zSg*6z@Ukr1&5IkuK82?`tirM5Mq4W#Arnyx;8Wr8c0rS=^aZX*g-NgRia9!^p$ zE^QvM3kqI)M6#P3knK4`B?TkT<2zNd&~=NGA<_%5H* z6;>pXQR6D=X~ll8@doIcK3zzhB083KlPa8t&kZC(x18(HlvlAh&74e*J?WN;dmVN;X&37IHz0=uk2!XHETC+NZ@df1V*CaeXupgxh3aciD$#3u%h*aNvUtmzWIE z{Ym{a;CUlU-*eYjbOWm;{@jeT_qR(`z%nO0289?Bd+V+UuIZk2&%8imTY`-IY)dBQ zW{f=*I?WPjqRQ;{{JW&^PjoF4`rU840izF3$I_g{)7q(BYqWcJw-`Za*8CYVYsssK zc8QhFKhuQF!K}XAaam6 z29j6YQlmwoBlIWAAgERZQZ!Qd^1?zK|0=l01F=;}M}!T6`(-*D%YM@?5cBU6w$G>( zvz@pSBqTnvCTs(eg)oUh!G2V@l!xyPW_Xr@vp`I1yw}0$#R*-Lm$Ac~4YMEEAV{&x z(ufO_d!O(=)veRu##}K@YZB&OJP!@3w-r9re&-noZGG@=srXR?i+s5Bxuj$I^K({@ ze@6p?ojI4z%UJ!_R_y(+Dc8+U_hL?hMeXJFW{c1OCLO&8#fs}AoI)@sXyJ}l~e zr)#9!m)3~M*tI74 zk$h85UxTvw7r{ln5-oD*UBq289jO4ZI+>uNR=hieN~GrYLZnMU8x)^S-*Ek5VFdk#B_a}1NZ?`gNQsH1;5a4sjv!}_ZG75Zg+hz5W6kevZCVr9ylL{m#s&(l^z zEP@Z_S3}t|dQg5oMfBep9|@4=s@T*JCJAhh#8j!?QZISN09ytdXe0CRcCqPpb9NwL z)e_ZO&r{5b*jB{$@-CM3(&bTvHIS@3H{H*4UAkMy2d(pm`NbLAu6NZd^QpLJs{}?) zmZt}0$Qqub@Me!JLdYMAsb?E#Ex%?|^zEnMw0ip`P%?y(fZfe?gTYH* zX^C&XE$|^=9n-i%!y@6vm5Fj3gkh$Ehc+Cq{vqf-%@G@y`y;43wZH9b9-}I?J!La_ z!?WY*VoZ=4yu0T)Q0oKO4IYpeoLzBKj|^m{|#m1^L(^T|3pI-@ z(0`pR-7W~;D!QM_jxG`G zV&_Vm(t@2}A2i(I04|{@y|^ltGC4Sk#+bbcLS`CVhEU`gvyr@p+MBap(A5>lR}rk9 z2HR}YrvQpFxM*gMdJYZEWzec){PEjYoT7^I*Gdk@y@K~gpOJ4AAE(lkNWw9TL0cW% zK3%`kmPwK!qbDzXilTqsi|lLPa+(ms`@oCJ9t9!pMaN@zdC{qzw&r}39$E5JeBa;+ zt^cyOPSfS_$4#zJfsc4Y-e?&=aftOR?|YmNP$+|i&KM;{TFzMuq|}Jk67SlU`lrtl zv9uOA2T5fy!QaSx+Xeo@3j2)0r1!zBdY1Dm2q5A#-Y47s9GwxS@u+##)95e|t&``R z6XCl_a8#x^cw`J*7)1tONL#bq2IVvpE>E*bhA1@$+^eY&xv=Qrn?5px#>zVqAoCjI z?a|&FIIvZten9#N@~!L0wwl>p{^4U3MZKz3d3U&sHQXmSxZ6wIVFXQH#IpYw3QHq6 z*~*;FZyh!%j;t4nbwsL4PHPzFf*Xk;)9)d&j$L#FYny2U(|H(OtPzJMJn#rYA3v#3^$+eCeLE_2!ysQ)qYmb7QRV6i2s%4U2GD8qRnubANUHdkGHw}{++ zGvOyfB?`(xbTlXN8Qjs9{uto z`W4zSiN%qh9WA^m1><{O@39Us)%;H|tN#AgAMW$*o0g{Q9NJlW@mxSW3#6uo*RMbq zh8FvEFl^@u+Opci5U)jRWt|B%SRt-pk|L?EI;=i~mCTZ~P0Yj&`D;r!sbT%Q^VLBL z9oVB5hw0BW<#o*Qaesau)g2pq>@K=Zx$fhU_y?L^1)`yy<7Zsnx2#ovm5WhlLKk|x zNnO4BYs+4eY5rv{Dzl3ExGC31v8A_wR4dsTqtwG04VkyKd!TgYWa8K)$}wP{bBSyG z&rFF#gDOSo6BW`n@9!=Rq#4E1Svspi&s2>E1# zwWCeE9;h0~Z;!z#mX+wAfDXpYV(!$-rszp5-zu5u88zBGWS@&RPEiw+S~h*aMAI1P zB|GqJe-VKx6mEuo!QlV7)?Ke3ZLy%PVSM2mGM?|!by^LoEa6k1g~Yf*TTL`da(K{E z5}F`QZ+q{JW&}coeTHx^&2#kcQ>?x@Tl_!OomX5F;o9v%5kiyRI|xWk=pr4d3L=6^ z?^U`;@1cnV5J3pN3P|rDJyhwvcR_mZ5JJwl&;HKd-}!y}@?4x;W4&^fX>1nLD7Oncbb_cDSGGIXD40=!f z2#Z{_p8oP`Xm&Nb-7OKjYDYxh>SW+UVg{49jFDNBchzh_OZP`f6*~1yDL*h9BN33CQZ#0x9{u-`SnM7V5Xb!x0{X7rvlCXob?&{RQS?JFR$MyKcB& zp+*a{x*-;$Lg3D!^~P*wCRrkH0FvZj#%rRM2@}iVnCu<|a;6{YoZ;V7nR)w$%E$4Z zUANj9)wg>!g*0;G%L29_a}<+~C-7DgA<^d2X=EBINj+t{X{ik^u^OO08JUQ^Y0s4Vt zTpE7wUMk`?M}3Xq&aX8xK^a3{+o?7ZYAdJGM(OF=@KJ2P^t%@{fZNC;3HvS?wJoMc zr51M(>~OEBYup!mcg6d})#<%61TH@pdMG1LK*!8mtfurbaxCL`iAd=)h7p44x(be= z3JU;D$9+fLy-EZX>r*3r?4$)-lO zI(o`3f=C4fK|anj%yZ)Av6Y~#a*np;rsC1}NL+lz+ga08vBC8GqyPd@jdQdsy9TX;9ytBUHqx-9R{66(|>O++uK>28I1HFWfvytnC~ofUN5mBu)` zm(d9sPG(INrK^jXC6Ng`O+njI9C!MOyr&xsMT;Cqk2R4lxMp>{(3WbGov4-awD zHRT?ivO%ff0S0xq!WRI^V%p8ifDbv?yV9)Kr$AQ0uhVsiZZ$v+4_F4+hcKa!i{+j1 z;YXd3$o}N2&q_1F`nbbZO|c#Tw_|n^-?(=#Y8uj`(*(&dmy?wQb-z_Kcf&L-jR_?* zkE*Z>EnoRee9tsF=e{xqlfAGh9Igko?V4yQIj+$lV&Rp&e*;wT`+z{|34*`MvFXsp z#kV|~i#B6`c+Q?M1ShWC^Vm$pyeEE9>35hw+dE2QXQ;(0Sn7{D-eBd5GLfvO;sME>y!0*A>az(hVO=@dvz5!NxPJlSrJ z*&tKjkCi=`e^T#s|MYx?y3U-gdkVYoQ6Sq|(joR|!5GMe3lNuADlkBDVO?{8@Xm=x zFa_M*UekVwVRLN{#JoRonGU)RPiEm9Qku}AquEa>)TzkU3stlggOZ;x?UV1O!e0tQ zRBMpGkdX;b`xSl)BbFe;#b%URS2?FolMLCe>9*>^k|w#+>GM01X6 zze;SucbV(f$Li#EB^?XOyJ?G9)m8D$CYT*IO(x)T(3+Xw!TxVSyOyS<^mT~F#31X^ zFf2=|1{$^?kkPsR5{@eZX6%xw!{b~DdB$;f5$C^J7lk{awHAs0~ye{ z`%Y0#XS%qPtgt7O-SE3!Xd5fk{_YTKH4c7jL>WbBQhlS$&AzLF_Ax(+e4xb2`ih2D z{>8bG{}@)YkM_;P`%+an{Tf@-R=&SzWNtoTEiop8!1cq|57&10#7L}*g;6_F3{!Ya9Q(ex%Wjn^}rOdWa`sqWIrYe(qx5uS-X2ud!U8VUqv#| zzfUO{hFYAmYEEhbnULXm9Mo2ZModK&S zD?h&fb8V-6YbNc~9%?E|xWOln2aViyMmf`v6q~QtEN!P`^WACU*>!?dQ8EEq95Sa3 z1W)bI9)wCDg^FxVyltEtp_yseGXtMbU!6SYv(RuHXP$C$b|#c8xp%E7!-!{qE*9CK z|J4B9X?5^*;1zLwo~-2Q`T>*bLFJER#Usmf%b~lqi5J2|o)v7*bTja=-fCd2p4Pn) zN|B*}t&m2NWbf&-zQjWq?ZZv=<%=^wN7l$tPm)d!=5nqOz?BsMoX})sQ4q@P=K7df z227PA|7AXQ|0Tph?2lbBZq}iHNnr2ud#SiLSS((YLcHM{f6hurCoYW0TsN2@b zzQhFD6ZFziX8IR3)Ho)hS5oC&20>(?20;0l}W!h zgdex(RxxC-!*R0b^mj&c!j-yfr|;NcFP2;U{zP!5f134=ydbt+#=}3FwT=`hf2hYb za|8O-1%|cFPKB0-h*s>sgW%Q6FrAk#GQ^&2lhMgw@|qe!24j?~gKbuNKecGY6IneZ zbcAn+>Mo*pk^i_>*^JPp?CUY#gY=2kXym9XxUvV>Kts&#^(&j4A^Lka#KmIIX<9N6 z!8k>bQKiHD5)kQr>PA3<)a7unDBs?%OL^yZ&rVc(D1J|4TGrTXQt(Px=SvkO$TcEO z^6NKo(nD*(uQTxqU@Jcr(mxU2L|GVl+gGt+4@jQnbY>^Xs`%$|>Bv}>EnEt5A|!FV zqb{t6FVbj_xa=#!DRHeNHwC)Juv{vK6;gWq&s~-gHZ@0!;iDJIm9>8mnE2s)p?IBT z9abAQJM%4OHG&UJ>u;(!o+{6S?tnZz!YKRn0(+%qs_!yH8;c{a4@K5+U9ZZI6&AxA z32m%BN_D}rk>uKhj^0b*74?39xG7jW8HSPE3cDH2&O!HSB^pH?MLRspdJOXNqryeN z%L&&@rjI&WCeI}GH^n-5Hy^+DY|UhsqQE*KWoN<`;eC=OV;AoWBjJVF=x2+Eh~MM$ z%C_Et>(=P4)p|k+0)oG$?n+wt9?W90+lN2+oPP%v7+z_tlrQ??UD4V0lzFw30=Y4K zpP`W@#!Zz4RQ-PPxwmMu%`_1 zvpz0=Zp??1Ko>-tpbFwy!`*yXZ1rx(<{3tR)#EdC-xD0+Zc*i`#qR$Cj{1+pJG~sR z9Sz!1yX5{)u)8y?h3@`;+V})Y{406S5O}mF@hTZ?$`TZh5>FA*hE`kG^?OsNV@SuBc~7amq!R#Za zo8sX-hluF4j>%GVjJJo=B8OSGA%O4`?YIo=OH4U@vCm6K&!H=Wy(`c*tM81w6s-o} zEX%&9gDg&_I*SR{VC{le?VBa~buUjghrzl)Mpp{>xuMUXr(UanGdaJ(6Gtf;R5$RT zqR2P+`N8t+oy)$*UrE9mh!^0a8$1sAfg~)|xiNsE?xlEVzyc)MsU=^k3^~7A0K*Vl z;pyM_y?CD2jh(L-u}=Qk3@eG>zAg(C-RhgSkSjAL6U9-wO8MQRnkY$O=I^J`th4bE ztxNT#7HV22`RM-qf(2NpQ-H1Z8hw9e`_N;`|L|>LsjpUc?OB2M*8$Yf*`;*y)tH39 z8bU^T<9w^+r-`JG$a_At?+H-Y6H?uvpA|vXot6^vH9GFd)ZUD&g*P zsW`3c4Y*r#8GK+hm$4zkNNCCNXwqlo#*xDTB*RYgBm*v6%U%0vspu2W_@T?9V%xzlsb8i z3?VXGI5O&R^k!CXu~>uO(>zw!y2gA&zcmwcS)f2&LMtS9jBLfo+0M-+Vpj5gkEW4xo$B>8TuL>Lz=aj^61Pk?In2F@U>4>QL}N zD1s?cNCxrPV`oCOwDrzApb{vDg8MAcfUkp*H{fV7Hld7);|#ylC_nfcMfh_wod;;W zY30#zq+25+1OX6l+`0p9J(&E)^8g6%L(M*VZV2$S-y*>+Iy5Ul?t)F)-{fce<--4UZz6t~3ga?2= zX;EKb@jX@}u)u#9l!JqL7ef!&NB_;IV@Jh;uL3r=yS9u%tlAlZW9yo{qoS=MSgr* zifI0xPMx%+UjDF%L)Wj08&5Wj=cbZZ0d^cOCnaCt#EeMVacE52^&z+CKM-nJ`AxFr z?d4rZ-s;K^E6d#Y_AL)LQF?=UUHZRTRt!V-8T^qsTz(pMag`pI2f~-0iA=lc$%3n0 zbu|r^&y;PEG;8y3%hYEpKYH~UUrK$qO`+tyV7#6WbXD>LE(uohJTf?6D7 zKq(;tz6J&hJ{Sczk5=7Y`01+Fe7Z4sdVr$=brS=y?a*bWfZNzgo6&6Yj`dwve>BRY zYzydml-b5F*1MSZoo$b60e&^9uYvT?c);1;^E!KCGFuU!ucwi28q3dP2?0SGN%g)2 z%%;drTdFAx7y-42h2r@gpk9*XHTL1V2*C3*rT*9)uwOIXT>>_00p`+)GmSrXsuE!Q4wVb@EQ6)TYzyEMQp<~)HbGp1>usygYW37>RNryUr zB%yJUYH=Ai2~;cP8;SZ|pV7J6keY(79^fjy|IL#I?Oi$_0Jppe3v_wNLi}6eh)k#a zDxB*jxXbcvI~at*&{FZS^4O)h**ZrLjMX}%Hp(6nH6Qy^;9Uug`%^!T>}c2)su^gY zLElvUfW%yeaDD#TL&@ko^`_5r&MsPO>GtuT%Sfj)&hO$luIY8A7k8oSG8uk3sG!VI z4Xx#Cg>jVak{2j?+v+UmO!=2oi8lK0xRUPgRSd}bs+&S*cL)Z#2BdhPL5 zx8xJmc@Za-^!v0(mh+j~N9{Hnn~UT!Arc8DeKc&R0S8WTXP??Y1U(*}LL7@m%Q!M!KXzROuPIlve<(S+!#%4q03^^!zV4T(c6*Ao1F<`TVWr(#_>z z87#x{%(T+hGq$pxNFE)Ps#gZ`V#fA9hWnA!RN3!Iiws@sR?jPxZ~vlVreET z0d>yk*<&|E)7Vm`$=YA_^hav++1F769@j_=)5N5;IFbPfwShC@0T&<~u>G7d`u8?n z!vMHn%73nCQ;F!=;_UMn6qznq>0EwzgS#pRFv+*Q0-KN{N>?-?)&mo(;wQqhf;S*N zXy+9(Sf*zSXVhgH7(sMFXMzXYgBh_S_-nHrV-Nd21`OBk@0iWOph_-uD1K|0}~Bj&cODRR@m~z zr{ov>d#8;{x{@mz&3Q&@WnAH9B^kB+#$0d=EwIdhn1>^b5~HB6CJ`?WrBBH>$u01w zM{S;TV-)T*=9n&XU31w9d2~^`Cww=+cIe;kbLpRNT*xsgdY_})D##Yxytb^-%54?2 zZ8&^YP?Y@rB-MyBogF_Q!cAG_qs)wzfK>q6_ogPf$&6Lq&$VUIi9;1B&}#V98P15H z96$Ks6p($4YoAlRiwz&+zY$5YZQw(;N}IW-@HwRm5B^F3$LuXuu&Fnw!;eQ3U!yK| z2}8K#jUPJr#Mm3xC<$OGoA+j09jp)QKB{2a%KM3LvGQGP7mj6|(H^c_s2CO zQ;Ewm2Xq0=V0}b#i+{KMpmDkPH@L52Oj!}N*;6KVqBVLLq(l@dE4AqpR$F4g_VY7U zm=CDDB5p8pB9?&7 z%xt0_)c`+)UlDZCP9`$gO=*TI!|L`PoW4j4KRg?|$Is`-f@l4H{RIXnj_m#>Nc#fm%>ud+QqKK+b-#dHH$m)t08hX-UtRR?zrrS()jiwG#Q}d3G1cEvvNSZpY7HQ=|VYDL8u?cC(wL(c= z{qovOql(+$|L5Y2nLa4k?CeFxth|!c5v(`~!k%{_51@hOv|?vtCdImtN*$@)OUADy zveH$bB&W?ki4N&9EA?}BrHFp5@<$rI^t@J0TCOTn^SmQ0i>`^wPYFGK225c@f1pMf z^kn>N0t`OHkr%#E*F%lHp_N3#Zzkx*Ez9URA-Gt?5`U`1zAwdH_KwMfJzisq;~G*N zGE@9mQRg=MVX$J9w@GXuwPaI2vE-GpIrVo}?DP0;C$}%#Odn%uHmq%rYCPz={c|V$ z_84s!TT{r3jdc5q(}dNlic{{&?30C-tuCXx6%0hdI=Ryzkx!v*?I%qn&EMyu-f^GS zc`xLuT%sKEFdRCWYR+f|$?JY|(kvUV)fFCt22jls9>6|@_d9JGb6#^Nj7#>U`-H)K zPAog<_o+Up`L3$#nuTDh%lN7f(cML^^BKRrNt@zOlFcE!CA#C9&5pSFtRG-REw>bK z7vuL`p{hHJ&nlJeSHZ_Wib`$Yfb)R-qTR~(ZPaGuj%4*+xzd~27VnY-7Y*oLlj4yu_Adz{%11U>@H5cIN!5oTG=k}l39-rfEq|D~F z2Qde~@4rPViv93)pd$+ubTD~D&3U_4wB#H*$PX#)N=aV0I`5s^ z7Xll<@6lvfO8&pS*bY3^D!9iRzM~U77~SYOI}-6sPm*|&jE8A%UA@WM$zg!}U566iTeOTC&e$NR>Y z-So?wGn_`+EKZ{sLK*~7fWAEUfb4Sa_W?ByCbh-wR@BrJj_}c`{=dX7`}6pbkMO7F zC*bZ!5*$oa-NFXru-Rrwz_@Qm84aibLDJ`@GY)UZVwHA}Oh0%XjsYsc>7zn3k9;rHce2Kl33j_>n}#voE4GI5V*a~cV7^4t0n=BbqSpN>`s1TU>Bm~uV2xPs zo*yQrr?-lIsorA0Q``2c0*Oc?6%Rx_4@kg;Xx%rhVsnjr$rO3SVLu09t6wi?#*)Jz z_X)S4M)z5jt&J_KODVvOvW2wROxdW=_iqvn*tO8xoQ-9ndrTdvEV4h)6Ze3a+`rb!WxNYMd z`cw`tR(dA!?ufk_VwxQ0=%O#7AfR;ykY(=!(w@%sHCB&ab4hip*qfR+64B+Q)ux|# zY0%D=Av2a3-L&fN*^)MU2uJVyVYfY`Kya_&Nzbd9ih>6PcAmTp15>2HAB~;mOn<*s z{EyG$qZCbk3)Ni5;Wr_f1jVF+$L>1@_Oq z*_gqdosuJHTz5$^?|obIUgzYq;&E0hX`ACoqyTyte#+T=la3eJ9%75Qpx>X)2)O%c z@Nra8fFNIBd|KAi<_JY@0o#{aD=N+Ju3jdiHeojz7=1Ip3#KY_)K8RZXTIDTeU|Qb zp{p)$6S%QtoH%Ir8>I3Nco}Bf4Cw5KC@-TygeFEFVYuqe1{It-LJQ6vgvmB)Xv?u& zRqwR%J!dS$ZLk2WY>{OJ5bSI2Uqy*66##wm_N7=!kTz@=Y9ZEcjm3C;VmgQ&s#vL1 z{v|yyctQk-b!5@WbV>UH)D3MGFNU8H(U<}Wd4;oLR=4ty))dDE)xJcqQ&oM=Ka0qZ zWLGk&QGlMh_s6ntChfuO@|kwY1Sm&cc|0iMk!Hm^w3KGF0Ihldx}E-jjHIyL)Bs(S zqzwE)^&hx!nOIL|*zU}!URP6JGOpJ5(cK^Df_*~DZK`PGq6j%msVU)$Z@7bei|OSE&^ePCjbDVu|jpCqQ{cz$byeCu83I45dem3HI% zrCaT+=I^6<02P}2hTTnAltkZcc#mZcnt{ZZzX$54yD8J|N6C|q?MiyrX*cYUzLdgi z5MVY?KoTf!6NNZ}$Zk=1(vbLinzk*C-Lue(@!5b|6W*tlwu^6^8lL-0A-R*Te$Oj0 z9~Ux*bpFn~PD~?BMkq7%^d5y~S+^*buXAn~U=hEH|0&rw6C(&#Li}2OIaD#%Cbb3~ zMKudJ`kqhXZ9pqpz1yd_r;SV7Ut+m{Ot*`;oP2W+0~Szqh0Jb^0;r5*O{#q&tl={j zbcWiB%n!#9A<$=fcBQtf?EY{j|Hr7CEt{vjAUcro{jnPn!&7axqa1e5*S>;Kxftc`5*{O|<1wHh!jmT90R%SJf;LVdXMe3K zZm%~p-nYk;udRkNUBGXFB8lb|2|`-4u_OH*K%dq4g7h#${;=~kpU-&p6cWIcO?{Rd&dumNL$*8=T30JP2cfF zN=XPT2xt2xie}rpGMa;wzXejbPB&)-=$nGoP9CMan*y>FINBTCvy_NrSagx;H`^P58yq z#JRsktx9?eQhugBA?$Q$J0U=jwc9QT*k{-qR;r~SL@1vk1U8)3M%|;mqzDW~66so9 z)cTLNqvdUSGQ2jD;KlIUoG7uNJ2PsOg`0T-V0`Xs33qA{KSxtij8js-tUk6!Y;xY7 zj9%by85!D4l~Bl+69`}yZ%BE9_h|FV9Oi4i@duoP3-s$s2^b}HUdo_8E^>btDis-2&u~=0nQGr7G7$gP z^bW;>N4xyAU@5{Ue23N)V@|dxiEs-486w8S?}a~8_QoL=huze*gUIMoh2yhwl>xVn zV%-S?agI>~pwWb8qN&feEp$5^RN}cRx+v+)9Yvb)_aG^E2u*{@Mq+ znGZgEB6k7Ov7s?NSnD@ji>}>CIUHtkkACqih?~Ofh}}3^{sFniP!)~Xr}BzM^L_7O z5sYq%jIBJTCNrphIt#=crf6UatsbcV)b_aYv^}PI-}DTnkn-64GVx-W?I>$@RrdZLlCuL7To3GG8mP`_on=2q?bl+!&j?RH zyEf>O{iQr$*E+e#`u-={OnNu$*w{YBE)JL)Ia{z2tLHMF9MHjZpN{@8O@>z69{7TP zyxLFhUU#kJXEdeXBa*`m>dNdwq16aWEh(S7vpmd+g?*-8en&^Mu%YOxO72P79}cCw z6_hRN)vx`SyaT}vGYvb5=lc%$L9Odg91^KNd-h4>{y=GuXA36teB zJ7qS6OW~QaerPRqUcmL2R^bkW548uKa8nJ+MxN8T93_=VY zZU45i9%DRxQ9>^w&9NJ>->x*>O)jmnYdxfTjuwSQvEP26>G)O_Epl3W8LWJJjC)J@ zNOu2E#&Y<2fJCtad4f2FHUx)`FnejQdmAjaNgI}7qdV9=JXY~Q9@g2 zV^xlOth|)u)px;fcRnyx;XXMt`iITem{IPGU_IY>$vf`|v7`B))6@>Pf&rB`w;jJI z3-kmyq?R6d;c_TDtF#hFI+5uKx#+2BLcydt2-4*r4CA7IyGY&3jd?~&+cE9fLVe7# z6|M}*!YzQQ(4;r@eXI(Cu`wCt0wtO-^xUM-dh)k%Gk+bGRXthn!A8fU>`qR)9?hdS zcG%;Y>Zg>Q)pP(0Nl*Td;hQrM*$WxNPLipgC(f-5zEMu@QZLZ0-n_XDjOg>=sP6Z zTc13Qy5wE-)#GQ1Kn>AhwLFD!Bveu}RyXzh>8cu^GUM&(XoBgG+EK2Tl($`{f76N^8dFDVa z0dq`y>J*z~YhRn%jmC4ml6xNbtaoIF>4(^HPOL4+n!fsXnGWGWUC+Y=I{B&Opd|V> zwfYLo#7dD!q6rHF9;o)XLUTStr*&r9L@2KCe!&MnJy-90wInW0!0m;VQL%Ro96547 ztNlyuC0^qz2u^tiB9q5SON_CVtF|>2ZGg&m@_x0bYu_g2lX1uKi}i~13vm9p$l&fQ3xd#>stZq@mO$+wdwQp0j zd+O@~FSSnzPf!RGK~ZNKmLZIg8dBG2wDLfl8r&`UW3g}c-!{=}s$(i@w2plnwNTl) zlV9k-&PiG=SKogvk4+R-dJxk~-~A#-B7V-_ZJB=(P@}Cdhfj~LfCZtJEq+T>2tj^f zSZh$?Qug7%)tW?G8o_ST=R`Uxu4g);SIB3>>Rz8*YkWs|E^%5vRG0hj^JDHqYI>%n zr9#Rvb}DydM80e@&sdAw3BnfMg?WA$PS|2Zexv^JBLjvE&tR zNSDo^Q{%p<2v6Msj^+;QR~h*SwVluUqXSStw|pHyiKjd`v!z1pIsiYLtWAVW6s^cu zre{4>xgj%w^f0yr^m^4F!wsl?1&_~Scwvp;-+@ZQE#;b4yf0?qi$K!HWW#=zdZb6^ z>-$;10K={l6yoK*80njWm1NoSrf2G%+{}+N{?6oAwv;d;nK#BCD@X&-?#H$~B=38@ z^SkG$Zh`6A5T%Md(?|Q1MmAXy25J_2jFZVWq9?>$t@0__WGX|t>mJ}l?zA2lX~6PN zDgTN|JpX-Of0V+tx)pvytQF=kk(quu$6@Q9Wxwe~`a_|(u17Nm58EiMi8p>F*s#qp z4~*fqSg7u{7ta$9u=)kjJZ54W7aQ#L*4=Q{cY`5sali#N> z^3v|4IAVh+*=NiY&*bUQ9x@6Bfnm;@=*sVIUAoq6qifc1yMa(_7b$A*HB{f#sPt0Q z#dHI*8@|!5Am)Ys1{d~;l?qrAc3~yPOFg;PFEp%$iJQ}oo6(J?Wm;Nai1;wjg#|k_ zF$0&%!b4+Y(8k9!+?5O0j0`Sl;*edOI{@M1bbh!F~wILFIR19o~%DMYf&}-?TDLqUd<#e zbgy*t4TjCQK#HTqP4IDE2v;j&R1Y=dOYdTBivRPiU&{$#sbN1Tc9V5+viawSc`BWW zgE`kqew1t$Y&+N}O3zNb`&pXu;mO92$Ecvg+ie*)0o2VG!2|7(z`zob(*&NJb2Ah) z;&LlIBhF>tXiS(FL(d->D<&;0a1>tA4L{e4ccG_PZircQz|u-F+mLggDVIrJ_c!7o1{pU{t0Q!* z=KZ&)VjP7&Kvqj%V0#F5lA^&qldUZGeOTt?9>&MNW^lNYw)RxvyMgCLF?v)Icx}1U zMekzX$2+#7+$mno*i}#XL_wKhCVjIqcK|FO55zM0=-Ldo1>JW;N{}AUT1&|N1K*Vg zV!FuvO+!Wyc#P$Ynd&;K<%?Mvx7R8%=M!3!DCmXMk!;~>9A1GCD5bAbC6h#x))#x=@PtriKU!JvBcf!!YAyvAYZuyYBkqodEzkWjJK%u@=kzwx z)yDdRQ)UXcRwOh^KlBdytbFp#C6`y>zDk~flw8&o*)Af_;0igmUA#1T5-Pn1IH%(W z4%l@DR{dNtNqAPDoufE(kb@BM?d>|y8>Ty6Y>yC;>iOgzroM19uE5^f^Wld@DVsu$ zzoQIqzB%-NNIqmOUhdeD{32Z44>9%UxRu=zhYRZ5+KVMSVY!%MLWuGl`^+LL7gq+w zIvc}c{i%4~?otLr>f}DpG5HpKwhk1(oDQE_F1{G=e0sDQ@sZJmUKx=n3F}ojm9PAn zDymF_RNYkE5m%rlak9*>C^yA_YxXcCHjRv=wkxH-qwi<+Iw;lmq_<&k0H$dIavIMGJAXb9mM;?1KntV*8lM&b%PiKBG zABTvBVPIf>R8^4E2D0cPD(^H7{z>89aYi{4{cj9WndaWD@hAE4kkObE=ERd&3ShU7y4B(}z_)?)%-URx; E0CWl01poj5 diff --git a/vendor/gems/graphql/guides/operation_store/operation_index.png b/vendor/gems/graphql/guides/operation_store/operation_index.png deleted file mode 100644 index 89d64c3c7e8b0e0b2fc7176acfb7772e3f5aca91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72173 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 diff --git a/vendor/gems/graphql/guides/operation_store/overview.md b/vendor/gems/graphql/guides/operation_store/overview.md deleted file mode 100644 index 7e5c0b2ef48..00000000000 --- a/vendor/gems/graphql/guides/operation_store/overview.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - OperationStore -title: Overview -desc: Learn how persisted queries work and how OperationStore implements them. -index: 0 -pro: true ---- - -`GraphQL::Pro::OperationStore` uses `Rack` and a storage backend ({% internal_link "ActiveRecord", "/operation_store/active_record_backend" %} or {% internal_link "Redis", "/operation_store/active_record_backend" %}) to maintain a normalized, deduplicated database of _persisted queries_ for your GraphQL system. - -In this guide, you'll find: - -- [Description of persisted queries](#what-are-persisted-queries) -- [The rationale](#why-persisted-queries) behind them -- [How `OperationStore` works](#how-it-works), in brief - -In other guides, you can read more about: - -- {% internal_link "Getting Started","/operation_store/getting_started" %} installing `OperationStore` in your app -- {% internal_link "Workflow","/operation_store/client_workflow" %} and usage for client apps -- {% internal_link "Authentication","/operation_store/access_control" %} for the sync API -- {% internal_link "Server Management","/operation_store/server_management" %} after your system is running - -Also, you can find a [demo app on GitHub](https://github.com/rmosolgo/graphql-pro-operation-store-example). - -## What are Persisted Queries? - -_Persisted queries_ are GraphQL queries (`query`, `mutation`, or `subscription`) that are saved on the server and invoked by clients by _reference_. In this arrangement, clients don't send GraphQL queries over the network. Instead, clients send: - -- __Client name__, to identify the client who is making the request -- __Query alias__, to specify which stored operation to run -- __Query variables__, to provide values for the stored operation - -Then, the server uses the identifier to fetch the full GraphQL document from the database. - -Without persisted queries, clients send the whole document: - -```ruby -# Before, without persisted queries -query_string = "query GetUserDetails($userId: ID!) { ... }" - -MyGraphQLEndpoint.post({ - query: query_string, - operationName: "GetUserDetails", - variables: { userId: "100" }, -}) -``` - - -But with persisted queries, the full document isn't sent because the server already has a copy of it: - -```ruby -# After, with persisted queries: -MyGraphQLEndpoint.post({ - operationId: { "relay-app-v1/fc84dbba3623383fdc", - # client name / query alias (eg, @relayHash) - variables: { userId: "100" }, -}) -``` - -## Why Persisted Queries? - -Using persisted queries improves the _security_, _efficiency_ and _visibility_ of your GraphQL system. - - -### Security - -Persisted queries improve security because you can reject arbitrary GraphQL queries, removing an attack vector from your system. The query database serves a whitelist, so you can be sure that no unexpected queries will hit your system. - -For example, after all clients have migrated to persisted queries, you can reject arbitrary GraphQL in production: - -```ruby -# app/controllers/graphql_controller.rb -if Rails.env.production? && params[:query].present? - # Reject arbitrary GraphQL in production: - render json: { errors: [{ message: "Raw GraphQL is not accepted" }]} -else - # ... -end -``` - -### Efficiency - -Persisted queries improve the _efficiency_ of your system by reducing HTTP traffic. Instead of repeatedly sending GraphQL over the wire, queries are fetched from the database, so your requests require less bandwidth. - -For example, _before_ using persisted queries, the entire query is sent to the server: - -{{ "/operation_store/request_before.png" | link_to_img:"GraphQL request without persisted queries" }} - -But _after_ using persisted queries, only the query identification info is sent to the server: - -{{ "/operation_store/request_after.png" | link_to_img:"GraphQL request with persisted queries" }} - -### Visibility - -Persisted queries improve _visibility_ because you can track GraphQL usage from a single location. `OperationStore` maintains an index of type, field and argument usage so that you can analyze your traffic. - -{{ "/operation_store/operation_index.png" | link_to_img:"Index of GraphQL usage with persisted queries" }} - -## How it Works - -`OperationStore` uses tables in your database to store normalized, deduplicated GraphQL strings. The database is immutable: new operations may be added, but operations are never modified or removed. - -When clients {% internal_link "sync their operations","/operation_store/client_workflow" %}, requests are {% internal_link "authenticated","/operation_store/access_control" %}, then the incoming GraphQL is validated, normalized, and added to the database if needed. Also, the incoming client name is associated with all operations in the payload. - -Then, at runtime, clients send an _operation ID_ to run a persisted query. It looks like this in `params`: - -```ruby -params[:operationId] # => "relay-app-v1/810c97f6631001..." -``` - -`OperationStore` uses this to fetch the matching operation from the database. From there, the query is evaluated normally. - -## Getting Started - -See the {% internal_link "getting started guide","/operation_store/getting_started" %} to add `OperationStore` to your app. diff --git a/vendor/gems/graphql/guides/operation_store/redis_backend.md b/vendor/gems/graphql/guides/operation_store/redis_backend.md deleted file mode 100644 index adc2fa590c5..00000000000 --- a/vendor/gems/graphql/guides/operation_store/redis_backend.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - OperationStore -title: Redis Backend -desc: Storing persisted queries with Redis -index: 3 -pro: true ---- - -`OperationStore` can use Redis to store persisted queries. Pass a `redis:` option when adding the plugin: - -```ruby -class MySchema < GraphQL::Schema - use GraphQL::Pro::OperationStore, redis: Redis.new -end -``` - -(You can initialize `Redis` with any options you need.) - -__Note:__ Be sure that this Redis instance is configured as a _persistent database_, not as a cache. You don't want it to throw away old keys! diff --git a/vendor/gems/graphql/guides/operation_store/request_after.png b/vendor/gems/graphql/guides/operation_store/request_after.png deleted file mode 100644 index 657172b964555d3b4fcd57f69d35f01b126003e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28830 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 BxKRKA diff --git a/vendor/gems/graphql/guides/operation_store/request_before.png b/vendor/gems/graphql/guides/operation_store/request_before.png deleted file mode 100644 index 61dea0856b7932568b28f60b0c32364621069271..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23142 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 Yf2QM{uruy>`{<#`zf+d302uoJ4}N-G!vFvP diff --git a/vendor/gems/graphql/guides/operation_store/server_management.md b/vendor/gems/graphql/guides/operation_store/server_management.md deleted file mode 100644 index 7fe37854bc5..00000000000 --- a/vendor/gems/graphql/guides/operation_store/server_management.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro - OperationStore -title: Server Management -desc: Tips for administering persisted queries with OperationStore -index: 5 -pro: true ---- - -After {% internal_link "getting started","/operation_store/getting_started" %}, here some things to keep in mind. - -## Rejecting Arbitrary Queries - -With persisted queries, you can stop accepting arbitrary GraphQL input. This way, malicious users can't run large or inappropriate queries on your server. - -In short, you can ignore arbitrary GraphQL by _skipping_ the first argument of `MySchema.execute`: - -```ruby -# app/controllers/graphql.rb - -# Don't pass a query string; ignore `params[:query]` -MySchema.execute( - context: context, - variables: params[:variables], - operation_name: params[:operationName], -) -``` - -However, take these points into consideration: - -- Are any previous clients using arbitrary GraphQL? (For example, old versions of native apps or old web pages may still be sending GraphQL.) -- Should some users still be allowed to send custom strings? (For example, do staff members use GraphiQL to develop new features or debug issues?) - -If those apply to you, you can apply some logic to `query_string`: - -```ruby -# Allow arbitrary GraphQL: -# - from staff users -# - in development -query_string = if current_user.staff? || Rails.env.development? - params[:query] -else - nil -end - -MySchema.execute( - query_string, # maybe nil, that's OK. - context: context, - variables: params[:variables], - operation_name: params[:operationName], -) -``` - -## Archiving and Deleting Data - -Clients can only _add_ to the database, but as an administrator, you can also archive or delete entries from the database. (Make sure you {% internal_link "authorize access to the Dashboard","/pro/dashboard" %}.) This is a dangerous operation: by archiving or deleting something, any clients who depend on that data will crash. - -Some reasons to archive or delete from the database are: - -- Data was pushed in error; the data is not used -- The queries are invalid or unsafe; it's better to remove them than to keep them - -If this is true, you can use "Archive" or "Delete" buttons to remove things from production. - -When an operation is archived, it's no longer available to clients, but it's still in the database. It can be unarchived later, so this is lower-risk than full deletion. - -## Integration with Your Application - -It's on the road map to add a Ruby API to `OperationStore` so that you can integrate it with your application. For example, you might: - -- Create clients that correspond to users in your system -- Show client secrets via the Dashboard so that users can save them -- Render your own administration dashboards with `OperationStore` data - -If this interests you, please {% open_an_issue "OperationStore Ruby API" %} or email `support@graphql.pro`. diff --git a/vendor/gems/graphql/guides/operation_store/sync_example.png b/vendor/gems/graphql/guides/operation_store/sync_example.png deleted file mode 100644 index f8db4cc219c5f6c51553c229b6baba6c0b24e35d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101088 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

i=ZrC&BMpC~qyq;9sWCzk*Yg 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 WSUYkCf?OX!KPfSJ(JEoXp#K4-Bk#ok diff --git a/vendor/gems/graphql/guides/pagination/connection_concepts.md b/vendor/gems/graphql/guides/pagination/connection_concepts.md deleted file mode 100644 index 74e77153ac6..00000000000 --- a/vendor/gems/graphql/guides/pagination/connection_concepts.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Pagination -title: Connection Concepts -desc: Introduction to Connections -index: 1 ---- - -__Connections__ are a pagination solution which started with [Relay JS](https://facebook.github.io/relay), but now it's used for almost any GraphQL API. - -Connections are composed of a few kinds of objects: - -- `Connection` types are generics that expose pagination-related metadata and access to the items -- `Edge` types are also generics. They represent the relationship between the parent and child (eg, a `PostEdge` represents the link from `Blog` to `Post`). -- _nodes_ are actual list items. In a `PostsConnection`, each node is a `Post`. - -Connections have some advantages over offset-based pagination: - -- First-class support for relationship metadata -- Cursor implementations can support efficient, stable pagination - -## Connections, Edges and Nodes - -Connection pagination has three core objects: connections, edges, and nodes. - -### Nodes - -Nodes are items in a list. A `node` is usually an object in your schema. For example, a `node` in a `posts` connection is a `Post`: - -```ruby -{ - posts(first: 5) { - edges { - node { - # This is a `Post` object: - title - body - publishedAt - } - } - } -} -``` - -### Connections - -Connections are objects that _represent_ a one-to-many relation. They contain _metadata_ about the list of items and _access to the items_. - -Connections are often generated from object types. Their list items, called _nodes_, are members of that object type. Connections can also be generated from union types and interface types. - -##### Connection metadata - -Connections can tell you about the list in general. For example, if you {% internal_link "add a total count field", "type_definitions/extensions#customizing-connections" %}, they can tell you the count: - -```ruby -{ - posts { - # This is a PostsConnection - totalCount - } -} -``` - -##### Connection items - -The list items in a connection are called _nodes_. They can generally be accessed two ways: - -- via edges: `posts { edges { node { ... } } }` -- via nodes: `posts { nodes { ... } }` - -The differences is that `edges { node { ... } }` has more room for relationship metadata. For example, when listing the members of a team, you might include _when_ someone joined the team as edge metadata: - -```ruby -team { - members(first: 10) { - edges { - # when did this person join the team? - joinedAt - # information about the person: - node { - name - } - } - } -} -``` - -Alternatively, `nodes` provides easier access to the items, but can't expose relationship metadata: - -```ruby -team { - members(first: 10) { - nodes { - # shows the team members' names - name - } - } -} -``` - -There's no way to show `joinedAt` above without using `edges { ... }`. - -### Edges - -Edges are _like_ join tables in that they can expose relationship metadata between a parent object and its children. - -For example, let's say that someone may be the member of _several_ teams. You would make a join table in the database (eg, `team_memberships`) which connect people to each of the teams they're on. This join table could also include information about _how_ that person is related to the team: when they joined, what role they have, etc. - -Edges can reveal this information about the relationship, for example: - -```ruby -team { - # this is the team name - name - - members(first: 10) { - edges { - # this is a team membership - joinedAt - role - - node { - # this is the person on the team - name - } - } - } -} -``` - -So, edges really help when the _relationship_ between two objects has special data associated with it. If you use a join table, that's a clue that you might use a custom edge to model the relationship. diff --git a/vendor/gems/graphql/guides/pagination/cursors.md b/vendor/gems/graphql/guides/pagination/cursors.md deleted file mode 100644 index d808a3850d1..00000000000 --- a/vendor/gems/graphql/guides/pagination/cursors.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Pagination -title: Cursors -desc: Advancing through lists with opaque cursors -index: 4 ---- - -Connections use _cursors_ to advance through paginated lists. A cursor is an opaque string that indicates a specific point in this. - -Here, _opaque_ means that the string has no meaning except its value. cursors shouldn't be decoded, reverse-engineered, or generated ad-hoc. The only guarantee of a cursor is that, after you retrieve one, you can use it to request subsequent or preceding items in the list. - -Although cursors can be tricky, they were chosen for Relay-style connections because they can be implemented in stable and high-performing ways. - -## Customizing Cursors - -By default, cursors are encoded in base64 to make them opaque to a human client. You can specify a custom encoder with `Schema.cursor_encoder`. The value should be an object which responds to `.encode(plain_text, nonce:)` and `.decode(encoded_text, nonce: false)`. - -For example, to use URL-safe base-64 encoding: - -```ruby -module URLSafeBase64Encoder - def self.encode(txt, nonce: false) - Base64.urlsafe_encode64(txt) - end - - def self.decode(txt, nonce: false) - Base64.urlsafe_decode64(txt) - end -end - -class MySchema < GraphQL::Schema - # ... - cursor_encoder(URLSafeBase64Encoder) -end -``` - -Now, all connections will use URL-safe base-64 encoding. - -From a connection instance, the `cursor_encoders` methods are available via {{ "GraphQL::Pagination::Connection#encode" | api_doc }} and {{ "GraphQL::Pagination::Connection#decode" | api_doc }} diff --git a/vendor/gems/graphql/guides/pagination/custom_connections.md b/vendor/gems/graphql/guides/pagination/custom_connections.md deleted file mode 100644 index d10058b07c5..00000000000 --- a/vendor/gems/graphql/guides/pagination/custom_connections.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Pagination -title: Custom Connections -desc: Building & using cursor-based connections in GraphQL-Ruby -index: 3 ---- - -GraphQL-Ruby ships with built-in connection support for ActiveRecord, Sequel, Mongoid, and Ruby Arrays. You can read more in the {% internal_link "Using Connections", "/pagination/using_connections" %} guide. - -When you want to serve a connection based on your _own_ data object, you can create a custom connection. The implementation will have several components: - -- The __application object__ -- the list of items that you want to paginate in GraphQL -- The __connection wrapper__ which wraps the application object and implements methods used by GraphQL -- The __connection type__, a GraphQL object type which implements the connection contract - -For this example, we'll imagine that your application communicates with an external search engine, and expresses all search results with a `SearchEngine::Result` class. (There isn't _really_ any class like this; it's an arbitrary example of an application-specific collection of items.) - -## Application Object - -In Ruby, _everything_ is an object, and that includes _lists of objects_. For example, we think of an Array as a _list_ of objects, but Arrays are also objects in their own right. - -Some list objects have very fancy implementations. Think of an `ActiveRecord::Relation`: it gathers up the parts of a SQL query, and at the right moment, it dispatches a call to your database to fetch the objects in the list. An `ActiveRecord::Relation` is also a list object. - -Your application probably has other list objects that you want to paginate via GraphQL connections. For example, you might show a user some search results, or a list of files from a fileserver. Those lists are modeled with _list objects_, and those list objects can be wrapped with connection wrappers. - -## Connection Wrapper - -A connection wrapper is an adapter between a plain-Ruby list object (like an Array, Relation, or something application-specific, like `SearchEngine::Result`) and a GraphQL connection type. The connection wrapper implements methods which the GraphQL connection type requires, and it implements those methods based on the underlying list object. - -You can extend {{ "GraphQL::Pagination::Connection" | api_doc }} to get started on a custom connection wrapper, for example: - -```ruby -# app/graphql/connections/search_results_connection.rb -class Connections::SearchResultsConnection < GraphQL::Pagination::Connection - # implementation here ... -end -``` - -The methods you must implement are: - -- `#nodes`, which returns a paginated slice of `@items` based on the given arguments -- `#has_next_page`, which returns `true` if there are items after the ones in `#nodes` -- `#has_previous_page`, which returns `true` if there are items before the ones in `#nodes` -- `#cursor_for(item)`, which returns a String to serve as the cursor for `item` - -How to implement these methods (efficiently!) depends on your backend and how you communicate with it. For inspiration, you can see the built-in connections: - -- {{ "GraphQL::Pagination::ArrayConnection" | api_doc }} -- {{ "GraphQL::Pagination::ActiveRecordRelationConnection" | api_doc }} -- {{ "GraphQL::Pagination::SequelDatasetConnection" | api_doc }} -- {{ "GraphQL::Pagination::MongoidRelationConnection" | api_doc }} - -### Using a Custom Connection - -To integrate your custom connection wrapper with GraphQL, you have two options: - -- Map the wrapper to a list object at the _schema level_, so that those list objects are _always_ automatically wrapped by your wrapper; OR -- Use the wrapper manually in resolve methods, to override any automatic mapping - -The first case is very convenient, and the second case makes it possible to customize connections for specific situations. - -To __map the wrapper to a class of objects__, add it to your schema: - -```ruby -class MySchema < GraphQL::Schema - # Hook up a custom wrapper - connections.add(SearchEngine::Result, Connections::SearchResultsConnection) -end -``` - -Now, any time a field returns an instance of `SearchEngine::Result`, it will be wrapped with `Connections::SearchResultsConnection` - -Alternatively, you can apply a connection wrapper on a case-by-case basis by applying it during the resolver (method or {{ "GraphQL::Schema::Resolver" | api_doc }}): - -```ruby -field :search, Types::SearchResult.connection_type, null: false do - argument :query, String -end - -def search(query:) - search = SearchEngine::Search.new(query: query, viewer: context[:current_user]) - results = search.results - # Apply the connection wrapper and return it - Connections::SearchResultsConnection.new(results) -end -``` - -GraphQL-Ruby will use the provided connection wrapper in that case. You can use this fine-grained approach to handle special cases or implement performance optimizations. - -## Connection Type - -Connection types are GraphQL object types which comply to the [Relay connection specification](https://relay.dev/graphql/connections.htm). GraphQL-Ruby ships with some tools to help you create those object types: - -- {{ "GraphQL::Types::Relay::BaseConnection" | api_doc }} and {{ "GraphQL::Types::Relay::BaseEdge" | api_doc }} are example implementations of the spec. They don't inherit from your application's base object class though, so you might not be able to use them out of the box. -- Type classes respond to `.connection_type` which returns a generated connection type based on that class. By default, it inherits from the provided `GraphQL::Types::Relay::BaseConnection`, but you can override that by setting `connection_type_class(Types::MyBaseConnectionObject)` in your base classes. - -For example, you could implement a base connection class: - -```ruby -class Types::BaseConnectionObject < Types::BaseObject - # implement based on `GraphQL::Types::Relay::BaseConnection`, etc -end -``` - -Then hook it up to your base classes: - -```ruby -class Types::BaseObject < GraphQL::Schema::Object - # ... - connection_type_class(Types::BaseConnectionObject) -end - -class Types::BaseUnion < GraphQL::Schema::Union - connection_type_class(Types::BaseConnectionObject) -end - -module Types::BaseInterface - include GraphQL::Schema::Interface - - connection_type_class(Types::BaseConnectionObject) -end -``` - - -Then, when defining fields, you could use `.connection_type` to use your connection class hierarchy: - -```ruby -field :posts, Types::Post.connection_type, null: false -``` - -(Those fields get `connection: true` by default, because the generated connection type's name ends in `*Connection`.) diff --git a/vendor/gems/graphql/guides/pagination/overview.md b/vendor/gems/graphql/guides/pagination/overview.md deleted file mode 100644 index 0828af92ed1..00000000000 --- a/vendor/gems/graphql/guides/pagination/overview.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Pagination -title: Overview -desc: Introduction to pagination in GraphQL -index: 0 -redirect_from: - - /relay/connections/ ---- - - -GraphQL-Ruby ships with several implementations of Relay's "connection"-style pagination. You can familiarize yourself with connections in {% internal_link "Connection Concepts", "/pagination/connection_concepts" %} and see how to use them in {% internal_link "Using Connections", "/pagination/using_connections" %}. It also supports custom connection implementations and type definitions, which you can explore in the {% internal_link "Custom Connections", "/pagination/custom_connections" %} guide. - -GraphQL has its own [great pagination docs](https://graphql.org/learn/pagination/) for further reading. diff --git a/vendor/gems/graphql/guides/pagination/stable_relation_connections.md b/vendor/gems/graphql/guides/pagination/stable_relation_connections.md deleted file mode 100644 index 3c20dafa285..00000000000 --- a/vendor/gems/graphql/guides/pagination/stable_relation_connections.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Pagination -title: Stable Relation Connections -desc: Advanced pagination for ActiveRecord -index: 4 -pro: true ---- - -`GraphQL::Pro` includes a mechanism for serving _stable_ connections for `ActiveRecord::Relation`s based on column values. If objects are created or destroyed during pagination, the list of items won't be disrupted. - -These connection implementations are database-specific so that they can build proper queries with regard to `NULL` handling. (Postgres treats nulls as _larger_ than other values while MySQL and SQLite treat them as _smaller_ than other values.) - -## What's the difference? - -The default {{ "GraphQL::Pagination::ActiveRecordRelationConnection" | api_doc }} (which turns an `ActiveRecord::Relation` into a GraphQL-ready connection) uses _offset_ as a cursor. This naive approach is sufficient for many cases, but it's subject to a specific set of bugs. - -Let's say you're looking at the second page of 10 items (`LIMIT 10 OFFSET 10`). During that time, one of the items on page 1 is deleted. When you navigate to page 3 (`LIMIT 10 OFFSET 20`), you'll actually _miss_ one item. The entire list shifted "up" one position when a previous item was deleted. - -To solve this bug, we should use a _value_ to page through items (instead of _offset_). For example, if items are ordered by `id`, use the `id` for pagination: - -```sql -LIMIT 10 -- page 1 -WHERE id > :last_id LIMIT 10 -- page 2 -``` - -This way, even when items are added or removed, pagination will continue without interruption. - -For more information about this issue, see ["Pagination: You're (Probably) Doing It Wrong"](https://coderwall.com/p/lkcaag/pagination-you-re-probably-doing-it-wrong). - -## Installation - -You can use a stable connection for _all_ `ActiveRecord::Relation`s by installing it at the schema level: - -```ruby -class MyAppSchema < GraphQL::Schema - # Hook up the stable connection that matches your database - connections.add(ActiveRecord::Relation, GraphQL::Pro::PostgresStableRelationConnection) - # Or... - # connections.add(ActiveRecord::Relation, GraphQL::Pro::MySQLStableRelationConnection) - # connections.add(ActiveRecord::Relation, GraphQL::Pro::SqliteStableRelationConnection) -end -``` - -Alternatively, you can apply the stable connection wrapper on a _field-by-field_ basis. For example: - -```ruby -field :items, Types::ItemType.connection_type, null: false - -def items - # Build an ActiveRecord::Relation - relation = Item.all - # And wrap it with a connection implementation, then return the connection - GraphQL::Pro::MySQLStableRelationConnection.new(relation) -end -``` - -That way, you can adopt stable cursors bit-by-bit. (See below for [backwards compatibility](#backwards-compatibility) notes.) - -Similarly, if you enable stable connections for the whole schema, you can wrap _specific_ relations with `GraphQL::Pagination::ActiveRecordRelationConnection` when you want to use index-based cursors. (This is handy for relations whose ordering is too complicated for cursor generation.) - -## Implementation Notes - -Keep these points in mind when using value-based cursors: - -- For a given `ActiveRecord::Relation`, only columns of that specific model can be used in pagination. (This is because column names are turned into `WHERE` conditions.) -- The connection may add an additional `primary_key` ordering to ensure that the cursor value is unique. This behavior is inspired by `Relation#reverse_order` which also assumes that `primary_key` is the default sort. -- The connection will add fields to the relation's `SELECT` clause, so that cursors can be reliably constructed from the database results. - -## Grouped Relations - -When using a grouped `ActiveRecord::Relation`, include a unique ID in your sort to ensure that each row in the result has a unique cursor. For example: - -```ruby -# Bad: If two results have the same `max(price)`, -# they will be identical from a pagination perspective: -Products.select("max(price) as price").group("category_id").order("price") - -# Good: `category_id` is used to disambiguate any results with the same price: -Products.select("max(price) as price").group("category_id").order("price, category_id") -``` - -For ungrouped relations, this issue is handled automatically by adding the model's `primary_key` to the order values. - -If you provide an unordered, grouped relation, `GraphQL::Pro::RelationConnection::InvalidRelationError` will be raised because an unordered relation _cannot_ be paginated in a stable way. - -## Backwards Compatibility - -`GraphQL::Pro`'s stable relation connection is backwards-compatible. If it receives an offset-based cursor, it uses that cursor for the next resolution, then returns value-based cursors in the next result. - -## ActiveRecord Versions - -Stable relation connections support ActiveRecord `>= 4.1.0`. diff --git a/vendor/gems/graphql/guides/pagination/using_connections.md b/vendor/gems/graphql/guides/pagination/using_connections.md deleted file mode 100644 index 9bd9e722833..00000000000 --- a/vendor/gems/graphql/guides/pagination/using_connections.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Pagination -title: Using Connections -desc: Pagination with GraphQL-Ruby's built-in connections -index: 2 ---- - -GraphQL-Ruby ships with a few implementations of the {% internal_link "connection pattern", "pagination/connection_concepts" %} that you can use out of the box. They support Ruby Arrays, Mongoid, Sequel, and ActiveRecord. - -Additionally, connections allow you to limit the number of items returned with [`max_page_size`](#max-page-size) and set the default number of items returned with [`default_page_size`](#default-page-size). - -## Make Connection Fields - -Use `.connection_type` to generate a connection type for paginating over objects of a given type: - -```ruby -field :items, Types::ItemType.connection_type, null: false -``` - -The generated return type will be called `ItemConnection`. Since it ends in `*Connection`, the `field(...)` will automatically be configured with `connection: true`. If the connection type's name doesn't end in `Connection`, you have to add that configuration yourself: - -```ruby -# here's a custom type whose name doesn't end in "Connection", so `connection: true` is required: -field :items, Types::ItemConnectionPage, null: false, connection: true -``` - -The field will be given some arguments by default: `first`, `last`, `after`, and `before`. - -### Opting out of default connection handling - -To opt out of GraphQL-Ruby's default connection handling, add `connection: false` to the field definition: - -```diff -- field :items, Types::ItemType.connection_type, null: false -+ field :items, Types::ItemType.connection_type, null: false, connection: false -``` - -Then, add any arguments you want (`first`, `last`, `after`, `before`) and make sure that your resolver returns an object that can fulfill the fields of the configured return type. - -## Return Collections - -With connection fields, you can return collection objects from fields or resolvers: - -```ruby -def items - object.items # => eg, returns an ActiveRecord Relation -end -``` - -The collection object (Array, Mongoid relation, Sequel dataset, ActiveRecord relation) will be automatically paginated with the provided arguments. Cursors will be generated based on the offset of nodes in the collection. - -## Make Custom Connections - -If you want to paginate something that _isn't_ supported out-of-the-box, you can implement your own pagination wrapper and hook it up to GraphQL-Ruby. Read more in {% internal_link "Custom Connections", "/pagination/custom_connections" %}. - -## Special Cases - -Sometimes, you have _one collection_ that needs special handling, unlike other instances of its class. For cases like this, you can manually apply the connection wrapper in the resolver. For example: - -```ruby -def items - # Get the ActiveRecord relation to paginate - relation = object.items - # Apply a custom wrapper - Connections::ItemsConnection.new(relation) -end -``` - -This way, you can handle this _particular_ `relation` with custom code. - -## Max Page Size - -You can apply `max_page_size` to limit the number of items returned and queried from the database, regardless of what the client requests. - -- __For the whole schema__, you can add it to your schema definition: - -```ruby -class MyAppSchema < GraphQL::Schema - default_max_page_size 50 -end -``` - - At runtime, that value will be applied to _every_ connection, unless an override is provided as described below. - -- __For a given field__, add it to the field definition with a keyword: - -```ruby -field :items, Item.connection_type, null: false, - max_page_size: 25 -``` - -- __Dynamically__, you can add `max_page_size:` when you apply custom connection wrappers: - -```ruby -def items - relation = object.items - Connections::ItemsConnection.new(relation, max_page_size: 10) -end -``` - -To _remove_ a `max_page_size` setting, you can pass `nil`. That will allow unbounded collections to be returned to clients. - -## Default Page Size - -You can apply `default_page_size` to limit the number of items returned and queried from the database when no `first` or `last` is provided. - -- __For the whole schema__, you can add it to your schema definition: - -```ruby -class MyAppSchema < GraphQL::Schema - default_page_size 50 -end -``` - - At runtime, that value will be applied to _every_ connection, unless an override is provided as described below. - -- __For a given field__, add it to the field definition with a keyword: - -```ruby -field :items, Item.connection_type, null: false, - default_page_size: 25 -``` - -- __Dynamically__, you can add `default_page_size:` when you apply custom connection wrappers: - -```ruby -def items - relation = object.items - Connections::ItemsConnection.new(relation, default_page_size: 10) -end -``` - -If `max_page_size` is set and `default_page_size` is higher than it, the `default_page_size` will be clamped down to match `max_page_size`. If both `default_page_size` and `max_page_size` are set to `nil`, unbounded collections will be returned. diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-1.26.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-1.26.1.txt deleted file mode 100644 index 33f59a79862..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-1.26.1.txt +++ /dev/null @@ -1 +0,0 @@ -33537b3b1a5afef64ea25817d607afae983de531902c18dc5c99bd4b52538b5b03cb3f7032dc3a53b5690ef85a7abd7a0422cdead9a871a187b3bc755ba4062c diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.0.txt deleted file mode 100644 index 44e71f5782f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.0.txt +++ /dev/null @@ -1 +0,0 @@ -960ec05f3ef88fff70bef68ca02fa3d37b512f9ad668345a903766776aaca08fba52795a9625cde39f9c2080ae13ec6c76b8757f05c26ccdfb27edcc69112ca8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.1.txt deleted file mode 100644 index ebfd63f26e5..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.0.1.txt +++ /dev/null @@ -1 +0,0 @@ -37364b0f6dbb5e97203e30172d3ed21fdd3c90f78de3e93479d15008d00b170d4e1656dc86b686de69fe060f4573d743f341a1ee55eeb793426694619ffc3cb0 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.0.txt deleted file mode 100644 index d4dde131a6f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.0.txt +++ /dev/null @@ -1 +0,0 @@ -9de179b3c8a2f374c112146cbad6436cc72b553f2a7fb289171af50201137609545636aa5fa972c6fd19f371f8414c9339a708394169dc1582ebef04c68f9ecc diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.1.txt deleted file mode 100644 index 80661dc017d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.1.txt +++ /dev/null @@ -1 +0,0 @@ -2438c01b8bfd1ad23ff41f3dffd68b2a3cf150ca12f2dd45e2ba06fb063e6344dfca0d38e3d5f55693e050d9b44738e563abb484f24799fec80c6b78662075cf diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.10.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.10.txt deleted file mode 100644 index 923dafa0b36..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.10.txt +++ /dev/null @@ -1 +0,0 @@ -4fd53a85c197db33fdf2a73593a016dd687bd9e8a5e9eb01e8e09594bbfae2cd7f5a27a17a7555603e2ff14b779f26362091e478b37737b3ed8a1a7cb8b0fce2 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.11.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.11.txt deleted file mode 100644 index 281bb84c3a8..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.11.txt +++ /dev/null @@ -1 +0,0 @@ -af885c3e261efe1ce3d14861d46d2150de3195d71debbfd0ac0de866eb7de45b6f5080a3686d8750522cd8f0eb499c6b1abb2e54bc9d449c62d9efe678440127 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.12.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.12.txt deleted file mode 100644 index 71c345d8204..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.12.txt +++ /dev/null @@ -1 +0,0 @@ -dd4d30115510d1767dcbce8a64e91c241d48a4f6b4c9d57df29e334143431958783a17ed20cf7601675a1957b000c200dca4b9a649b1c433dbf879e5344d1c67 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.13.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.13.txt deleted file mode 100644 index b254a248f6d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.13.txt +++ /dev/null @@ -1 +0,0 @@ -545dc5c0ec289b6bec7c2ab59bf41708885f1e3a746dfd6b070d3d87eb82e4107ee154110b9340896144875384dca6ee7a4a5676b3301ed76884308d389fd96e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.14.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.14.txt deleted file mode 100644 index 1bf28d8d196..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.14.txt +++ /dev/null @@ -1 +0,0 @@ -90f5193f787414b85ae649881d252dc0b0c1b6a880920ed117cea40aefa21350c0d0fc91e7a5a980c4958a5fdd7932874db5df966db81365a3882631fbae4eb5 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.2.txt deleted file mode 100644 index 487a2e22fb5..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.2.txt +++ /dev/null @@ -1 +0,0 @@ -c512384b9d0482a44925a0e47026b6cd48f48282c3e22f9bb90fad6452dd5209f394752b0ecfe1e5ea41cd5f848325fd4fb9c68c54d63c36774fa568cfc93c01 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.3.txt deleted file mode 100644 index af8efb645b7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.3.txt +++ /dev/null @@ -1 +0,0 @@ -4c46c862544ad3bd836949a57e7dc272a4bd5e6a71757879b6de8729d063c866d8ff2f2f6959001fc1120c4f748e25379039fa855fd9b1b717ae6ad5c43ed02f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.4.txt deleted file mode 100644 index 920cffc4170..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.4.txt +++ /dev/null @@ -1 +0,0 @@ -8320d4f89f1986c23e92cfe1dae0fad37c7ca6d7b39e5f05d110e7167aa3d0ce6ae995d514986941caa90f76aa3fa96679810e7fe3fe5232500c954c5d3645a3 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.5.txt deleted file mode 100644 index 2936a79b4c2..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.5.txt +++ /dev/null @@ -1 +0,0 @@ -6c6248cf76ea0ca250f9e0ff587e6475a51c3fa3b4cdb6a517a35baf9226d87be2b009f073a952ac33a19bbe46cdf44889466b160dfa20fc24d8a365bab4238b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.6.txt deleted file mode 100644 index 198b76c1596..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.6.txt +++ /dev/null @@ -1 +0,0 @@ -875b3f09b699e006733a2ffee31379db3f775b79408b35726a24c8a00386b1a7fcfafc56c57163a71dadafa22fab24aa7dec5608feae1cce8c59bb652e60e439 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.7.txt deleted file mode 100644 index 908fe3aca76..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.7.txt +++ /dev/null @@ -1 +0,0 @@ -cc841f4c0365ddfd65b40b1af2c93217f6c5ab531331fa0e5e84e70dfbd7f997f20db2a5d741da7652c3efcb5dde42987668ed29387a378db22234b2aa18153a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.8.txt deleted file mode 100644 index cc2b312e954..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.8.txt +++ /dev/null @@ -1 +0,0 @@ -66710fed7209fef5223602a3d8722c70c83f79540dff9752b42caad6ab53be7d74cd304dfc7e789ade3972d590ed3198aa42ea8e89dcb46c929fa1b9d3a997fa diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.9.txt deleted file mode 100644 index e959c61a0ff..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.1.9.txt +++ /dev/null @@ -1 +0,0 @@ -0bfb112eaca92cb98adb4beffed43f22a68993dacf67bf3631bec05f30b29954181629b5ab11d429619d75ec76c7295ae74f7eeba9940aadec2cdef7e5d96a95 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.2.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.2.0.txt deleted file mode 100644 index 6bf73f68b35..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.2.0.txt +++ /dev/null @@ -1 +0,0 @@ -a4c9d7118ab03cb27bf49c97b0fe95e5695ace646bf9f459ec327bc18190dc9bcdcc772438873a7a7531213ba4d8d10af151e195a4b8971342230e5ad2d92346 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.0.txt deleted file mode 100644 index ae841586d41..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.0.txt +++ /dev/null @@ -1 +0,0 @@ -b1a909bf8582e6c253bdb901f9865634dd970519529eb34021c138b6cbfe76c1573756899d7966c16f32878dd11272d6f6c8be7abde3654c9a5969e93af62b0a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.1.txt deleted file mode 100644 index 25911b14389..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.1.txt +++ /dev/null @@ -1 +0,0 @@ -6b23b11331e345a757ba31edb3af77e3174996603162dd1fb698971886545ff8344dfc39f21c6096d2206f7be1d7f529efbddc6f86cbe15ddf81232e4b631603 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.2.txt deleted file mode 100644 index 221cda64d42..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.2.txt +++ /dev/null @@ -1 +0,0 @@ -cc215bc8fee73b02c40ce29070244a31072f42e8a68a3b7038ef449572eab977180d13bc61fa3cbd02cc6855fadaa05bcfc6db033b517e8b8136960a188bf01a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.3.txt deleted file mode 100644 index 252d760ae44..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.3.txt +++ /dev/null @@ -1 +0,0 @@ -019e92e06ffab9cbecde167c72321136e0826db4bd4ae7a74c33b0d915e6b253c847f688f3efdc4a7f27428f0cd36b2ffa34fa02769ff3a431e101b88fcd7533 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.4.txt deleted file mode 100644 index 4e6db32eda8..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.3.4.txt +++ /dev/null @@ -1 +0,0 @@ -e08061b7eae9ac0deb15a4d7361deddc9cd881d89931652806fb4a2a544c5b29e58245f850322c228dbbd3000920ce94d2de2bc2882cec29dc0c46698943058f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.0.txt deleted file mode 100644 index 923811547b3..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.0.txt +++ /dev/null @@ -1 +0,0 @@ -d2152d80aee8cd1699c4e64e28329c51ddee38ea6ea318f89c6e9bc996fbd68d4db28831fcedb5583ba92d499852dd7fc8ca99583376eacd28f00c908fec8f28 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.1.txt deleted file mode 100644 index 8bfb1504aea..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.1.txt +++ /dev/null @@ -1 +0,0 @@ -e0a1bfe31eebec9523faf1710ef1887cf15bac93a9476778804958b646f9664bdbf304000cc568ccf87ca7d450aba646081bee295c998fd81491156890a1d092 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.2.txt deleted file mode 100644 index 4cdc2d1267e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.4.2.txt +++ /dev/null @@ -1 +0,0 @@ -cce6aca47058577462d4bf51c02b578c6aaf7bb47335e3435129efc9662e37e13756542d40ce728702a373d4e93416bb72202dfa9781bcc3de61c81b29471a5a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.0.txt deleted file mode 100644 index d286673b9fe..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.0.txt +++ /dev/null @@ -1 +0,0 @@ -753347081830d3007f568b04007cb401353130f3964ed5d75a65898b1751f88b47d571e43357e09173b6ba10ec954c946fe74adcf64b29a5fa799c50ec704a7a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.1.txt deleted file mode 100644 index d56f308b5a6..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.1.txt +++ /dev/null @@ -1 +0,0 @@ -f83113767fb51f584f0d27b34f44fccc8ca05fab61e150bd6dd9098c9b3aef41751c4337ca994e2b78486fdd71c623a6d24da985d3bcee983a33c7a798a25e2d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.2.txt deleted file mode 100644 index ed1ad1ee061..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.2.txt +++ /dev/null @@ -1 +0,0 @@ -e56b5290ed8798c67ca8e2fb1d6af8895570893e4c71e7958f3051dfa24617ec7772d325e44f54dc320f2963e802b28ba8036f26a4932971c5b9a2181bd98c6b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.3.txt deleted file mode 100644 index dee10ad5e17..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.3.txt +++ /dev/null @@ -1 +0,0 @@ -92d3392668d3365d61d31d1906f19e54b4cc330383a2876997ed3cefa787cbcd51f009b6a30181f0631ee6a80a3402549f44d4905145aadeb7abe421167daa6c diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.4.txt deleted file mode 100644 index a437383dc37..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.4.txt +++ /dev/null @@ -1 +0,0 @@ -77201981e1495aa32181b36ca444b020860f83e6a29d8a749beca11cb02d4ca8e25fdfad8dea1c0365b8616661d39452de72d861f0460828db03151a627fb7ee diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.5.txt deleted file mode 100644 index df90dc1de44..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.5.txt +++ /dev/null @@ -1 +0,0 @@ -93269e41b9069a070584b77722d412c3a43251deabe013090874a657a07f1180aec5d5a90091fa8cac7885147d83e9be0701f6e3bb52d3bc17c4759069882bb4 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.6.txt deleted file mode 100644 index cda1481101a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-enterprise-1.5.6.txt +++ /dev/null @@ -1 +0,0 @@ -4e7776668f4e8f7897e41a2d8844c1684e914c477f1eb8f4aa204db9dca56c219f0a6d631e253b23d951d15a8e8bf106f786925655491a1f562ac270561f8f81 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.0.txt deleted file mode 100644 index 0a61231f38f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.0.txt +++ /dev/null @@ -1 +0,0 @@ -4de5bbda7a3e9ed29eb3cdb25a0cea1a91f57dd34bb73827628f49a605cec244583cf0b9b6d9dfab932573f99c2ada4c98f4ef569fc82fa32b046e0ffb3e976c diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.1.txt deleted file mode 100644 index 498689076dd..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.1.txt +++ /dev/null @@ -1 +0,0 @@ -4951e730ead0e493c21ad337af84eb99551a1e4c66b35c8ebfabc000b1bc59d13c4c45c3081fa68719f56522ab1f1bc4c4258e634c0da3414cb82041ed2e122f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.2.txt deleted file mode 100644 index 1c73dd7a4be..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.2.txt +++ /dev/null @@ -1 +0,0 @@ -bc165db6aaf32d51e710626eb2baa36851e0d9ca4ff88676d0fdd9120264e599e326dd1602713197308d010392a46f7f3472e805b5a1677af18887e3e7b0bdaa diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.3.txt deleted file mode 100644 index 5a678cd82cc..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.3.txt +++ /dev/null @@ -1 +0,0 @@ -077630f850706f87104890dd7ccaca7fc01e44f73c2f5bf5750bfcb5b15a4b58e2028aec04ed3066abbeecf7863af399bfbb4ad1bbc7aea9c034faf949118b5e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.4.txt deleted file mode 100644 index 9257e7600bd..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.0.4.txt +++ /dev/null @@ -1 +0,0 @@ -e9be770ff2117a4692a11f5b5c8e5e9f5095bd7aa7ca24a9e085dd278a548caed28396d100302ec52ab295aafd7fab849c83420a49311034779ec30eb3b4a232 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.0.txt deleted file mode 100644 index be24807af48..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.0.txt +++ /dev/null @@ -1 +0,0 @@ -c934c912626c926921a4e5260edb3e1d108d3bd3b680137a3077a0baa6e802966399fcbb52c3b2d60333db9d8a5e508a4c8e3344ff2ef05555aeb76967537be7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.1.txt deleted file mode 100644 index 80dd08feb0d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.1.1.txt +++ /dev/null @@ -1 +0,0 @@ -deca840ea6676077e39608a63b3d33cbc032f69ab147d2a1c679da8852e087204b3c6b643cde3bde411d7d1bb7cf79bbc21b504fd08940ea5b6cd7cd868c3e12 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.0.txt deleted file mode 100644 index 68ce43adbaa..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.0.txt +++ /dev/null @@ -1 +0,0 @@ -70b8ebe4f3b0c99a16a544aa26bf0d9c8605c4e1aed1fd9374c93f7818bc08860690f60b3f28f48a6cfa8c5864312a2ea94b7c8caead40f6f609f07739d55ec7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.1.txt deleted file mode 100644 index 42537745d2e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.1.txt +++ /dev/null @@ -1 +0,0 @@ -d061d329ed1518519382f182ffc48ccbaa9cf7a2abea701a563477bfd3dd39418accc67449d6891afff6482c0c14f05a7a459ac42e801dac6fd1c91e6a0de974 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.2.txt deleted file mode 100644 index 31b8d2a354b..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.2.txt +++ /dev/null @@ -1 +0,0 @@ -6f8c9be62438f95aa146e302c7234d55ea592fda72d483873b20140412cc912b3e1dba8789451448f600c15c38f40989bc34c42aa18ceb1f796cab50c774afb5 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.3.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.4.txt deleted file mode 100644 index aa1c4369dc9..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.4.txt +++ /dev/null @@ -1 +0,0 @@ -6e900c64b27dbeee3180e5e624dd8eff409305bddd95bb651e8105a00a699bbcf9dc321df87c815372221e02b332ffbb9512800a60e0daf4ed323b181f4e681a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.5.txt deleted file mode 100644 index 30c9450f55a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.5.txt +++ /dev/null @@ -1 +0,0 @@ -aff26d02fb3eb8751f763b12e4085c24e7efca383f1117b3b5ad119c328334f7f3abfdb294f9fc3f33a466c031f0ed856bf459d979eac93e7eff4f0f76610774 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.6.txt deleted file mode 100644 index 71aa159a860..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.6.txt +++ /dev/null @@ -1 +0,0 @@ -e304f7f23f6e940159d34f0d871054070a8235d657c141157072655d00787e9182629280509012160c757dcd1afd12982875dc0197e381393051d0798293cdb2 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.7.txt deleted file mode 100644 index cee219a369c..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.7.txt +++ /dev/null @@ -1 +0,0 @@ -08d220a1b2580d9e763238d553838b692f26262be563b685e9ff69e02a6c03fe125647bd6b193256a4dab58aadeb45921ad986b51ad0d019e69b77e8ecc61650 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.8.txt deleted file mode 100644 index 8efabf3a0f7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.10.8.txt +++ /dev/null @@ -1 +0,0 @@ -de7618f67acf9016e74bde682a3fbcac61a422f6e61d06f85eebcf0aabb10111a55d2e05054bd68841909e0a53c73104c369c3e919447329ad6e1aaa2e8e7f26 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.11.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.11.0.txt deleted file mode 100644 index de457fc9c7f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.11.0.txt +++ /dev/null @@ -1 +0,0 @@ -8f856d904af40ac0424743a1a40bad96408908202c031d7f0d67a74a085c000bb5db17e9e1c97dfbc79095b4006f82d18ca6f67fe830a655895ebda0e614ce71 \ No newline at end of file diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.0.txt deleted file mode 100644 index 3e09c3e79cf..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.0.txt +++ /dev/null @@ -1 +0,0 @@ -75208ae66c220aa3d275a3765a7e15e9f589efb4fa7135fee57b33ecd13347f12ce8b4f5207b8457693a9b8f68eab5f11db746afbf2d0eba436396faf5502421 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.1.txt deleted file mode 100644 index cd52d81390e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.1.txt +++ /dev/null @@ -1 +0,0 @@ -0da30804e51647104a2b0be0bc9c5ca3ed43d8c64db33ae9afd7f7b73923a9243824e2d44a37cbb47372ec6a5c6b5aa54765d481dc51b885267ba7ab4672484d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.2.txt deleted file mode 100644 index 7c89a594821..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.12.2.txt +++ /dev/null @@ -1 +0,0 @@ -2a12902ed9321419fedf166021e50fdbfd695ca7b6245bf10a4354f95f38ac60de7bc4465ec873d6ed1af7adf0f631308739de799243e88a893f7527b8df9df9 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.0.txt deleted file mode 100644 index f2bef5bd51f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.0.txt +++ /dev/null @@ -1 +0,0 @@ -5446303fdfad4e2cb2bb47535d60bdf11a3e039c7b1edf8b02f9e8218ea1b87367f59726e6ad227e32d74a51f39f776b36d5f0fe59de1e72475a38376509cfa6 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.1.txt deleted file mode 100644 index ed2be387f52..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.1.txt +++ /dev/null @@ -1 +0,0 @@ -79f699934f0e9da8f328256b5c67144046341011e0629234d0391f206a807a00f457c71e00eb03b8fd4f0779156d189b54b56f6d7525904acb5ea19d061fceaf diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.2.txt deleted file mode 100644 index f934520e579..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.2.txt +++ /dev/null @@ -1 +0,0 @@ -e1aa0bc146fa154e3fb3655fd8e96e4fa7adc27b165ebf2c56880f64ac1938e3ba8ce8e7c7c02e921d54255ddfe9f7e44ecc3fad8675eaa0b1055813e989c2ef diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.3.txt deleted file mode 100644 index 3590bc487a3..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.3.txt +++ /dev/null @@ -1 +0,0 @@ -a1f2ed08b503cf48d8fe7e0ac7f09d194e5f215de1ddce9acad8a0a6e9abc0865d62583b84af9e2a18a8ede3caa47d8d0b148caca986813bd4d03ff4f5c9ff5e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.4.txt deleted file mode 100644 index debafa45d35..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.4.txt +++ /dev/null @@ -1 +0,0 @@ -3e1684ac0a06712bb565d0c4eff852f8240cee55824b75899d044db2815ae60c20fd367686a69e0b4f3bdd9764da18691d8af736561a13303d8be5cb9648b56a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.5.txt deleted file mode 100644 index 40fc1377380..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.5.txt +++ /dev/null @@ -1 +0,0 @@ -d481fb6595d6b0cf5a5cf0fe2c29367dd4687c6fa8f7a12373b896dca6e0b4464ccaa6a46d8f8dcabc247ce0b1818161091b18f39ff14b8d2bbbc72769a0df23 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.6.txt deleted file mode 100644 index c725de3a08e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.13.6.txt +++ /dev/null @@ -1 +0,0 @@ -e380c037088472eac1d71a64b419dc917eadbcd5e91d0273cf97d394d9833cbd93070d9ae3f8227e401a953a6982571713a3672df9b7d8ca37f1f216b9d3011a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.0.txt deleted file mode 100644 index b1d82d5d2bf..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.0.txt +++ /dev/null @@ -1 +0,0 @@ -22543434eb5bbb551f8a47fa2fa7f41f9210128b34e3bb8d88e47faf02fed606c9269d493fd3702cd8ae9861998ad967fff7095775a0fb8af8aa012b9dda06f2 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.1.txt deleted file mode 100644 index 73e10e6b5f9..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.14.1.txt +++ /dev/null @@ -1 +0,0 @@ -2c41c982e963f91f777e9b7c31dffc62d30e10cc741affdc168d94233016e3b07ae8ebe3c9631ea57b7a2433fd353f2087557dcfe2394d64e379485a20723893 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.0.txt deleted file mode 100644 index ccee9816e6a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.0.txt +++ /dev/null @@ -1 +0,0 @@ -06757f5a7d878584a29864cc3790e9687d119aaed5fe3c66afdf2ff498ceaba63c0bf9bc4fe81fdaad2b5e933acd7a889c33cec825abb673f6f624c16c5a110d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.2.txt deleted file mode 100644 index 85114034020..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.2.txt +++ /dev/null @@ -1 +0,0 @@ -ec02be5c1b0e7defc6686d42d3033397a144de006ef7ae9ce0168d3aee459529edab8ca357a95f204010bfa7cbd14b7a0c7049892cad968ff2ede267c5cc5224 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.3.txt deleted file mode 100644 index e6f8f014a04..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.3.txt +++ /dev/null @@ -1 +0,0 @@ -ba4c18f634b340b6064fbb919f4b33445d0c0c16e81f462d929c9437498c1349571f055d73e2bc0388a7e34a54316592a9f606cff12095b9e70483d962349a71 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.4.txt deleted file mode 100644 index 11a9e75d4f1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.4.txt +++ /dev/null @@ -1 +0,0 @@ -bca1561e9657893e6143404525b0857be42dbbf2148b64004e09207c5dce5b5c98bb431381989ac73b846d58e68d5eae12240ff01abf89660d0c7c66ec2f5b07 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.5.txt deleted file mode 100644 index 6e48c6b94f0..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.5.txt +++ /dev/null @@ -1 +0,0 @@ -dad57401f7f2029b45dbd3b3c307025320b999e9d22396888cbfcec41ebac21838d4fb7ab63925ca3fc4fed67323842cd6788ccd20c8e6f315dd54f76d848e3b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.6.txt deleted file mode 100644 index ed03743abd4..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.6.txt +++ /dev/null @@ -1 +0,0 @@ -476db12210ef3f92de3ec5743d06fa371495932773161f7e7dc574db0b8f45b5beef99d677394b3c4e5371a1b92ffb7c923e02c27f39ec3c570812b5480acaa7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.7.txt deleted file mode 100644 index ebadcd06f55..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.15.7.txt +++ /dev/null @@ -1 +0,0 @@ -fc69c5d1a8335ce8eba380e2d4aaf61e0f704e9dfd293fd435b485d3f84a191bb32b3eec91c8f718d307306abaea3b23f941e2e68cb8623c220d4009aa94b0fc diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.0.txt deleted file mode 100644 index 1c439d2eed4..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.0.txt +++ /dev/null @@ -1 +0,0 @@ -28dde609677a6303795e12452bad9e35b828bdc3cbfdaff5c40a8d30b6f1f53df6158fcb42659a5d55b89a696c9c0ec484c218c3df8c68abee4b02a141c5d284 \ No newline at end of file diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.1.txt deleted file mode 100644 index 22e734706a5..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.1.txt +++ /dev/null @@ -1 +0,0 @@ -d0d27966bcb3df9bb4f7b4188db4715321ce98beef0d57f759b003f79edf1d03dc6691380b0f4eef9f1124d5bc6932878e262af0ce98dcdd23a421b22c5232c8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.2.txt deleted file mode 100644 index a697b82840f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.16.2.txt +++ /dev/null @@ -1 +0,0 @@ -b181b6fc70695498b5faa3216664b9849a6b8e32ae36517e3cd82ea6373e61821e4b0e193339ce2e92ee5528d9c694794bfeb27a3f2cd822c9576168e37e6f77 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.0.txt deleted file mode 100644 index f0a598a86e8..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.0.txt +++ /dev/null @@ -1 +0,0 @@ -ade1d11d1a0dad72e0472ac4bbd157a444d179ffb420246cea1d4bdd45d241bd520753f57816df6851e553669573455117940859c49b9840790621c827acd8e8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.1.txt deleted file mode 100644 index 1d0dc2fdb28..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.1.txt +++ /dev/null @@ -1 +0,0 @@ -594becf061695edb79ac93675a2c574cc06f7f4069d6af57974ba6da3b47a48876a0fce44dca6524042bc6dc97f1e415c534160424973ecf004efbea8d283b0e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.10.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.10.txt deleted file mode 100644 index a58ed16ff47..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.10.txt +++ /dev/null @@ -1 +0,0 @@ -9c8b3e77aba8029499736dc4ce306654805d105fd9149e32c7078e59aaf573590f0c8590d2b714547d7033a0b964e7d4ab4deb8ceb469bdb6b92f0d155776c24 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.11.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.11.txt deleted file mode 100644 index 0d163cfb650..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.11.txt +++ /dev/null @@ -1 +0,0 @@ -4445d2a146ffba7b5d4c89826755984a17bc20cf6013f8271372d4feca6cd31b37be1314af9b2dc26b77978ad5681e75788b5c89532d4a093f9776d970d5eb8e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.12.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.12.txt deleted file mode 100644 index 206b18ac1ff..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.12.txt +++ /dev/null @@ -1 +0,0 @@ -be3250e7a13552e41a8a27d3a9e25f2fcd6dd8e6433887f13ff5e0be03a28d67478e937c5eca33f9d3a322b36f1e5d3f596c7c5b026842e07d0603ed78872736 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.13.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.13.txt deleted file mode 100644 index f96c95c437d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.13.txt +++ /dev/null @@ -1 +0,0 @@ -f14f76518749a8c6727efcfac579344c1fffc53f04cb26e0a94349f9f91a667529dd7fc1d838b82a478b50ae4ef11b03bff04e35add8d6035d11092c4bc8f625 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.14.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.14.txt deleted file mode 100644 index 1f9e58078b0..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.14.txt +++ /dev/null @@ -1 +0,0 @@ -79563aa9deb6b955e65c6e4a6c0326d98432932ad6c328886f7e4a13a0d22ba7813888cd07085c4503ade24068cb1a4d8a6b09e33b76b642ffc90bf560032a9f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.15.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.15.txt deleted file mode 100644 index 576c85abec9..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.15.txt +++ /dev/null @@ -1 +0,0 @@ -40b16be98dd17dd8b41979d9f9015e72119a9913648d5d10ab91f1dc1ee07e391e9f54f116d7a258ef722720ed61983533df79b0b58647fb2cf0f3399aac675a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.2.txt deleted file mode 100644 index ef92981e266..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.2.txt +++ /dev/null @@ -1 +0,0 @@ -a33ca96756a41030479fd094c9cb907f28d95b182d52d8121e445cb76586ce653daefb6870ed8ef88dd33958ae981a3d1965c00d4f2890af0d637dd664fd9be1 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.3.txt deleted file mode 100644 index 15f55100da0..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.3.txt +++ /dev/null @@ -1 +0,0 @@ -3987f32362d4bb4995078a0a6c3c9449ec65918aed02e49e9d1908ae6fb22f6acb2c56dbfddbad3072b68177b229965c441d85211341f46cd6969fba1f5545b2 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.4.txt deleted file mode 100644 index 6c7e6b0db44..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.4.txt +++ /dev/null @@ -1 +0,0 @@ -4d216428febffcc0c5c73c75a2f835470e60e86faa93d7cd5743dc51e4f0b68cd1da5f1e870cbd6d4e6616aa5ca3478fbe281b5b00a5913e21f35973228638fb diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.5.txt deleted file mode 100644 index b74d881b0b1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.5.txt +++ /dev/null @@ -1 +0,0 @@ -f5f32ea4acbcd18efffc7a8f076052d74b17eda16cbde21693c995c7428cddf5391579fb2b98c2839bcd11facb71edef1fa90e9dffdb309e6eea2672dcc99a61 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.6.txt deleted file mode 100644 index 98a5728321b..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.6.txt +++ /dev/null @@ -1 +0,0 @@ -a5dd58498ee11e2e1057151ba5e1c674f7adf4d6e1e47ad0af2a38ee7a075f5845a8b928620d63db8365eb12e669be586ec9b28adde8b63ba9fa67ead5080a82 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.7.txt deleted file mode 100644 index cdf3e79b744..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.7.txt +++ /dev/null @@ -1 +0,0 @@ -d39669286b1f4e1f6227188755d59fdad2151ef67a6de3ca8deafb9c3eed69a12914d68005cc6cf9565c888e7cbd7c751db3824cc50389316329fa0723617ec8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.8.txt deleted file mode 100644 index 4b457745df1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.8.txt +++ /dev/null @@ -1 +0,0 @@ -bf8de39f503f423a10569fa534ac06243e118c9d2039d6ffb9b10251b516de218e5998943ee0fe7b8ddb6f6aa6b84bdaeb87480397cd76cb6de37205fae40127 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.9.txt deleted file mode 100644 index c3939adbd65..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.17.9.txt +++ /dev/null @@ -1 +0,0 @@ -1914a9f65eb64f67497e8a5315b65e8bc087c808a98ad77ac790fc0ab1fdef73c06ed277317cae8e210f992bd9f59915e7191d4a504681bf78e888b5177f1638 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.0.txt deleted file mode 100644 index 7b868320152..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.0.txt +++ /dev/null @@ -1 +0,0 @@ -2dcfda18af745b557c62c2b60c83c96dcc767ef4f09c4ec723ae599677571204f9db59f4617fdb69eb78e24e205d91e81f236ab3aca491b1318055d1ae933b3f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.1.txt deleted file mode 100644 index b659f5c933e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.1.txt +++ /dev/null @@ -1 +0,0 @@ -34ebd540c1a341ab8fe8a59051af68c187311e407ec1ec9444ca5a2231c6b6c835ee13f9fa3bdde44b8b7d8439d2c8dce76364e8863badecb8c6fe67332ed93a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.2.txt deleted file mode 100644 index 9bd5120229c..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.2.txt +++ /dev/null @@ -1 +0,0 @@ -c863ba11770864257cb232fc1cc25a2386c3387bcab8aa95a309e4ee67bb6e184f5e6281b45820cc1383cd80e3cdee8a5c011951e7ac6d3397c3925879017777 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.3.txt deleted file mode 100644 index a5dd18b2098..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.18.3.txt +++ /dev/null @@ -1 +0,0 @@ -c0c009deab18661ad98d30c44f1cbb33a44b6a21c806428bfc6b50f28e0fbe67c7b2fd4696b8e4992b9d1cee3d4c71d7b7b4de91dbf0c103715f1542ba6a39c8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.0.txt deleted file mode 100644 index e6bd6b99d4b..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.0.txt +++ /dev/null @@ -1 +0,0 @@ -70775118ede3e9600778bb92cc5b8a56f731ae133f820a7dd2e5d00b279a79e4a1e0be9e400b0fd092f1a8295d5cf10fa4b2931f834542998ede4327d25d9d12 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.1.txt deleted file mode 100644 index 6ea65b088c2..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.1.txt +++ /dev/null @@ -1 +0,0 @@ -aeff5df888036b4ccd8d7523673c077b2819015a0e1f27091b72de091a7d0cf609940ffba7cd73f3f48e0195c61c5e627fc86886775313f31beb31cb2d21e196 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.2.txt deleted file mode 100644 index ca36afa2456..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.19.2.txt +++ /dev/null @@ -1 +0,0 @@ -385d1311d4c5e41cf0c508b1ba6ee1edf16f29f010d913311b87d3b9f3bdc844dce970258fca3d606fc7d472f2024dbbd4d797c775c3bee455bfd158cb1e90fd diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.0.txt deleted file mode 100644 index a44421fbf02..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.0.txt +++ /dev/null @@ -1 +0,0 @@ -ec288ac8a27c79d493613abf9ae867a6ba3af28a029166d100c1153cad4c4039e8ba94beadf633d2889fe2af5c3bf91998c4ea67f00ac08150e9209b52c49938 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.1.txt deleted file mode 100644 index 97ef4b3b53d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.1.txt +++ /dev/null @@ -1 +0,0 @@ -6362be7dc25124d8cda2f4ecace1b54088c8af7137f0a8789a80df63ace77ea25b5b9f09c72abb6e73deb204224d38f6f8de6a314fe255acca8fb8de3cc379d1 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.2.txt deleted file mode 100644 index 8aa79868733..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.2.txt +++ /dev/null @@ -1 +0,0 @@ -804dc71671ca8ed64f2fcf5fdfc7913758109c59b78a724dc6d6926829044aa08b354a862dcbc1bdb403def1f24e1e34eef2115778b671f87855cbfba62fa2b3 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.3.txt deleted file mode 100644 index 7462982c064..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.2.3.txt +++ /dev/null @@ -1 +0,0 @@ -513828ef6e829ff917a08095443e51224f8a7dd5cbd2379e350e0505ad412d36fa9e151dc7ec034c65c4b564ff41ec4e40c9bbeb14501a245dd4bbfd61a64c78 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.0.txt deleted file mode 100644 index 1529b94c970..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.0.txt +++ /dev/null @@ -1 +0,0 @@ -813aa497534c608234e188a4272266cf2f2e14691c626c68c0ef1b05291dc305db1c274c57cf2d5bd83568fb7064bb121f8fecb617014496d23a98df3660486d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.1.txt deleted file mode 100644 index 0016fc8a1fd..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.1.txt +++ /dev/null @@ -1 +0,0 @@ -bb055c688d9c427a9f0535e22041bc7c414c74a9c40bd626a7ccc59595303afc7e3361c644edda5041350f814a59dfc64f403030a32b948e707e6a1dbc72872e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.2.txt deleted file mode 100644 index 5f0825458d6..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.2.txt +++ /dev/null @@ -1 +0,0 @@ -002710975d063bb3301eec6a2b52b068dfed50349bf44e7d7926709ce357cc70108bc05df1352929c8989b0425988e11ee2bc187967a0966211f30be5a0963b8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.3.txt deleted file mode 100644 index 6d1a9d97b12..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.3.txt +++ /dev/null @@ -1 +0,0 @@ -d75f4a170d3763ed3f01c241bdb01e21bfb5fabf607c13a45ed4815ac7d4f98ff44e4e6bc2ab7b91d380d5a510a21c4224d95ca61ee52e3d10992fe022605570 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.4.txt deleted file mode 100644 index 9917aa9fb67..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.20.4.txt +++ /dev/null @@ -1 +0,0 @@ -ce98318463a74c268ca618f46cf441859c2a374abd5908f1f25c788f934d87a9ab97377c3422882e8ad86da4d926cfbc30356856f6d2312c793a168abea8069f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.0.txt deleted file mode 100644 index 650897d0c55..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.0.txt +++ /dev/null @@ -1 +0,0 @@ -3ba6e99c5d9ff61a771f8e775027470b066ca8fd80b45926cfc5b9a89f09ea44bf616bc07099dc39c2f096bb70e0d331aa648de0779012be9fae6abd1e81fa5f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.1.txt deleted file mode 100644 index 37f5ec63e22..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.1.txt +++ /dev/null @@ -1 +0,0 @@ -d3cb24e0212471783cb116ffd3aeac56007692d9107fd9f3d5b50b65057865b321ca52c049ec87b196129140d044282ae807aa1e14f36623b72c6c88f2b90956 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.2.txt deleted file mode 100644 index cb9cb0e3e76..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.2.txt +++ /dev/null @@ -1 +0,0 @@ -61049f9ecb5e4124486dd881ef942503988413f24256048a7ef93ce7ccae2ab9f16a8915e5ace0f04c84d4e75732acac93e36af23e893eba7d3959cc4787fc9f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.3.txt deleted file mode 100644 index f3b20dc40a7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.3.txt +++ /dev/null @@ -1 +0,0 @@ -40342ee99d7aafd5de610927de407a3d736cf37afa3ecd9ecfb35abade00675d0f4156ad47f81871d63658a67ff59a3fb46660dfc28a10d21223ac40cdd13bdb diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.4.txt deleted file mode 100644 index f9bd5d67a52..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.4.txt +++ /dev/null @@ -1 +0,0 @@ -7f47e41ca607a330a0dc988a9706fe8fa4065ee9045512ef0d5e9ebe12f53040e761cb9162448c1625af7d230baa2d24cdd3efbba0e4852bf855a075c2857bf6 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.5.txt deleted file mode 100644 index c649a915bff..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.5.txt +++ /dev/null @@ -1 +0,0 @@ -d128ce5bcdf09b67de33cc8a32f3e8e6f3efb602f7c61bce7001ad7f616299923b90ada691d50a120a73478673cc7db49736822013556de86dd902209a5c5b0d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.6.txt deleted file mode 100644 index 2ee04ae6a00..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.21.6.txt +++ /dev/null @@ -1 +0,0 @@ -3cfa5ee53774a042aa9cbdc2a64747265b359048db0087b24d084611c2c1a97a7f3f20547612d5681a338c32fd45c496c21a511872d098bde50ba8e66579c456 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.0.txt deleted file mode 100644 index a88228b9727..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.0.txt +++ /dev/null @@ -1 +0,0 @@ -f35ee054e97240c089f1f56d233875e6038ac264facfeddc0aeb2abd5d1c7c0844e92f2ab3087c45c2999f4fd7bec8720bcfd4794af19dc5e565afa884e1be85 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.1.txt deleted file mode 100644 index 7ea8b9bfb18..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.1.txt +++ /dev/null @@ -1 +0,0 @@ -fb7fc671dec8dfc0528b42177b380ff7997340029746c367523aee8dd5bb2b335cd61b6663f5b7a198df5473fe61b31b2eadfb61fe93ca13bfe18d8294e80329 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.2.txt deleted file mode 100644 index 6f317661ec6..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.2.txt +++ /dev/null @@ -1 +0,0 @@ -3e7421186fdcc7ef7ab87f3a294b84d3478f47dbe54a5cf68ad739625c4cb9b58b3ac83c900039ee65b2e789b37d5faf9a67586c29045fc099bbd3e6f09eff85 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.3.txt deleted file mode 100644 index 0c7577b25a7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.22.3.txt +++ /dev/null @@ -1 +0,0 @@ -300b9bb439118289cba69db994e9402697114d1b262b7c2879e49e19266f7b7b97d0aab45f54921c5449847579043867fbaf451165d616da78d945927415fdb9 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.0.txt deleted file mode 100644 index 580ecb3c7d2..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.0.txt +++ /dev/null @@ -1 +0,0 @@ -c87ffc9ad12f452c013e996f2c4a792869c4a6b333c01091cb535ee62ccdc8f8546ae09738d08bd284bbfc27719b6cf22730c06678434e807407ce3b601dc090 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.1.txt deleted file mode 100644 index 804c9bf0504..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.1.txt +++ /dev/null @@ -1 +0,0 @@ -cbcbceda448c0b31acfbb5fc5ac18dc1bd90787009bb9b17758629b3fc1b1fb697c64666bb864d2a3cf968ee14f0c7aaf5e9887c4eefded223689b414c0a3b71 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.2.txt deleted file mode 100644 index 00f7bafcfa3..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.2.txt +++ /dev/null @@ -1 +0,0 @@ -8006a4ce467f9fe0842757075fbbfd6dbeddafe5dc1e3de640a8fb3406126155cd782b16128fdf056d79d6681eff83c5f2b1e86b49460a7a0b23b2b231ae781a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.3.txt deleted file mode 100644 index ba0b4cb27f1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.3.txt +++ /dev/null @@ -1 +0,0 @@ -8bbab870128ce2e9283c41ef84959f99b15f003f7b0d499c95b2e40c0f1b706e65b3d9046749a56ba241046d2cdb5c9411c1a414efd87b7a9961595074c39983 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.4.txt deleted file mode 100644 index 811daad2749..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.4.txt +++ /dev/null @@ -1 +0,0 @@ -84f8cf2aa279a63440dc412b7ccf6338df48b4b3f5aa21af99ad15febe667d277b725bb924e1ac48766ba72d02dcc0170f4d5cdb2c280615ec60a98839391d89 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.5.txt deleted file mode 100644 index 580110b6e85..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.5.txt +++ /dev/null @@ -1 +0,0 @@ -6adfe5c8e153b43bd4fdb2f0dc79c2e5d035d3fe9e571c2f97255edd87d2419c91f8495bedaab3c3761d4e6b7e7b460c091d5189a184e6e2ed785ea06f204597 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.6.txt deleted file mode 100644 index 7a944d0d774..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.6.txt +++ /dev/null @@ -1 +0,0 @@ -f910abcf08a2ad51e46f68f492b2d651fa5d5debf80761464ce1809d2672b1db1064db304f6f5bd19c2587792b160c9bb5fa526b371fd3a4060e8a0841f5452d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.7.txt deleted file mode 100644 index d0c5f4b4297..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.7.txt +++ /dev/null @@ -1 +0,0 @@ -461c5c8d68892208c8fe457a51a6c18b253ec32cd37aface4fa2eae81fca94874c654a95a16f21571134476970a66304b261063c7fe2cd8a19daaba9bb90389b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.8.txt deleted file mode 100644 index 30466800130..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.8.txt +++ /dev/null @@ -1 +0,0 @@ -e6f77862bf426401c4b86bfc92eae792da5ae3cc830c03ae6f90e80efa8ff1e324cfd4a49474832b0f82d32c2e568018b757a5efcbd896ed4de7ee5dc736e9e5 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.9.txt deleted file mode 100644 index febdaa6cf0e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.23.9.txt +++ /dev/null @@ -1 +0,0 @@ -3b93d5b119ca2b21784fb6b151f5e9f9b9630c8cd4f26995d87669097e2d19048588d4055dc230f2f926aa248f88bfbb4a4ec547c2f5ad6b801ff43b05c5c391 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.0.txt deleted file mode 100644 index 606ecf7dbf1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.0.txt +++ /dev/null @@ -1 +0,0 @@ -325cc56c6cb2773eb9341963371f1c80167168a76601a96a097e7d60f77bed0715ef8b21e5132555308295775d061674cf87f47ce390155496099fa1e49bb441 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.1.txt deleted file mode 100644 index b86cee5a9b7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.1.txt +++ /dev/null @@ -1 +0,0 @@ -b6934de96b0d4f0758d0e84671d5e9d82e5e5790c01d3dbb53db1793aa4836e216d0962386542126f3b2dc1a27b35bec1ae802e644794a6029ce38314269d5f5 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.10.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.10.txt deleted file mode 100644 index 7640af11d0a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.10.txt +++ /dev/null @@ -1 +0,0 @@ -e53374f59ccb74dae6d99f2d9fffff6a932f442d31646aa4dd7a6725f434dd08925dba18e4c049432cebb48b0cdabdb72a0653b0c8eeb32399fb3a8cecb9380b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.11.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.11.txt deleted file mode 100644 index ceba77d4c9f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.11.txt +++ /dev/null @@ -1 +0,0 @@ -26b5a3d30b0866b87dbcc800f5860c92f982aa04fdf8a855674ffec3bc8ce1fbe041672df9d00c4318040c44239b9c3ab829c5f99f6568d0d37f3cea19ecdb81 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.12.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.12.txt deleted file mode 100644 index 18aba842de3..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.12.txt +++ /dev/null @@ -1 +0,0 @@ -019ea12ce24507d58afd8ffbcd4a8de990f8cdf354c7c273480dd09eca211a709dcb7e41cedb99813cdfc680bae8eab1f25826246ea85c5860898f60c34b3af8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.13.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.13.txt deleted file mode 100644 index 3126e350805..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.13.txt +++ /dev/null @@ -1 +0,0 @@ -566d9dfda5e0c39310f5c54e5c3cf44d3f7118224ac8a1aab2ccd2c9a46f34acaf29bf99a5baf2e2454a83c0b894cedff316a5b1e85d577d3c0721297afad78e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.14.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.14.txt deleted file mode 100644 index 178771ae90f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.14.txt +++ /dev/null @@ -1 +0,0 @@ -d3e3134b7e49c1f0c7c368c0fb5cdf0c27448b7527aac4cc7df012137b52919962b41ed0c3c503298232ccd7ea03a781be798b310af654906150b446a5043671 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.15.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.15.txt deleted file mode 100644 index 0b7932b8a49..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.15.txt +++ /dev/null @@ -1 +0,0 @@ -f2b2eb456344075b733c1fac38c258ec02b0fa492ecc124d6d5a090b7b949eed1166da008b90182db9d29f106236251b3a2cc61fd2e99fe7dba268e20c3a0ddd diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.2.txt deleted file mode 100644 index df070b6a60a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.2.txt +++ /dev/null @@ -1 +0,0 @@ -4b1fb57d91cd1ee1f94ecfdacd0b67585c3638c8cc1b06bfcdd7c8e050f15bba37c573fb1a394451ffcf5cc2077f39c24a77b2f130943538a3c01098d487228e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.3.txt deleted file mode 100644 index 6acb6e6cb34..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.3.txt +++ /dev/null @@ -1 +0,0 @@ -c0dc667c78c347daecfaca74da7e7cd230cd1c3992dc4c0b30014aa6b8add65ef7b110eca420495b93ee4f90a472297bde81e0758b77675389e3d9dd7edc0faf diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.4.txt deleted file mode 100644 index 47b76e81009..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.4.txt +++ /dev/null @@ -1 +0,0 @@ -2a89e5dd34183dcbaf9474a26d51c51b7f37ef39aa09fe9e28de310910b00b81751a9d03e40b0c70dee7422d6b10bc92e6adae7fc480d8e0c943ee6ecce05761 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.5.txt deleted file mode 100644 index 291b6aadd79..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.5.txt +++ /dev/null @@ -1 +0,0 @@ -a179aa5876ac3dc84d516ed73c08fdae2879b7ed8a725beb762860f02f671061d9e4f6569828bce0754da49ed3a1f46d134444a71ffd1a60d71078ee16260391 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.6.txt deleted file mode 100644 index 782979b4f37..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.6.txt +++ /dev/null @@ -1 +0,0 @@ -60c791368edeecb40481e573b129851090fc11f3b5f2b6a6a05ae1f7a64ff53e113addc456a4df469b9bcdf84199bc52381df6fc5cf03d65028023f5e3520be9 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.7.txt deleted file mode 100644 index 5448e2f11e1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.7.txt +++ /dev/null @@ -1 +0,0 @@ -a6cfd98894639e5fbf7c687b60efcb66c7c0c7a9cc8a810083a2f9cc7d87a3ac51df3c3b6b4d49e4ea95cc7bae2e839177d83550cba992eaa99bfffcd85168a7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.8.txt deleted file mode 100644 index 5d766b61466..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.8.txt +++ /dev/null @@ -1 +0,0 @@ -f684ab732ae69ead5c4726c0c7bf2ec72127061b499cfc7f144348fb7b584b3cfdd02f8382158f849b6d0a109bcf1d0597a9eaa6dd427e254a784337b57aec7f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.9.txt deleted file mode 100644 index d0cc22e278e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.24.9.txt +++ /dev/null @@ -1 +0,0 @@ -06f556766da9333ddcf1d75e04707501e9f36420870da5df72e4c3e20d757fb8e157d3ea5fae5650090efea305bd7fce790cc5829ec08d322022c8f3c0c4ebff diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.0.txt deleted file mode 100644 index e822d5a3ed9..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.0.txt +++ /dev/null @@ -1 +0,0 @@ -5cefd636eae11fec8127d2245d9cc2d630492311ab55ccae464058ea83920f24342d64d22afff325a1132f5a55f4c0721cc77077311b2ad906a39d4a7a6ec425 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.1.txt deleted file mode 100644 index 2b37ff364dc..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.1.txt +++ /dev/null @@ -1 +0,0 @@ -2ce7d2337ba0ce219cb8c8cd80f39b6db3a523175ff1ef09410b01a4f6f19139b19af3b54174d8cd09ce6fdb0e7eb526825fc50433d64c3db36d2ddee916fb2c diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.2.txt deleted file mode 100644 index eb6ca1b2ff0..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.25.2.txt +++ /dev/null @@ -1 +0,0 @@ -39a4910d324054894d3b6695b9651040f7fdbb418450827409c6b8361a628a16a5a7ae6f1bb2d57b9b3784218862f7428572a0baf9d18ac3b9c0e5267018e479 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.0.txt deleted file mode 100644 index edaaa693fcd..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.0.txt +++ /dev/null @@ -1 +0,0 @@ -c5e6955c2878ed82c168a6f73abee8e6ed5a79a6f57bc25131a44603e88c4bab17726f1f6c1c856ad3aeae96f8a19d38fae864019f6d2b64306f9c4e5433a6ce diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.2.txt deleted file mode 100644 index 79b23d76d94..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.2.txt +++ /dev/null @@ -1 +0,0 @@ -c274b2993b36849ef08083eed7b9712d88eada464a893bdc123bbb7dd46e90417e3e3206e377d00a23a730a04170440375c6f2198418043d2fdc28baa0463e28 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.3.txt deleted file mode 100644 index 083a5d11b69..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.3.txt +++ /dev/null @@ -1 +0,0 @@ -1913e81ac6a1f6055b023b4e33d0188f801c722e4f6f18a410f2776169e1fa1dc87fad1e89416305ff4e08e352f9b7107a61fe9edf65d215ec2547cc4a78eb81 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.5.txt deleted file mode 100644 index d37841db695..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.26.5.txt +++ /dev/null @@ -1 +0,0 @@ -34218544d143f790a5424f12ec792f04b2d4e0e4244d549cb9baa6c3eacc1b68d2f0960a678479507c893050804d5cad916ba3ad9f8caec9c9958c7c02fd0dd5 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.0.txt deleted file mode 100644 index df2cec8543a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.0.txt +++ /dev/null @@ -1 +0,0 @@ -0f796160fcacd74386d1e955cafa7b51815593efcaa72d5f1583f024a4732abd8e97e6c2a9d83ff6a5f3f6c9906f367a002e11455db16fbbd71f4538c2552411 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.1.txt deleted file mode 100644 index 8fa03b871d4..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.1.txt +++ /dev/null @@ -1 +0,0 @@ -c0ea2ec42b02b4f03f6499dfaa153f53814f0b91638146e4d3b3f9021da2df1fd3bf8dbac150091d4ca988e2271addcd263624999700d16c53b77ee4dd430038 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.2.txt deleted file mode 100644 index 356af9cd323..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.2.txt +++ /dev/null @@ -1 +0,0 @@ -ffdaed66f84e10c2118ab0b4db4f2d958412380851edfc84c4b3c87f1b6c108399a0655912d750fdcc17393df7d803a345d02569846b8ef6fdd6a3f5d3ac8f83 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.4.txt deleted file mode 100644 index a9975e1cee7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.4.txt +++ /dev/null @@ -1 +0,0 @@ -aef6b82f70e6f264f8a762d3a2fce3d4b2f6779b07d91f15102a11dca76f0c1fcbfdaa46c436fe3407683a3f194a1bb39578934cdb8cbb10589c8c7526508228 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.5.txt deleted file mode 100644 index 6c3037ca5b4..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.5.txt +++ /dev/null @@ -1 +0,0 @@ -5107cca81a11674de03cc65f0c3a13389c4538225b10a8bcc0c6fc4aae2ec08e7c01e18ad5f86fa5db5fde01608a695fbdada4ea98fb4041dd4a3a7ed5019262 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.6.txt deleted file mode 100644 index 9df116f4b8d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.27.6.txt +++ /dev/null @@ -1 +0,0 @@ -ecf249015ae4da921afc1291cbc7c8519804d47895d7de771760fb8c76a9bc19f3eba7e0d7cd07beb5a47c27974340d94b9dba1f1bec17428728f802cf157df2 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.0.txt deleted file mode 100644 index 184bae98e88..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.0.txt +++ /dev/null @@ -1 +0,0 @@ -46190abe8d74973ecc67c365029c861ef0b06ab06adad979cacf8dafa87df0b8a1b297ebc58867a62674e06afd9663467641e60da6a3e7c4ab60859fae409a36 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.1.txt deleted file mode 100644 index dc02c5325f7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.28.1.txt +++ /dev/null @@ -1 +0,0 @@ -1393056efd26d98854bb54e345e9bb3dc5032648724540f5aa759c92e1a3076f7317a64e9a4275658b150b81e53e06185025848f467ea6d92af805197a6c9660 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.0.txt deleted file mode 100644 index fc98027ece0..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.0.txt +++ /dev/null @@ -1 +0,0 @@ -8b7c83c448d7cadf1223c75f495b1342809f703374433a54cd184d5d4c2e934bf5f1ab3cebea1c901de42780e7a73a6f1fdea575400b32376efd2ef1befd0d63 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.1.txt deleted file mode 100644 index 7ed66d4fec3..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.1.txt +++ /dev/null @@ -1 +0,0 @@ -2f6aad9879bdf10f3089102c0f37b79233d3b6dd76267a3c52b286340f723723cc4aa0642eda555ce640e615c8af1b7c815db83160f00ef389aef949ebc62bb6 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.2.txt deleted file mode 100644 index 9b06209c9f5..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.2.txt +++ /dev/null @@ -1 +0,0 @@ -60d43612eaedfb0130e4e4c27f6f4239d6f5a1febca6eecd799b14923da2684830c75b5807d0c8894fa5194efa65b2800093147a4222090e60e532858315ba32 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.3.txt deleted file mode 100644 index fcd79bac1d2..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.3.txt +++ /dev/null @@ -1 +0,0 @@ -40aa14f24d3a3151de2015c5b3cd58a98ea7a2c0fccaa4663700bd0c804d68a129b9ceb63fc07b288ea814c74eeb4f20c82768781c99b8e80908531fb114f0d4 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.4.txt deleted file mode 100644 index e04b4c74cf0..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.29.4.txt +++ /dev/null @@ -1 +0,0 @@ -bfbc862b5f4f3e5a06517713e3bb618dce6b3e3f6feded200634ce97344651a98e408094cf7c02ee711008ded100049b3e743526f374f6ca7f9d38bccd08d004 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.3.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.3.0.txt deleted file mode 100644 index 16de8852617..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.3.0.txt +++ /dev/null @@ -1 +0,0 @@ -cf7d74176481decbc4b8eea28a1133076fd184856bbee95f583f9c0818771028ec65d55f7f711df439faf5c1c85a47f22e7f4165665261922aa8b6e0d0d950d4 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.0.txt deleted file mode 100644 index d72a6a58fdb..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.0.txt +++ /dev/null @@ -1 +0,0 @@ -b51cb1d642400b5a3099d0b9194b82050d812d4f1ba782c707f7f3090931439baf241e4eae745452399f48c60de64082689069cbf3fb1b371c943f2fd493dfe7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.1.txt deleted file mode 100644 index c5d3c9f8d27..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.1.txt +++ /dev/null @@ -1 +0,0 @@ -e565badaef42bdcf0c9dd679947d46bbb9149dfab40ed481f5195df6e24df4844df2c7fc333df52b7fac05d1008353d99d7930591bcaace4055d41bdd1358e08 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.2.txt deleted file mode 100644 index 874744e8c6e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.2.txt +++ /dev/null @@ -1 +0,0 @@ -26805a034902ede828fbf023b88aa7a9c03f0ee6dfc8e8dafb1404fdc1c64959a5b952d794d356ed08f05ce1e1bcd2d1be48054578003885ba91ad002d94c470 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.3.txt deleted file mode 100644 index ebdec9716e2..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.3.txt +++ /dev/null @@ -1 +0,0 @@ -1922b4218dc1549263354398c358fd79146de111b8e01d32408d0bcac382f4b90e27106e6338e36e9e36175761e8c0a55a2c86b01239cd64ff7a2ea9c2cba5ee diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.4.txt deleted file mode 100644 index 4a8e021d36a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.4.txt +++ /dev/null @@ -1 +0,0 @@ -65231e030a03fb97fe4bb9d57d3c6586f8a243cc164dcf9e9119046a1ae96ec527d498639a1e434654747e236f412130219a3513152e9190974d2e0a8f00af2b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.5.txt deleted file mode 100644 index 34ff06ed702..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.5.txt +++ /dev/null @@ -1 +0,0 @@ -2934e5e305b16beca9e37e6b55f4969e24877c86e68c2825ed8b8fae1e84c3ee97ecf3e9450c5d34d1bc1410a32c045f60ca65d544a9c960ab1d3ef5fb5ec18d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.6.txt deleted file mode 100644 index 6b75656188e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.6.txt +++ /dev/null @@ -1 +0,0 @@ -2956ec0ff31daf1aa6c6b7078ca56807ebab9f24cf8be2cca2bb2e0da164ecb0efbe6f8a37751af0179ffd84c90243e136363a47db18bfa0bcc7acb1a264d903 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.7.txt deleted file mode 100644 index c3dd83ff1cf..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.7.txt +++ /dev/null @@ -1 +0,0 @@ -f4d1816ae87e21d2d1f8d7ab1ba75606f4714259ef8190e8d24fe9dfa708c15311fddad571c6815b8bb4e324377a0634983f262f3d4bd6068625d71401f0d850 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.4.8.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.0.txt deleted file mode 100644 index 0c60f008582..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.0.txt +++ /dev/null @@ -1 +0,0 @@ -a2b3e35485b4cbd0656aa0b6a6583809e8db9364a913ef94415c40d26fb8b55201f77d91cc8c411f45e4ebf949530262478a99e4655a4573f3cf709f82a43ab7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.1.txt deleted file mode 100644 index 7b8694ae3de..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.1.txt +++ /dev/null @@ -1 +0,0 @@ -a7a844b3e17989a6a4b7638627bc8045b0ce68330e683c03af4960470aaf53c381490bf16e1fb216d79c1c7db528e634d1c9559a1cc9635a39cf3bda84cf4502 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.2.txt deleted file mode 100644 index 8dd3d61974e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.2.txt +++ /dev/null @@ -1 +0,0 @@ -6578f9ddf46edd80fe156ac11799f1e979c581affbddca2ecadb4246934722666677a88e648e3d4799008f9c482f5ff4477e8cb62b4838196d07bf6665f64074 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.3.txt deleted file mode 100644 index 988108dde60..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.3.txt +++ /dev/null @@ -1 +0,0 @@ -26d1e4db3e66dc9469053d424c29cd5a02f9d91d16d5db2eede9fe6c924640dedf27e304a99b38841550eaa1d0a1343b3f17cea0f40b6277d0f94a9df5257130 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.4.txt deleted file mode 100644 index 0443ef64aa8..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.4.txt +++ /dev/null @@ -1 +0,0 @@ -60c95c06fdf6b106683cd87ce993f2b8f6903f85050826d26bd5d579144c4ab71068166c7e9d6c47577c38cb93b2f4095f87b60a69e8ff391338d4c935c0e5e1 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.5.txt deleted file mode 100644 index 633c7b352a7..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.5.txt +++ /dev/null @@ -1 +0,0 @@ -8657b73725fe9390deeaf37f5d6a902cbd2939f8039a9ef0e96d3a7000ab5f835023aa323dad399ef8a00c8686dca5397295b8aadaa9367105976720c9dc9e61 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.6.txt deleted file mode 100644 index 462ae5a73ca..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.6.txt +++ /dev/null @@ -1 +0,0 @@ -928afc8c4526b1869a9e88b2e62115a97d9a062e5bc36a3393a85d887a038da293a08e9baa828d6121a1d0a03974aff7b91bed7cb38d30ebe5dbb5e3e8f96cf7 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.7.txt deleted file mode 100644 index 95d4a462f9c..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.7.txt +++ /dev/null @@ -1 +0,0 @@ -8b59d45fd5bb4debf257f4215fcc0e304ac31bcf814cb2684fa49fd1696fbad35d88f6e578c502f927819bffd1efce4dd0909e59a51ba6a4af53618e5ff20165 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.8.txt deleted file mode 100644 index 04191f0b051..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.8.txt +++ /dev/null @@ -1 +0,0 @@ -0119e593bdb84bc500b61bc4f4712d5f15b33f5fa5e6860f9e0ecdc0215ef7e61dd53472f5b03fee5ff149649712e9cf1ffc25141875c9cc4eab0cb2c9ec626b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.9.txt deleted file mode 100644 index d4a62d49335..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.5.9.txt +++ /dev/null @@ -1 +0,0 @@ -d82db8271dd1bae9d6b49b0e3c9c9446dc95cdaa1405b99af6bf65c551dc8994c445137ec98e11c8b61363b9490ea2fc22ea98f966dbf645f0dcbef2419f3220 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.0.txt deleted file mode 100644 index 9db787db56a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.0.txt +++ /dev/null @@ -1 +0,0 @@ -0fa5c3a16722351ac3b57f54b2cab3dfced63a5cdf1b90d12824e6b69424135d4ae0187089b6b7cc27e5a47f1bc89e2ee111001142789eb1bdab062e048d466a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.1.txt deleted file mode 100644 index 0246e23ff8a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.1.txt +++ /dev/null @@ -1 +0,0 @@ -7da501aaf5560330a34d52d8df2170b2dc1ad307b922abe2d6a2da117c3dad8281385bcd8a092a4ed6642e7bb91e114d552c859511523e8f719fca5ee50e8171 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.2.txt deleted file mode 100644 index 371e87ef0f3..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.2.txt +++ /dev/null @@ -1 +0,0 @@ -804a6575faed960eb1081ba5988fcf53d89c55f68b251397aa5a59fed1e9e0e4272bf18a27dfa03ea15d72271893be93e40423b54a8f2eece9a314ede8a5d517 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.3.txt deleted file mode 100644 index 55fb0b83198..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.3.txt +++ /dev/null @@ -1 +0,0 @@ -5e8d1483abc2f5e65e0cd7dddcf826b0fe2dda0166ae2e13e107bf10c0268c0267b21b851e4f9671877fb5a0cf9a71081e2536eaa76d3f5ee2adf9e899b6d751 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.4.txt deleted file mode 100644 index aa487b94cac..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.4.txt +++ /dev/null @@ -1 +0,0 @@ -c4cbd897270ecd150eb59784d7a568c61e9d9f7c9ad9a99cd7547be857cfde0ca268f1d6d0ce726d4f6003882253ab6a63d334e406a1fae4cb55b07fc9f61b38 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.5.txt deleted file mode 100644 index 727dff4797e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.6.5.txt +++ /dev/null @@ -1 +0,0 @@ -ad004946d3f432fe8c3694e8a8410d6ce6f29a7fbed0d785d05e488e6ecb8ccf523d2d73514fcee8481ceb66053d9eb88297f4fab25d031938c08ee2dcc58968 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.0.txt deleted file mode 100644 index 494f3f80aa4..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.0.txt +++ /dev/null @@ -1 +0,0 @@ -377a41d7a5831d0d20176bd7ebc7ad4ba24cdf7bdbc2879eb35844ece7e48390ca598061b58b6fe60b965af7f9097b353dc30b9ce89180ac03bc54a4fe3ee59d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.1.txt deleted file mode 100644 index edfa059336e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.1.txt +++ /dev/null @@ -1 +0,0 @@ -0c22a1050619fc84f024a8928831c8b1dcc733b05742b249c2928d4d97f7bc4106839a1038e92671b42ac8bf2c0647dcdc09a6c7cd8ef240a67f841bf7ed3ffc diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.10.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.10.txt deleted file mode 100644 index 7ac35823cb9..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.10.txt +++ /dev/null @@ -1 +0,0 @@ -9cc3900fa9b04a667146595141586592e1c7f72d4fb88d6af8e95b74d50cb8c096e5abd00d35e295a66dbdc7774b0afd311a20747e6051d786da36564282aeca diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.11.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.11.txt deleted file mode 100644 index 89da39ca5ca..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.11.txt +++ /dev/null @@ -1 +0,0 @@ -983b45f0aa5a7905d98cbe6571a4028fcbc115eff42deaa378edf5591b546a6b5e5cddfbbb39b5d363ffc16147dc72e056bd66c8f3e8adba97e58064f2c12695 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.12.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.12.txt deleted file mode 100644 index 60acdaa6533..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.12.txt +++ /dev/null @@ -1 +0,0 @@ -739bd0c352ef4d183426504ddd41456d05191fcf3230214e9e84d7011a84c65263ec3299d9ef42a829e49d370ece69d15cd3f3d913bf63043fd95f55b1f6ac48 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.13.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.13.txt deleted file mode 100644 index de8194ae016..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.13.txt +++ /dev/null @@ -1 +0,0 @@ -7d9aae99f424473e4e07aa6e119645027d8da99f062c162cc7f67929fddd886192745383b70e053e2df6ae2176208c5781b92203f8a7194c1390ec13dce36a12 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.2.txt deleted file mode 100644 index 0a682678b1c..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.2.txt +++ /dev/null @@ -1 +0,0 @@ -5c8539eca13d99875f50d8a77c27327ab0c14eea88e31e0ffb78a81990f1e7bcdb34b2cc2ba0ec647cbc36ce74c4d990f6483381388166d4e6a9191c77c69d4d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.3.txt deleted file mode 100644 index 641b1ca62bc..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.3.txt +++ /dev/null @@ -1 +0,0 @@ -7c1076a9065fbb7fe69796e8bf33454e1ed59aac0fb967b08e7756193d3a9aa41ba4f92e36ddb9e7d50c73cce42d516c0d0d2492ba1ec3f1af005597513c6f1b diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.4.txt deleted file mode 100644 index 7e2e8829288..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.4.txt +++ /dev/null @@ -1 +0,0 @@ -3ab8ffacb8cd9ee0b22695b63e8ba4a4602007f54928cf4745b2d6a8866bc8291806ab492526e4ebb2c297ec27a7bb605afe674d440ee3027d4c192dc06978a9 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.5.txt deleted file mode 100644 index f0c5a8dbe33..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.5.txt +++ /dev/null @@ -1 +0,0 @@ -3ec4d622f1a1d2efc910872db03f79ca43c35294fa7a93adf5283c68faf21cb7b2a9116d47a8b0e49f174276b84751d8e9e16a1a51bc20ab5ea8a40df2b41183 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.6.txt deleted file mode 100644 index 3db2833b88d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.6.txt +++ /dev/null @@ -1 +0,0 @@ -2f7fd5c57b8564bb9af06d82449a46957c6adf723dad61092d6e2fa91e79966fa15f6dbe49df90e99f56d51c9808c1c5e6f25c80855728923a69bd77646d894a diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.7.txt deleted file mode 100644 index 39317935c44..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.7.txt +++ /dev/null @@ -1 +0,0 @@ -a3625b161363afb04bbf497ae1af1cc3c580753c95a2d281c1788f162fb146c92f4b73b7faf053cc73df6db61e90b4eb37d3cbe7247b9bc6edc92f217f6e691c diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.8.txt deleted file mode 100644 index 56a750886ca..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.8.txt +++ /dev/null @@ -1 +0,0 @@ -3aea6af5e7ed2a5ebab879d3ea65fea6b60be60d7b3f3663d03831797b3970beb896a82a035b8782c693b0f9e36c9f3d3b937ea09b165ee1478f393881f5ba18 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.9.txt deleted file mode 100644 index f239cb73643..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.7.9.txt +++ /dev/null @@ -1 +0,0 @@ -a9098604f33ebd469909ee7be84dbe8636adce7e91199c442d95ddc35da4773fe7117057cf1a0449611625c902de03a26d015f82b8917c97b8bbe5e4f1915d4e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.0.txt deleted file mode 100644 index a3e1a48dd94..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.0.txt +++ /dev/null @@ -1 +0,0 @@ -66ee5869510364c01668107c6343085e9f2755a07fcd4fb65d251e57773fa317c24bb2a3da091f796523a30bad6f6d310bb50d0c57d3a29a3ff4bc65215e5c2d diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.1.txt deleted file mode 100644 index b9530e06434..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.1.txt +++ /dev/null @@ -1 +0,0 @@ -924658f0f8cbd15afa5210d46c2e2c13a768ad0bb49e3bef00096b1d8836600a5e00d42ed7b3f04b49870c4042f97916e13f1a51d3ea367fdc5945fd4402df64 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.2.txt deleted file mode 100644 index 9b115ffcf3b..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.8.2.txt +++ /dev/null @@ -1 +0,0 @@ -78623f5ef2359e170dc04eaaf1b5397b74082f962b35bda766e2574218ad55c2b490884f1f76e7bd66acd091f68539b6ecf5f8eedc63190c26aeedb976278562 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.0.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.0.txt deleted file mode 100644 index 532770a678a..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.0.txt +++ /dev/null @@ -1 +0,0 @@ -cee3e8dfe8a1c7016ef9578edab3c81dc0ad7f87df1e97a860ea2ef455fb2ba88ecf8120529b0feea683ce623ce2b7e6172e97bf2850f73ba32b858410661676 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.1.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.1.txt deleted file mode 100644 index a2902b3226b..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.1.txt +++ /dev/null @@ -1 +0,0 @@ -ecde6cf51e0eb328ee6185334acc9f16b1ccae2568a8ea7f45d501a14a66ea2af76b41e240dd255e4d05e8fd5b8f3d6866c02daa7f30a6e0b1053ced95bee1e4 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.10.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.10.txt deleted file mode 100644 index 990d0ba7552..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.10.txt +++ /dev/null @@ -1 +0,0 @@ -8a7526d55d9dd3e4ba042b4e950105a9b99b09373fdecf8017ffb002bcb1aacbef6ebdd213588ec96558e49c2f23e9136f3167c928156a815045176ff2161b70 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.11.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.11.txt deleted file mode 100644 index bdbb294850b..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.11.txt +++ /dev/null @@ -1 +0,0 @@ -6ff8342db34dd6f7a7a7e11b2925df9205810ca4ce15ba2ec8a2ec79eafebdd0c1d7c706f82d6b5fe536d7b19c4ab4eda2c0129f1d0db9d6f597a2c92f5b38d3 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.12.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.12.txt deleted file mode 100644 index 3a3d0249e1f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.12.txt +++ /dev/null @@ -1 +0,0 @@ -ce8e9583918491e341bc47eb6a08712ea310d91361f7324e9e10574e60cde062deecf58306204267a08bb8a8ffdbb85fe73fa8382f91fb51cbc6f3ea7919df9f diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.13.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.13.txt deleted file mode 100644 index f66f088a960..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.13.txt +++ /dev/null @@ -1 +0,0 @@ -074391c2bdc294a1ec027d043a6bf172f70bf5ff75f0062821cabd5ce7469c88a3b60a0ed644e8bae178fdd9db26acef20b0721fcc708c1825da256ec5f2b4fb diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.2.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.2.txt deleted file mode 100644 index b1dadfb41f1..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.2.txt +++ /dev/null @@ -1 +0,0 @@ -a2370a8984ef9acb0664cc982bfc22fa2fc33df631b9e5b80fa8a2a9e8ef68d0c296f1f297e405c44b123445de67edc83dfcd6960b53063b0add7906202e5fb8 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.3.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.3.txt deleted file mode 100644 index 55646d1552f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.3.txt +++ /dev/null @@ -1 +0,0 @@ -f70be733046923edab517e4bfbada4dcbc9f9234d4706bce59a8d9f23100545d994e03c49ebe525ad4435d33b5abfe25191c690fd20d8e16ea0aa0979438f21e diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.4.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.4.txt deleted file mode 100644 index ed1c63914cd..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.4.txt +++ /dev/null @@ -1 +0,0 @@ -b9b8771b0dea2b4fe2d426ae0c84a522884c2b36cedd1095ded49fef9bc90c0373d70ff871635223f07f12ac9a067978d91d4b42292070e44f2a5f223e5f4d88 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.5.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.5.txt deleted file mode 100644 index 5ec917aa3a5..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.5.txt +++ /dev/null @@ -1 +0,0 @@ -de80808d62c6f49cc391ad2f0badc6040ac174271656649f0837aa11bb9188a5faa7b33c6a7640129f4b2df4cb1a00c823efcceff4f96aeac77655be4c51bd20 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.6.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.6.txt deleted file mode 100644 index 5882b80822d..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.6.txt +++ /dev/null @@ -1 +0,0 @@ -3e14feb6dd6a14d26c76f3acce640845f786961d4718730d4509e3b72990cc2a184db8e6d17f612fdcb1cd5f5e85a02f72a8bc69124b7173f0019664baa5cdf2 diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.7.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.7.txt deleted file mode 100644 index 9dfa22b0a05..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.7.txt +++ /dev/null @@ -1 +0,0 @@ -aac0fdf50112fb896616ce4dffe1e427dce076f7d13cfa6b6f62ef97c26eee56c3ff4e8e2e088ecf3dee263d841c294651dc65514159b3f4cab0cd6e53f1e0de diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.8.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.8.txt deleted file mode 100644 index 890975dbf3f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.8.txt +++ /dev/null @@ -1 +0,0 @@ -4da4d21bec267d782224732143535a3d9bd210c1b5ed9df56f9b21ffc9415ac04687b2ff01c4c6d8dd0c82613ce323db08e96b727e95a62855c47112c49d07ab diff --git a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.9.txt b/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.9.txt deleted file mode 100644 index 04fc089c13e..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/graphql-pro-1.9.9.txt +++ /dev/null @@ -1 +0,0 @@ -055f07c55bf22cda1ac6ce58b8acb74812ae150c3235ba7769b47933f827b4956fe9c036a40d48a4051554d161d9274c21dac57694daff1a0a8ee2558b838f2f diff --git a/vendor/gems/graphql/guides/pro/checksums/pro-1.26.4.txt b/vendor/gems/graphql/guides/pro/checksums/pro-1.26.4.txt deleted file mode 100644 index 9caa82b00e6..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/pro-1.26.4.txt +++ /dev/null @@ -1 +0,0 @@ -d1099e2f64e9ad256d86a624904a6a5fdd28241bbfa478fcd25bc32af53e5200892ce8e4808be0bcdd8b42a74e5463565002eb8810928748d3390c5c7f4eb736 diff --git a/vendor/gems/graphql/guides/pro/checksums/pro-1.27.3.txt b/vendor/gems/graphql/guides/pro/checksums/pro-1.27.3.txt deleted file mode 100644 index 61e0c3af45f..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/pro-1.27.3.txt +++ /dev/null @@ -1 +0,0 @@ -eaecab7fe1956b08d6bd11009bc984c4876acc93ba1977b2da2af3dc1c0dfd6974d1d98a25fd8005aea6890a4cb01bea0d5d9621dfa97f549e7b9c9d3afd944d diff --git a/vendor/gems/graphql/guides/pro/checksums/pro-1.27.7.txt b/vendor/gems/graphql/guides/pro/checksums/pro-1.27.7.txt deleted file mode 100644 index fa59cc80fb6..00000000000 --- a/vendor/gems/graphql/guides/pro/checksums/pro-1.27.7.txt +++ /dev/null @@ -1 +0,0 @@ -3cee596b97b156879fce33e22a5e6ccea3bc2a4f44d27451d4a32d910cb108bce0f83a2121fd7b247ade237040a86b824b37f63dc2cff7e26b73398fb4468d27 diff --git a/vendor/gems/graphql/guides/pro/dashboard.md b/vendor/gems/graphql/guides/pro/dashboard.md deleted file mode 100644 index 0b3fbf22bc5..00000000000 --- a/vendor/gems/graphql/guides/pro/dashboard.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro -title: Dashboard -desc: Installing GraphQL-Pro's Dashboard -index: 4 -pro: true ---- - - -[GraphQL-Pro](https://graphql.pro) includes a web dashboard for monitoring {% internal_link "Operation Store", "/operation_store/overview" %} and {% internal_link "subscriptions", "/subscriptions/pusher_implementation" %}. - - - -## Installation - -To hook up the Dashboard, add it to `routes.rb` - -```ruby -# config/routes.rb - -# Include GraphQL::Pro's routing extensions: -using GraphQL::Pro::Routes - -Rails.application.routes.draw do - # ... - # Add the GraphQL::Pro Dashboard - # TODO: authorize, see below - mount MySchema.dashboard, at: "/graphql/dashboard" -end -``` - -With this configuration, it will be available at `/graphql/dashboard`. - -The dashboard is a Rack app, so you can mount it in Sinatra or any other Rack app. - -#### Lazy-loading the schema - -Alternatively, you can set up the dashboard to load the schema during the first request. To do that, initialize `GraphQL::Pro::Routes::Lazy` with a string that gives the fully-qualified name of your schema class, for example: - -```ruby -Rails.application.routes.draw do - # ... - # Add the GraphQL::Pro Dashboard - # TODO: authorize, see below - lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema") - mount lazy_routes.dashboard, at: "/graphql/dashboard" -end -``` - -With this setup, `MySchema` will be loaded when the dashboard serves its first request. This can speed up your application's boot in development since it doesn't load the whole GraphQL schema when building the routes. - -## Authorizing the Dashboard - -You should only allow admin users to see `/graphql/dashboard` because it allows viewers to delete stored operations. - -### Rails Routing Constraints - -Use [Rails routing constraints](https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints) to restrict access to authorized users, for example: - -```ruby -# Check the secure session for a staff flag: -STAFF_ONLY = ->(request) { request.session["staff"] == true } -# Only serve the GraphQL Dashboard to staff users: -constraints(STAFF_ONLY) do - mount MySchema.dashboard, at: "/graphql/dashboard" -end -``` - -### Rack Basic Authentication - -Insert the `Rack::Auth::Basic` middleware, before the web view. This prompts for a username and password when visiting the dashboard. - -```ruby -graphql_dashboard = Rack::Builder.new do - use(Rack::Auth::Basic) do |username, password| - username == ENV.fetch("GRAPHQL_USERNAME") && password == ENV.fetch("GRAPHQL_PASSWORD") - end - - run MySchema.dashboard -end -mount graphql_dashboard, at: "/graphql/dashboard" -``` diff --git a/vendor/gems/graphql/guides/pro/encoders.md b/vendor/gems/graphql/guides/pro/encoders.md deleted file mode 100644 index d2b7284b838..00000000000 --- a/vendor/gems/graphql/guides/pro/encoders.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro -title: Encrypted, Versioned Cursors and IDs -desc: Increased opacity and configurability for Relay identifiers -index: 6 -pro: true ---- - -`GraphQL::Pro` includes a mechanism for serving encrypted, versioned cursors and IDs. This provides some benefits: - -- Users can't reverse-engineer node IDs or connection cursors, removing a possible attack vector. -- You can gradually transition between cursor strategies, adding encrypting while supporting any "stale" encoders which clients already have. - -`GraphQL::Pro`'s encrypted encoders provide a few security features: - -- Key-based encryption by `aes-128-gcm` by default -- Authentication -- Nonces for cursors (but not IDs, that would be silly) - -## Defining an Encoder - -Encoders can be created by subclassing `GraphQL::Pro::Encoder`: - -```ruby -class MyEncoder < GraphQL::Pro::Encoder - key("f411f30...") - # optional: - tag("81ce51c307") -end -``` - -- `key` is the encryption key for this encoder. You can generate one with: `require "securerandom"; SecureRandom.bytes(16)` -- `tag`, if provided, is used as authentication data or for disambiguating versioned encoders - -## Encrypting Cursors - -Encrypt cursors by attaching an encrypted encoder to `Schema#cursor_encoder`: - -```ruby -class MySchema GraphQL::Schema - cursor_encoder(MyCursorEncoder) -end -``` - -Now, built-in connection implementations will use that encoder for cursors. - -If you implement your own connections, you can access the encoder's encryption methods via {{ "GraphQL::Pagination::Connection#encode" | api_doc }} and {{ "GraphQL::Pagination::Connection#decode" | api_doc }}. - - -## Encrypting IDs - -Encrypt IDs by using encoders in `Schema.id_from_object` and `Schema.object_from_id`: - -```ruby -class MySchema < GraphQL::Schema - def self.id_from_object(object, type, ctx) - id_data = "#{object.class.name}/#{object.id}" - MyIDEncoder.encode(id_data) - end - - def self.object_from_id(id, ctx) - id_data = MyIDEncoder.decode(id) - class_name, id = id_data.split("/") - class_name.constantize.find(id) - end -end -``` - -Note that IDs are _not_ encrypted with nonces. This means that if someone can _guess_ how IDs are constructed, they can determine the encryption key (a kind of [known-plaintext attack](https://en.wikipedia.org/wiki/Known-plaintext_attack)). To reduce this risk, make your plaintext IDs unpredictable, for example, by appending a salt or obfuscating their content. - -## Versioning - -You can combine several encoders into a single chain of versioned encoders. Pass them to `.versioned`, newest-to-oldest: - -```ruby -# Define some encoders ... -class NewSecureEncoder < GraphQL::Pro::Encoder - # ... -end - -class OldSecureEncoder < GraphQL::Pro::Encoder - # ... -end - -class LegacyInsecureEncoder < GraphQL::Pro::Encoder - # ... -end - -# Then order them by priority: -VersionedEncoder = GraphQL::Pro::Encoder.versioned( - # Newest: - NewSecureEncoder, - OldSecureEncoder, - # Oldest: - LegacyInsecureEncoder -) -``` - -When receiving an ID or cursor, a versioned encoders tries each encoder in sequence. When creating a new ID or cursor, the encoder always uses the first encoder. This way, clients will receiving _new_ encoders, but the server will still accept _old_ encoders (until the old one is removed from the list). - -`VersionedEncoder#decode_versioned` returns two values: the decoded data _and_ the encoder which successfully decoded it. You can use this to determine how to process decoded data. For example, you can switch on the encoder: - -```ruby -data, encoder = VersionedEncoder.decode_versioned(id) -case encoder -when UUIDEncoder - find_by_uuid(data) -when SQLPrimaryKeyEncoder - find_by_pk(data) -when nil - # `id` could not be decoded - nil -end -``` - -## Encoding - -By default, encrypted bytes is stringified as base-64. You can specific a custom encoder with the `Encoder#encoder` definition. For example, you could define an encode which uses URL-safe base-64 functions: - -```ruby -module URLSafeEncoder - def self.encode(str) - Base64.urlsafe_encode64(str) - end - def self.decode(str) - Base64.urlsafe_decode64(str) - end -end -``` - -Then attach it to your encoder: - -```ruby -class MyURLSafeEncoder < GraphQL::Pro::Encoder - encoder URLSafeEncoder -end -``` - -Now, these node IDs and cursors will be URL-safe! diff --git a/vendor/gems/graphql/guides/pro/home.md b/vendor/gems/graphql/guides/pro/home.md deleted file mode 100644 index ad063c209cd..00000000000 --- a/vendor/gems/graphql/guides/pro/home.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: guide -doc_stub: false -outbound_url: https://graphql.pro -title: GraphQL::Pro Home -section: GraphQL Pro -desc: Overview of GraphQL::Pro features -index: 0 -pro: true ---- diff --git a/vendor/gems/graphql/guides/pro/installation.md b/vendor/gems/graphql/guides/pro/installation.md deleted file mode 100644 index 2ae3fcdbe4a..00000000000 --- a/vendor/gems/graphql/guides/pro/installation.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro -title: Installation -desc: Get started with GraphQL::Pro -index: 1 -pro: true ---- - -`GraphQL::Pro` is distributed as a Ruby gem. When you buy `GraphQL::Pro`, you'll receive credentials, which you can register with bundler: - -```sh -bundle config gems.graphql.pro #{YOUR_CREDENTIALS} -``` - -Then, you can add `graphql-pro` to your Gemfile, which a custom `source`: - -```ruby -source "https://gems.graphql.pro" do - gem "graphql-pro" -end -``` - -Then, install the gem with Bundler: - -```sh -bundle install -``` - -Then, check out some of `GraphQL::Pro`'s features! - -## Updates - -To update `GraphQL::Pro`, use Bundler: - -```sh -bundle update graphql-pro -``` - -Be sure to check the [changelog](https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG-pro.md) between versions! - -## Dependencies - -`graphql-pro 1.0.0` requires `graphql ~>1.4`. The latest version requires `graphql =>1.7.6`. - -## Verifying Integrity - -You can verify the integrity of `graphql-pro` by getting its checksum and comparing it to the [published checksums](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/pro/checksums). - -Include the `graphql:pro:validate` task in your `Rakefile`: - -```ruby -# Rakefile -require "graphql/rake_task/validate" -``` - -Then invoke it with a version: - -``` -$ bundle exec rake graphql:pro:validate[1.0.0] -Validating graphql-pro v1.0.0 - - Checking for graphql-pro credentials... - ✓ found - - Fetching the gem... - ✓ fetched - - Validating digest... - ✓ validated from GitHub - ✓ validated from graphql-ruby.org -✔ graphql-pro 1.0.0 validated successfully! -``` - -In case of a failure, please {% open_an_issue "GraphQL Pro installation failure" %}: - -``` -Validating graphql-pro v1.4.800 - - Checking for graphql-pro credentials... - ✓ found - - Fetching the gem... - ✓ fetched - - Validating digest... - ✘ SHA mismatch: - Downloaded: c9cab2619aa6540605ce7922784fc84dbba3623383fdce6b17fde01d8da0aff49d666810c97f66310013c030e3ab7712094ee2d8f1ea9ce79aaf65c1684d992a - GitHub: 404: Not Found - graphql-ruby.org: 404: Not Found - - This download of graphql-pro is invalid, please open an issue: - https://github.com/rmosolgo/graphql-ruby/issues/new?title=graphql-pro%20digest%20mismatch%20(1.4.800) -``` diff --git a/vendor/gems/graphql/guides/pro/privacy.md b/vendor/gems/graphql/guides/pro/privacy.md deleted file mode 100644 index c56f8d83657..00000000000 --- a/vendor/gems/graphql/guides/pro/privacy.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: GraphQL Pro -title: Privacy -desc: Privacy Policy for GraphQL::Pro -index: 7 ---- - -The following statement describes what data GraphQL::Pro collects during normal operation and how that data is used. - -## What Data We Collect And How It's Used - -GraphQL::Pro collects the following kinds of data: - -Kind of Data | Use ------- | ----- -Cookies | User authentication -HTTP traffic information, such as IP address and bundler credential | Authentication, system operation -User-provided profile information, such as contact email address and company name | Business communications, newsletter delivery (if signed up) -Billing information | Recurring payment for GraphQL::Pro license - -The `graphql-pro` Ruby gem collects no data and never "phones home" for any purpose. - -GraphQL::Pro is not directed at children under the age of 13. If you are under age 13, please do not use GraphQL::Pro. - -## Third-Party Services - -Use of GraphQL::Pro includes the following third-party services: - -Name of Service | Use -------|------ -GitHub Pages | Website Hosting -Digital Ocean | Cloud Application Hosting -Heroku | Cloud Application Hosting -Bugsnag | Application Monitoring -New Relic APM | Application Monitoring -Papertrail | Application Monitoring -Stripe | Payment Processing -Buttondown | Newsletter Management -Google Apps | Company Infrastructure - -## Data Security - -GraphQL::Pro does not collect ["Sensitive Personal Information"](https://gdpr-info.eu/art-9-gdpr/). GraphQL::Pro's systems are secured with strong, unique passwords and two-factor authentication is enabled wherever possible. - -You are responsible for using a strong, unique password to log into https://billing.graphql.pro. - -## More Information - -This document is managed [on GitHub](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/pro/privacy.md). You can use a GitHub account to watch for changes or subscribe to a [public RSS feed](https://github.com/rmosolgo/graphql-ruby/commits/master.atom). - -For questions, comments, or requests regarding this policy, please contact [info@graphql.pro](mailto:info@graphql.pro) diff --git a/vendor/gems/graphql/guides/queries/ast_analysis.md b/vendor/gems/graphql/guides/queries/ast_analysis.md deleted file mode 100644 index 2025774f1e2..00000000000 --- a/vendor/gems/graphql/guides/queries/ast_analysis.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Queries -title: Ahead-of-Time AST Analysis -desc: Check incoming query strings and reject them if they don't pass your checks -index: 1 -redirect_from: - - /queries/analysis/ ---- - -You can do ahead-of-time analysis for your queries. - -The primitive for analysis is {{ "GraphQL::Analysis::Analyzer" | api_doc }}. Analyzers must inherit from this base class and implement the desired methods for analysis. - -## Using Analyzers - -Query analyzers are added to the schema with `query_analyzer`, for example: - -```ruby -class MySchema < GraphQL::Schema - query_analyzer MyQueryAnalyzer -end -``` - -Pass the **class** (and not an _instance_) of your analyzer. The analysis engine will take care of instantiating your analyzers with the query. - -## Analyzer API - -Analyzers respond to methods similar to AST visitors. They're named like `on_enter_#{ast_node}` and `on_leave_#{ast_node}`. Methods are called with three arguments: - -- `node`: The current AST node (being entered or left) -- `parent`: The AST node which precedes this one in the tree -- `visitor`: A {{ "GraphQL::Analysis::Visitor" | api_doc }} which is managing this analysis run - -For example: - -```ruby -class BasicCounterAnalyzer < GraphQL::Analysis::Analyzer - def initialize(query_or_multiplex) - super - @fields = Set.new - @arguments = Set.new - end - - # Visitors are all defined on the Analyzer base class - # We override them for custom analyzers. - def on_leave_field(node, _parent, _visitor) - @fields.add(node.name) - end - - def result - # Do something with the gathered result. - Analytics.log(@fields) - end -end -``` - -In this example, we counted every field, no matter if it was on fragment definitions -or if it was skipped by directives. If we want to detect those contexts, we can use helper -methods: - -```ruby -class BasicFieldAnalyzer < GraphQL::Analysis::Analyzer - def initialize(query_or_multiplex) - super - @fields = Set.new - end - - # Visitors are all defined on the Analyzer base class - # We override them for custom analyzers. - def on_leave_field(node, _parent, visitor) - if visitor.skipping? || visitor.visiting_fragment_definition? - # We don't want to count skipped fields or fields - # inside fragment definitions - else - @fields.add(node.name) - end - end - - def result - Analytics.log(@fields) - end -end -``` - -See {{ "GraphQL::Analysis::Visitor" | api_doc }} for more information about the `visitor` object. - -### Field Arguments - -Usually, analyzers will use `on_enter_field` and `on_leave_field` to process queries. To get a field's arguments during analysis, use `visitor.query.arguments_for(node, visitor.field_definition)` ({{ "GraphQL::Query#arguments_for" | api_doc }}). That method returns coerced argument values and normalizes argument literals and variable values. - -### Errors - -It is still possible to return errors from an analyzer. To reject a query and halt its execution, you may return {{ "GraphQL::AnalysisError" | api_doc }} in the `result` method: - -```ruby -class NoFieldsCalledHello < GraphQL::Analysis::Analyzer - def on_leave_field(node, _parent, visitor) - if node.name == "hello" - @field_called_hello = true - end - end - - def result - GraphQL::AnalysisError.new("A field called `hello` was found.") if @field_called_hello - end -end -``` - -### Conditional Analysis - -Some analyzers might only make sense in certain context, or some might be too expensive to run for every query. To handle these scenarios, your analyzers may answer to an `analyze?` method: - -```ruby -class BasicFieldAnalyzer < GraphQL::Analysis::Analyzer - # Use the analyze? method to enable or disable a certain analyzer - # at query time. - def analyze? - !!subject.context[:should_analyze] - end - - def on_leave_field(node, _parent, visitor) - # ... - end - - def result - # ... - end -end -``` - -## Analyzing Multiplexes - -Analyzers are initialized with the _unit of analysis_, available as `subject`. - -When analyzers are hooked up to multiplexes, `query` is `nil`, but `multiplex` returns the subject of analysis. You can use `visitor.query` inside visit methods to reference the query that owns the current AST node. - -Note that some built-in analyzers (eg `AST::MaxQueryDepth`) support multiplexes even though `Query` is in their name. diff --git a/vendor/gems/graphql/guides/queries/backtrace_annotations.md b/vendor/gems/graphql/guides/queries/backtrace_annotations.md deleted file mode 100644 index 4accf58194f..00000000000 --- a/vendor/gems/graphql/guides/queries/backtrace_annotations.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Backtrace Annotations -layout: guide -doc_stub: false -search: true -section: Queries -desc: Use the GraphQL backtrace for debugging -index: 12 -experimental: true ---- - -`context` objects have a `backtrace` which shows its GraphQL context. You can print the backtrace during query execution: - -```ruby -puts context.backtrace -# Loc | Field | Object | Arguments | Result -# 3:13 | User.login | # | {"message"=>"Boom"} | # -# 2:11 | Query.user | nil | {"login"=>"rmosolgo"} | {} -# 1:9 | query | nil | {"msg"=>"Boom"} | -``` - -The backtrace contains some execution data: - -- `Loc` is the `line:column` of the field in the query string -- `Field` is the `TypeName.fieldName` of the fields in the backtrace -- `Object` is the `obj` for query resolution (used for resolving the given field), equivalent to `obj.inspect` -- `Arguments` are the GraphQL arguments for field resolution (including any default values and variable values) -- `Result` is the GraphQL-ready result which is being constructed (it may be incomplete if the query is still in-progress) - -## Wrapping Errors - -You can wrap unhandled errors with a GraphQL error with `GraphQL::Backtrace`. - -To enable this feature for a query, add `backtrace: true` to your `context`, for example: - -```ruby -# Wrap this query with backtrace annotation -MySchema.execute(query_string, context: { backtrace: true }) -``` - -Or, to _always_ wrap backtraces, add it to your schema definition with `use`, for example: - -```ruby -class MySchema < GraphQL::Schema - # Always wrap backtraces with GraphQL annotation - use GraphQL::Backtrace -end -``` - -Now, any unhandled errors will be wrapped by `GraphQL::Backtrace::TracedError`, which prints out the GraphQL backtrace, too. For example: - -``` -Unhandled error during GraphQL execution: - - This is broken: Boom - /Users/rmosolgo/code/graphql-ruby/spec/graphql/backtrace_spec.rb:27:in `block (3 levels) in ' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/schema/build_from_definition/resolve_map.rb:57:in `call' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/schema/build_from_definition.rb:171:in `block in build_object_type' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/schema/build_from_definition.rb:280:in `block (2 levels) in build_fields' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/field.rb:228:in `resolve' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/execution/execute.rb:253:in `call' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/schema/middleware_chain.rb:45:in `invoke_core' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/schema/middleware_chain.rb:38:in `invoke' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/execution/execute.rb:107:in `resolve_field' - /Users/rmosolgo/code/graphql-ruby/lib/graphql/execution/execute.rb:71:in `block (2 levels) in resolve_selection' - ... and 65 more lines - -Use #cause to access the original exception (including #cause.backtrace). - -GraphQL Backtrace: -Loc | Field | Object | Arguments | Result -3:13 | Thing.raiseField as boomError | :something | {"message"=>"Boom"} | # -2:11 | Query.field1 | "Root" | {} | {} -1:9 | query | "Root" | {"msg"=>"Boom"} | {} -``` diff --git a/vendor/gems/graphql/guides/queries/complexity_and_depth.md b/vendor/gems/graphql/guides/queries/complexity_and_depth.md deleted file mode 100644 index b316f3650d9..00000000000 --- a/vendor/gems/graphql/guides/queries/complexity_and_depth.md +++ /dev/null @@ -1,321 +0,0 @@ ---- -title: Complexity & Depth -layout: guide -doc_stub: false -search: true -section: Queries -desc: Limiting query depth and field selections -index: 4 ---- - -GraphQL-Ruby ships with some validations based on {% internal_link "query analysis", "/queries/ast_analysis" %}. You can customize them as-needed, too. - -## Prevent deeply-nested queries - -You can also reject queries based on the depth of their nesting. You can define `max_depth` at schema-level or query-level: - -```ruby -# Schema-level: -class MySchema < GraphQL::Schema - # ... - max_depth 15 -end - -# Query-level, which overrides the schema-level setting: -MySchema.execute(query_string, max_depth: 20) -``` - -By default, **introspection fields are counted**. The default introspection query requires at least `max_depth 13`. You can also configure your schema not to count introspection fields with `max_depth ..., count_introspection_fields: false`. - -You can use `nil` to disable the validation: - -```ruby -# This query won't be validated: -MySchema.execute(query_string, max_depth: nil) -``` - -To get a feeling for depth of queries in your system, you can extend {{ "GraphQL::Analysis::QueryDepth" | api_doc }}. Hook it up to log out values from each query: - -```ruby -class LogQueryDepth < GraphQL::Analysis::QueryDepth - def result - query_depth = super - message = "[GraphQL Query Depth] #{query_depth} || staff? #{query.context[:current_user].staff?}" - Rails.logger.info(message) - end -end - -class MySchema < GraphQL::Schema - query_analyzer(LogQueryDepth) -end -``` - -## Prevent complex queries - -Fields have a "complexity" value which can be configured in their definition. It can be a constant (numeric) value, or a proc. If no `complexity` is defined for a field, it will default to a value of `1`. It can be defined as a keyword _or_ inside the configuration block. For example: - -```ruby -# Constant complexity: -field :top_score, Integer, null: false, complexity: 10 - -# Dynamic complexity: -field :top_scorers, [PlayerType], null: false do - argument :limit, Integer, limit: false, default_value: 5 - complexity ->(ctx, args, child_complexity) { - if ctx[:current_user].staff? - # no limit for staff users - 0 - else - # `child_complexity` is the value for selections - # which were made on the items of this list. - # - # We don't know how many items will be fetched because - # we haven't run the query yet, but we can estimate by - # using the `limit` argument which we defined above. - args[:limit] * child_complexity - end - } -end -``` - -Then, define your `max_complexity` at the schema-level: - -```ruby -class MySchema < GraphQL::Schema - # ... - max_complexity 100 -end -``` - -Or, at the query-level, which overrides the schema-level setting: - -```ruby -MySchema.execute(query_string, max_complexity: 100) -``` - -Using `nil` will disable the validation: - -```ruby -# 😧 Anything goes! -MySchema.execute(query_string, max_complexity: nil) -``` - -To get a feeling for complexity of queries in your system, you can extend {{ "GraphQL::Analysis::QueryComplexity" | api_doc }}. Hook it up to log out values from each query: - -```ruby -class LogQueryComplexityAnalyzer < GraphQL::Analysis::QueryComplexity - # Override this method to _do something_ with the calculated complexity value - def result - complexity = super - message = "[GraphQL Query Complexity] #{complexity} | staff? #{query.context[:current_user].staff?}" - Rails.logger.info(message) - end -end - -class MySchema < GraphQL::Schema - query_analyzer(LogQueryComplexityAnalyzer) -end -``` - -By default, **introspection fields are counted**. You can also configure your schema not to count introspection fields with `max_complexity ..., count_introspection_fields: false`. - -#### Connection fields - -By default, GraphQL-Ruby calculates a complexity value for connection fields by: - -- adding `1` for `pageInfo` and each of its subselections -- adding `1` for `count`, `totalCount`, or `total` -- adding `1` for the connection field itself -- multiplying the complexity of other fields by the largest possible page size, which is the greater of `first:` or `last:`, or if neither of those are given it will go through each of `default_page_size`, the schema's `default_page_size`, `max_page_size`, and then the schema's `default_max_page_size`. - - (If no default page size or max page size can be determined, then the analysis crashes with an internal error -- set `default_page_size` or `default_max_page_size` in your schema to prevent this.) - -For example, this query has complexity `26`: - -```graphql -query { - author { # +1 - name # +1 - books(first: 10) { # +1 - nodes { # +10 (+1, multiplied by `first:` above) - title # +10 (ditto) - } - pageInfo { # +1 - endCursor # +1 - } - totalCount # +1 - } - } -} -``` - -To customize this behavior, implement `def calculate_complexity(query:, nodes:, child_complexity:)` in your base field class, handling the case where `self.connection?` is `true`: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - def calculate_complexity(query:, nodes:, child_complexity:) - if connection? - # Custom connection calculation goes here - else - super - end - end -end -``` - -## How complexity scoring works - -GraphQL Ruby's complexity scoring algorithm is biased towards selection fairness. While highly accurate, its results are not always intuitive. Here's an example query performed on the [Shopify Admin API](https://shopify.dev/docs/api/admin-graphql): - -```graphql -query { - node(id: "123") { # interface Node - id - ...on HasMetafields { # interface HasMetafields - metafield(key: "a") { - value - } - metafields(first: 10) { - nodes { - value - } - } - } - ...on Product { # implements HasMetafields - title - metafield(key: "a") { - definition { - description - } - } - } - ...on PriceList { - name - catalog { - id - } - } - } -} -``` - -First, GraphQL Ruby allows field definitions to specify a `complexity` attribute that provides a complexity score (or a proc that computes a score) for each field. Let's say that this schema defines a system where: - -- Leaf fields cost `0` -- Composite fields cost `1` -- Connection fields cost `children * input size` - -Given these parameters, we get an itemized scoring distribution of: - -```graphql -query { - node(id: "123") { # 1, composite - id # 0, leaf - ...on HasMetafields { - metafield(key: "a") { # 1, composite - value # 0, leaf - } - metafields(first: 10) { # 1 * 10, connection - nodes { # 1, composite - value # 0, leaf - } - } - } - ...on Product { - title # 0, leaf - metafield(key: "a") { # 1, composite - definition { # 1, composite - description # 0, leaf - } - } - } - ...on PriceList { - name # 0, leaf - catalog { # 1, composite - id # 0, leaf - } - } - } -} -``` - -However, we cannot naively tally these itemized scores without over-costing the query. Consider: - -- The `node` scope makes many _possible_ selections on an abstract type, so we need the maximum among concrete possibilities for a fair representation. -- A `node.metafield` selection path is duplicated across the `HasMetafields` and `Product` selection scopes. This path will only resolve once, so should also only cost once. - -To reconcile these possibilities, the [complexity algorithm](https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/analysis/ast/query_complexity.rb) breaks the selection down into a tree of types mapped to possible selections, across which lexical selections can be coalesced and deduplicated (pseudocode): - -```ruby -{ - Schema::Query => { - "node" => { - Schema::Node => { - "id" => nil, - }, - Schema::HasMetafields => { - "metafield" => { - Schema::Metafield => { - "value" => nil, - }, - }, - "metafields" => { - Schema::Metafield => { - "nodes" => { ... }, - }, - }, - }, - Schema::Product => { - "title" => nil, - "metafield" => { - Schema::Metafield => { - "definition" => { ... }, - }, - }, - }, - Schema::PriceList => { - "name" => nil, - "catalog" => { - Schema::Catalog => { - "id" => nil, - }, - }, - }, - }, - }, -} -``` - -This aggregation provides a new perspective on the scoring where _possible typed selections_ have costs rather than individual fields. In this normalized view, `Product` acquires the `HasMetafields` interface costs, and ignores a duplicated path. Ultimately the maximum of possible typed costs is used, making this query cost `12`: - -```graphql -query { - node(id: "123") { # max(11, 12, 1) = 12 - id - ...on HasMetafields { # 1 + 10 = 11 - metafield(key: "a") { # 1 - value - } - metafields(first: 10) { # 10 - nodes { - value - } - } - } - ...on Product { # 1 + 11 from HasMetafields = 12 - title - metafield(key: "a") { # duplicated in HasMetafields - definition { # 1 - description - } - } - } - ...on PriceList { # 1 = 1 - name - catalog { # 1 - id - } - } - } -} -``` diff --git a/vendor/gems/graphql/guides/queries/executing_queries.md b/vendor/gems/graphql/guides/queries/executing_queries.md deleted file mode 100644 index 8d51a1c9a20..00000000000 --- a/vendor/gems/graphql/guides/queries/executing_queries.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Queries -title: Executing Queries -desc: Evaluate GraphQL queries with your schema -index: 0 ---- - - -You can execute queries with your {{ "GraphQL::Schema" | api_doc }} and get a Ruby Hash as a result. For example, to execute a query from a string: - -```ruby -query_string = "{ ... }" -MySchema.execute(query_string) -# { -# "data" => { ... } -# } -``` - -Or, you can execute multiple queries at once: - -```ruby -MySchema.multiplex([ - {query: query_string_1}, - {query: query_string_2}, - {query: query_string_3}, -]) -# [ -# { "data" => { ... } }, -# { "data" => { ... } }, -# { "data" => { ... } }, -# ] -``` - -There are also several options you can use: - -- `variables:` provides values for `$`-named [query variables](https://graphql.org/learn/queries/#variables) -- `context:` accepts application-specific data to pass to `resolve` functions -- `root_value:` will be provided to root-level `resolve` functions as `obj` -- `operation_name:` picks a [named operation](https://graphql.org/learn/queries/#operation-name) from the incoming string to execute -- `document:` accepts an already-parsed query (instead of a string), see {{ "GraphQL.parse" | api_doc }} -- `validate:` may be `false` to skip static validation for this query -- `max_depth:` and `max_complexity:` may override schema-level values - -Some of these options are described in more detail below, see {{ "GraphQL::Query#initialize" | api_doc }} for more information. - -## Variables - -GraphQL provides [query variables](https://graphql.org/learn/queries/#variables) as a way to parameterize query strings. If your query string contains variables, you can provide values in a hash of `{ String => value }` pairs. The keys should _not_ contain `"$"`. - -For example, to provide variables to a query: - -```ruby -query_string = " - query getPost($postId: ID!) { - post(id: $postId) { - title - } - }" - -variables = { "postId" => "1" } - -MySchema.execute(query_string, variables: variables) -``` - -If the variable is a {{ "GraphQL::InputObjectType" | api_doc }}, you can provide a nested hash, for example: - -```ruby -query_string = " -mutation createPost($postParams: PostInput!, $createdById: ID!){ - createPost(params: $postParams, createdById: $createdById) { - id - title - createdBy { name } - } -} -" - -variables = { - "postParams" => { - "title" => "...", - "body" => "..." - }, - "createdById" => "5", -} - -MySchema.execute(query_string, variables: variables) -``` - -## Context - -You can provide application-specific values to GraphQL as `context:`. This is available in many places: - -- `resolve` functions -- `Schema#resolve_type` hook -- ID generation & fetching - -Common uses for `context:` include the current user or auth token. To provide a `context:` value, pass a hash to `Schema#execute`: - -```ruby -context = { - current_user: session[:current_user], - current_organization: session[:current_organization], -} - -MySchema.execute(query_string, context: context) -``` - -Then, you can access those values during execution: - -```ruby -field :post, Post do - argument :id, ID -end - -def post(id:) - context[:current_user] # => # - # ... -end -``` - -Note that `context` is _not_ the hash that you passed it. It's an instance of {{ "GraphQL::Query::Context" | api_doc }}, but it delegates `#[]`, `#[]=`, and a few other methods to the hash you provide. - -### Scoped Context - -`context` is shared by the whole query. Anything you add to `context` will be accessible by any other field in the query (although GraphQL-Ruby's order of execution can vary). - -However, "scoped context" can be used to assign values into `context` that are only available in the current field and the _children_ of the current field. For example, in this query: - -```graphql -{ - posts { - comments { - author { - isOriginalPoster - } - } - } -} -``` - -You could use "scoped context" to implement `isOriginalPoster`, based on the parent `comments` field. - -{% callout warning %} - -Using scoped context may result in a violation of [the GraphQL specification](https://spec.graphql.org/draft/#sel-EABDLDFAACHAo3V) and -break normalized client stores, which assume that a given object always -has the same values for its fields. - -See ["Referencing ancestors breaks normalized stores"](https://benjie.dev/graphql/ancestors#breaks-normalized-stores) -for details about this pitfall and alternative approaches which avoid it. - -{% endcallout %} - -In `def comments`, add `:current_post` to scoped context using `context.scoped_set!`: - -```ruby -class Types::Post < Types::BaseObject - # ... - def comments - context.scoped_set!(:current_post, object) - object.comments - end -end -``` - -Then, inside `User` (assuming `author` resolves to `Types::User`), you can check `context[:current_post]`: - -```ruby -class Types::User < Types::BaseObject - # ... - def is_original_poster - current_post = context[:current_post] - current_post && current_post.author == object - end -end -``` - -`context[:current_post]` will be present if an "upstream" field assigned it with `scoped_set!`. - -`context.scoped_merge!({ ... })` is also available for setting multiple keys at once. - -**Note**: With batched data loading (eg, GraphQL-Batch), scoped context might not work because of GraphQL-Ruby's control flow jumps from one field to the next. In that case, use `scoped_ctx = context.scoped` to grab a scoped context reference _before_ calling a loader, then used `scoped_ctx.set!` or `scoped_ctx.merge!` to modify scoped context inside the promise body. For example: - -```ruby -# For use with GraphQL-Batch promises: -scoped_ctx = context.scoped -SomethingLoader.load(:something).then do |thing| - scoped_ctx.set!(:thing_name, thing.name) -end -``` - -## Root Value - -You can provide a root `object` value with `root_value:`. For example, to base the query off of the current organization: - -```ruby -current_org = session[:current_organization] -MySchema.execute(query_string, root_value: current_org) -``` - -That value will be provided to root-level fields, such as mutation fields. For example: - -```ruby -class Types::MutationType < GraphQL::Schema::Object - field :create_post, Post - - def create_post(**args) - object # => # - # ... - end -end -``` - -{{ "GraphQL::Schema::Mutation" | api_doc }} fields will also receive `root_value:` as `obj` (assuming they're attached directly to your `MutationType`). diff --git a/vendor/gems/graphql/guides/queries/logging.md b/vendor/gems/graphql/guides/queries/logging.md deleted file mode 100644 index 75e5d9ec354..00000000000 --- a/vendor/gems/graphql/guides/queries/logging.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Queries -title: Logging -desc: Development output from GraphQL-Ruby -index: 12 ---- - -At runtime, GraphQL-Ruby will output debug information using {{ "GraphQL::Query.logger" | api_doc }}. By default, this uses `Rails.logger`. To see output, make sure `config.log_level = :debug` is set. (This information isn't meant for production logs.) - -You can configure a custom logger with {{ "GraphQL::Schema.default_logger" | api_doc }}, for example: - -```ruby -class MySchema < GraphQL::Schema - # This logger will be used by queries during execution: - default_logger MyCustomLogger.new -end -``` - -You can also pass `context[:logger]` to provide a logger during execution. diff --git a/vendor/gems/graphql/guides/queries/lookahead.md b/vendor/gems/graphql/guides/queries/lookahead.md deleted file mode 100644 index 8a5541d4851..00000000000 --- a/vendor/gems/graphql/guides/queries/lookahead.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Queries -title: Lookahead -desc: Detecting child selections during field resolution -index: 11 ---- - -GraphQL-Ruby 1.9+ includes {{ "GraphQL::Execution::Lookahead" | api_doc }} for checking whether child fields are selected. You can use this to optimize database access, for example, selecting only the _needed_ fields from the database. - -## Getting a Lookahead - -Add `extras: [:lookahead]` to your field configuration to receive an injected lookahead: - -```ruby -field :files, [Types::File], null: false, extras: [:lookahead] -``` - -Then, update your resolver method to accept a `lookahead:` argument: - -```ruby -def files(lookahead:) - # ... -end -``` - -That argument will be injected by the GraphQL runtime. - -## Using a lookahead - -Inside your field resolver, you can use the lookahead to check for child fields. For example, you can check for a __specific selection__: - -```ruby -def files(lookahead:) - if lookahead.selects?(:full_path) - # This is a query like `files { fullPath ... }` - else - # This query doesn't have `fullPath` - end -end -``` - -Or, you can list __all the selected fields__: - -```ruby -def files(lookahead:) - all_selections = lookahead.selections.map(&:name) - if all_selections == [:name] - # Only `files { name }` was selected, use a fast cached value: - object.file_names.map { |n| { name: n }} - else - # Lots of fields were selected, fall back to a more resource-intensive approach - FileSystemHelper.load_files_for(object) - end -end -``` - -Lookaheads are _chainable_, so you can use them to check __nested selections__ too: - -```ruby -def files(lookahead:) - if lookahead.selection(:history).selects?(:author) - # For example, `files { history { author { ... } } }` - # We're checking for commit authors, so load those objects appropriately ... - else - # Not selecting commit authors ... - end -end -``` - -Nested lookaheads return empty objects when there's no selection (not `nil`), so the code above will never have a "no method error on `nil`". - -## Lookaheads with connections - -If you want to see what selections were made on the items in a connection, you can use nested lookaheads. However, don't forget to check for `edges { node }` _and_ `nodes { ... }`, if you support that shortcut field. For example: - -```ruby -field :products, Types::Product.connection_type, null: false, extras: [:lookahead] - -def products(lookahead:) - selects_quantity_available = lookahead.selection(:nodes).selects?(:quantity_available) || - # ^^ check for `products { nodes { quantityAvailable } }` - lookahead.selection(:edges).selection(:node).selects?(:quantity_available) - # ^^ check for `products { edges { node { quantityAvailable } } }` - - if selects_quantity_available - # ... - else - # ... - end -end -``` - -That way, you can check for specific selections on the nodes in a connection. - -## Lookaheads with aliases - -If you want to find selection by its [alias](https://spec.graphql.org/June2018/#sec-Field-Alias), you can use `#alias_selection(...)` or check if it exists with `#selects_alias?`. In this case, the lookahead will check if there is a field with the provided alias. - - -For example, this query can find a bird species by its name: - -```graphql -query { - gull: findBirdSpecies(byName: "Laughing Gull") { - name - } - - tanager: findBirdSpecies(byName: "Scarlet Tanager") { - name - } -} -``` - -You can get the lookahead for each selection in a following way: - -```ruby -def find_bird_species(by_name:, lookahead:) - if lookahead.selects_alias?("gull") - lookahead.alias_selection("gull") - end -end -``` diff --git a/vendor/gems/graphql/guides/queries/multiplex.md b/vendor/gems/graphql/guides/queries/multiplex.md deleted file mode 100644 index c131e7e6145..00000000000 --- a/vendor/gems/graphql/guides/queries/multiplex.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Multiplex -layout: guide -doc_stub: false -search: true -section: Queries -desc: Run multiple queries concurrently -index: 10 ---- - -Some clients may send _several_ queries to the server at once (for example, [Apollo Client's query batching](https://www.apollographql.com/docs/react/api/link/apollo-link-batch-http/)). You can execute them concurrently with {{ "Schema#multiplex" | api_doc }}. - -Multiplex runs have their own context, analyzers and instrumentation. - -__NOTE:__ As an implementation detail, _all_ queries run inside multiplexes. That is, a stand-alone query is executed as a "multiplex of one", so instrumentation and multiplex analyzers and tracers _will_ apply to standalone queries run with `MySchema.execute(...)`. - -## Concurrent Execution - -To run queries concurrently, build an array of query options, using `query:` for the query string. For example: - -```ruby -# Prepare the context for each query: -context = { - current_user: current_user, -} - -# Prepare the query options: -queries = [ - { - query: "query Query1 { someField }", - variables: {}, - operation_name: 'Query1', - context: context, - }, - { - query: "query Query2 ($num: Int){ plusOne(num: $num) }", - variables: { num: 3 }, - operation_name: 'Query2', - context: context, - } -] -``` - -Then, pass them to `Schema#multiplex`: - -```ruby -results = MySchema.multiplex(queries) -``` - -`results` will contain the result for each query in `queries`. __NOTE:__ The results will always be in the same order that their respective requests were sent in. - -## Apollo Query Batching - -Apollo sends batches of queries as an array of queries. Rails' ActionDispatch will parse the request and put the result into the `_json` field of the `params` variable. You also need to ensure that your schema can handle both batched and non-batched queries, below is an example of the default GraphqlController rewritten to handle Apollo batches: - -```ruby -def execute - context = {} - - # Apollo sends the queries in an array when batching is enabled. The data ends up in the _json field of the params variable. - # see the Apollo Documentation about query batching: https://www.apollographql.com/docs/react/api/link/apollo-link-batch-http/ - result = if params[:_json] - queries = params[:_json].map do |param| - { - query: param[:query], - operation_name: param[:operationName], - variables: ensure_hash(param[:variables]), - context: context - } - end - MySchema.multiplex(queries) - else - MySchema.execute( - params[:query], - operation_name: params[:operationName], - variables: ensure_hash(params[:variables]), - context: context - ) - end - - render json: result, root: false -end -``` - -## Validation and Error Handling - -Each query is validated and {% internal_link "analyzed","/queries/ast_analysis" %} independently. The `results` array may include a mix of successful results and failed results. - -## Multiplex-Level Context - -You can add values to {{ "Execution::Multiplex#context" | api_doc }} by providing a `context:` hash: - -```ruby -MySchema.multiplex(queries, context: { current_user: current_user }) -``` - -This will be available to instrumentation as `multiplex.context[:current_user]` (see below). - -## Multiplex-Level Analysis - -You can analyze _all_ queries in a multiplex by adding a multiplex analyzer. For example: - -```ruby -class MySchema < GraphQL::Schema - # ... - multiplex_analyzer(MyAnalyzer) -end -``` - -The API is the same as {% internal_link "query analyzers","/queries/ast_analysis#analyzing-multiplexes" %}. - -Multiplex analyzers may return {{ "AnalysisError" | api_doc }} to halt execution of the whole multiplex. - -## Multiplex Tracing - -You can add hooks for each multiplex run with {% internal_link "trace modules", "/queries/tracing" %}. - -The trace module may implement `def execute_multiplex(multiplex:)` which `yield`s to allow the multiplex to execute. See {{ "Execution::Multiplex" | api_doc }} for available methods. - -For example: - -```ruby -# Count how many queries are in the multiplex run: -module MultiplexCounter - def execute_multiplex(multiplex:) - Rails.logger.info("Multiplex size: #{multiplex.queries.length}") - yield - end -end - -# ... - -class MySchema < GraphQL::Schema - # ... - trace_with(MultiplexCounter ) -end -``` - -Now, `MultiplexCounter#execute_multiplex` will be called for each execution, logging the size of each multiplex. diff --git a/vendor/gems/graphql/guides/queries/perfetto_example.png b/vendor/gems/graphql/guides/queries/perfetto_example.png deleted file mode 100644 index eba506ce4e454953f888a8bd1a63ec1fd9b74974..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114553 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 E1BR`I_5c6? diff --git a/vendor/gems/graphql/guides/queries/phases_of_execution.md b/vendor/gems/graphql/guides/queries/phases_of_execution.md deleted file mode 100644 index e75989a3ea0..00000000000 --- a/vendor/gems/graphql/guides/queries/phases_of_execution.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Queries -title: Phases of Execution -desc: The steps GraphQL takes to run your query -index: 2 ---- - -When GraphQL receives a query string, it goes through these steps: - -- Tokenize: {{ "GraphQL::Language::Lexer" | api_doc }} splits the string into a stream of tokens -- Parse: {{ "GraphQL::Language::Parser" | api_doc }} builds an abstract syntax tree (AST) out of the stream of tokens -- Validate: {{ "GraphQL::StaticValidation::Validator" | api_doc }} validates the incoming AST as a valid query for the schema -- Analyze: If there are any query analyzers, they are run with {{ "GraphQL::Analysis.analyze_query" | api_doc }} -- Execute: The query is traversed, `resolve` functions are called and the response is built -- Respond: The response is returned as a {{ "GraphQL::Query::Result" | api_doc }} diff --git a/vendor/gems/graphql/guides/queries/response_extensions.md b/vendor/gems/graphql/guides/queries/response_extensions.md deleted file mode 100644 index 5602de450fe..00000000000 --- a/vendor/gems/graphql/guides/queries/response_extensions.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Response Extensions -layout: guide -doc_stub: false -search: true -section: Queries -desc: Adding "extensions" to the response hash -index: 12 ---- - -During query execution, you can add to the response's `"extensions" => { ... }` Hash. By default, no `"extensions"` key is present in the result, but if you call the method below, it will be present with the given values. - -To add to `"extensions"`, call `context.response_extensions[key] = value` during execution. For example: - -```ruby -field :to_dos, [ToDo] - -def to_dos - warnings = context.response_extensions["warnings"] ||= [] - warnings << "To-Dos will be disabled on Jan. 31, 2022." - context[:current_user].deprecated_to_dos -end -``` - - -That would add to the final query response: - -```ruby -{ - "data" => { ... }, - "extensions" => { - "warnings" => ["To-Dos will be disabled on Jan. 31, 2022"], - }, -} -``` - -Values written to `context.response_extensions` are added to the GraphQL response verbatim. diff --git a/vendor/gems/graphql/guides/queries/timeout.md b/vendor/gems/graphql/guides/queries/timeout.md deleted file mode 100644 index 2d52189947f..00000000000 --- a/vendor/gems/graphql/guides/queries/timeout.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Timeout -layout: guide -doc_stub: false -search: true -section: Queries -desc: Cutting off GraphQL execution -index: 5 ---- - -You can apply a timeout to query execution with the `GraphQL::Schema::Timeout` plugin. For example: - -```ruby -class MySchema < GraphQL::Schema - use GraphQL::Schema::Timeout, max_seconds: 2 -end -``` - -After `max_seconds`, no new fields will be resolved. Instead, errors will be added to the `errors` key for fields that weren't resolved. - -__Note__ that this _does not interrupt_ field execution (doing so is [buggy](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/)). If you're making external calls (eg, HTTP requests or database queries), make sure to use a library-specific timeout for that operation (eg, [Redis timeout](https://github.com/redis/redis-rb#timeouts), [Net::HTTP](https://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html)'s `ssl_timeout`, `open_timeout`, and `read_timeout`). - -## Custom Error Handling - -To log the error, provide a subclass of `GraphQL::Schema::Timeout` with an overridden `handle_timeout` method: - -```ruby -class MyTimeout < GraphQL::Schema::Timeout - def handle_timeout(error, query) - Rails.logger.warn("GraphQL Timeout: #{error.message}: #{query.query_string}") - end -end - -class MySchema < GraphQL::Schema - use MyTimeout, max_seconds: 2 -end -``` - -## Customizing the Timeout Window - -To dynamically pick a timeout duration (or bypass it), override {{ "GraphQL::Schema::Timeout#max_seconds" | api_doc }} in your subclass. To bypass the timeout altogether, `max_seconds` can return `false`. - -For example: - -```ruby -class MyTimeout < GraphQL::Schema::Timeout - # Allow 10s for an incoming mutation, but don't apply any timeout for an admin user. - def max_seconds(query) - if query.context[:current_user]&.admin? - false - elsif query.mutation? - 10 - else - super - end - end -end - -# ... - -class MySchema < GraphQL::Schema - use MyTimeout, max_seconds: 5 -end -``` - -## Validation and Analysis - -Queries can originate from a user, and may be crafted in a manner to take a long time to validate against the schema. - -It is possible to limit how many seconds the static validation rules and analysers are allowed to run before returning a validation timeout error. The default is no timeout. - -For example: - -```ruby -class MySchema < GraphQL::Schema - # Applies to static validation and query analysis - validate_timeout 10 -end -``` - -**Note:** This configuration uses Ruby's built-in `Timeout` API, which can interrupt IO calls mid-flight, resulting in [very weird bugs](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/). None of GraphQL-Ruby's validators make IO calls but if you want to use this configuration and you have custom static validators that make IO calls, open an issue to discuss implementing this in an IO-safe way. diff --git a/vendor/gems/graphql/guides/queries/tracing.md b/vendor/gems/graphql/guides/queries/tracing.md deleted file mode 100644 index c5c1c4294f2..00000000000 --- a/vendor/gems/graphql/guides/queries/tracing.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Tracing -layout: guide -doc_stub: false -search: true -section: Queries -desc: Observation hooks for execution -index: 11 -redirect_from: - - /queries/instrumentation ---- - -{{ "GraphQL::Tracing::Trace" | api_doc }} provides hooks to observe and modify events during runtime. Tracing hooks are methods, defined in modules and mixed in with {{ "Schema.trace_with" | api_doc }}. - -```ruby -module CustomTrace - def parse(query_string:) - # measure, log, etc - super - end - - # ... -end -``` - -To include a trace module when running queries, add it to the schema with `trace_with`: - -```ruby -# Run `MyCustomTrace` for all queries -class MySchema < GraphQL::Schema - trace_with(MyCustomTrace) -end -``` - -For a full list of methods and their arguments, see {{ "GraphQL::Tracing::Trace" | api_doc }}. - -By default, GraphQL-Ruby makes a new trace instance when it runs a query. You can pass an existing instance as `context: { trace: ... }`. Also, `GraphQL.parse( ..., trace: ...)` accepts a trace instance. - -## Detailed Traces - -You can capture detailed traces of query execution with {{ "Tracing::DetailedTrace" | api_doc }}. They can be viewed in Google's [Perfetto Trace Viewer](https://ui.perfetto.dev). They include a per-Fiber breakdown with links between fields and Dataloader sources. - -{{ "/queries/perfetto_example.png" | link_to_img:"GraphQL-Ruby Dataloader Perfetto Trace" }} - -Learn how to set it up in the {{ "Tracing::DetailedTrace" | api_doc }} docs. - -## External Monitoring Platforms - -There integrations for GraphQL-Ruby with several other monitoring systems: - -- `ActiveSupport::Notifications`: See {{ "Tracing::ActiveSupportNotificationsTrace" | api_doc }}. -- [AppOptics](https://appoptics.com/) instrumentation is automatic in `appoptics_apm` v4.11.0+. -- [AppSignal](https://appsignal.com/): See {{ "Tracing::AppsignalTrace" | api_doc }}. -- [Datadog](https://www.datadoghq.com): See {{ "Tracing::DataDogTrace" | api_doc }}. -- [NewRelic](https://newrelic.com/): See {{ "Tracing::NewRelicTrace" | api_doc }}. -- [Prometheus](https://prometheus.io): See {{ "Tracing::PrometheusTrace" | api_doc }}. -- [Scout APM](https://scoutapp.com/): See {{ "Tracing::ScoutTrace" | api_doc }}. -- [Sentry](https://sentry.io): See {{ "Tracing::SentryTrace" | api_doc }}. -- [Skylight](https://www.skylight.io): either enable the [GraphQL probe](https://www.skylight.io/support/getting-more-from-skylight#graphql) or use {{ "Tracing::ActiveSupportNotificationsTrace" | api_doc }}. -- Statsd: See {{ "Tracing::StatsdTrace" | api_doc }}. diff --git a/vendor/gems/graphql/guides/related_projects.md b/vendor/gems/graphql/guides/related_projects.md deleted file mode 100644 index dff3e99c646..00000000000 --- a/vendor/gems/graphql/guides/related_projects.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Related Projects -section: Other -desc: Code, blog posts and presentations about GraphQL Ruby ---- - -Want to add something? Please open a pull request [on GitHub](https://github.com/rmosolgo/graphql-ruby)! - -## Code - -- `graphql-ruby` + Rails demo ([src](https://github.com/rmosolgo/graphql-ruby-demo) / [heroku](https://graphql-ruby-demo.herokuapp.com)) -- `graphql-ruby` + Sinatra demo ([src](https://github.com/robinjmurphy/ruby-graphql-server-example) / [heroku](https://ruby-graphql-server-example.herokuapp.com/)) -- [`graphql-batch`](https://github.com/shopify/graphql-batch), a batched query execution strategy -- [`graphql-cache`](https://github.com/stackshareio/graphql-cache), a resolver-level caching solution -- [`graphql-devise`](https://github.com/graphql-devise/graphql_devise), a gql interface to handle authentication with Devise -- [`graphql-docs`](https://github.com/gjtorikian/graphql-docs), a tool to automatically generate static HTML documentation from your GraphQL implementation -- [`graphql-metrics`](https://github.com/Shopify/graphql-metrics), a plugin to extract fine-grain metrics of GraphQL queries received by your server -- [`graphql-stitching`](https://github.com/gmac/graphql-stitching-ruby), tools to combine multiple local and remote schemas into a single graph that queries as one -- [`graphql-groups`](https://github.com/hschne/graphql-groups), a DSL to define group- and aggregation queries with graphql-ruby -- Rails Helpers: - - [`graphql-activerecord`](https://github.com/goco-inc/graphql-activerecord) - - [`graphql-rails-resolve`](https://github.com/colepatrickturner/graphql-rails-resolver) - - [`graphql-query-resolver`](https://github.com/nettofarah/graphql-query-resolver), a graphql-ruby add-on to minimize N+1 queries. - - [`graphql-rails_logger`](https://github.com/jetruby/graphql-rails_logger), a logger which allows you to inspect GraphQL queries in a more readable format. - - [`apollo_upload_server-ruby`](https://github.com/jetruby/apollo_upload_server-ruby), a middleware which allows you to upload files with GraphQL and multipart/form-data using [`apollo-upload-client`](https://github.com/jaydenseric/apollo-upload-client) library on front-end. - - [`graphql-sources`](https://github.com/ksylvest/graphql-sources) a collection of common GraphQL [sources](https://graphql-ruby.org/dataloader/sources.html) to simplify using `ActiveRecord`, `ActiveStorage`, `Rails.cache`, and more. - - [`graphql-filters`](https://github.com/moku-io/graphql-filters), a DSL to define fully typed filters for list fields. -- [`search_object_graphql`](https://github.com/rstankov/SearchObjectGraphQL), a DSL for defining search resolvers for GraphQL. -- [`action_policy-graphql`](https://github.com/palkan/action_policy-graphql), an integration for using [`action_policy`](https://github.com/palkan/action_policy) as an authorization framework for GraphQL applications. -- [`graphql_rails`](https://github.com/samesystem/graphql_rails), Rails way GraphQL build tool -- [`graphql-rails-generators`](https://github.com/ajsharp/graphql-rails-generators), Generate graphql-ruby mutations, types and input types from your ActiveRecord models. -- [`graphql-ruby-fragment_cache`](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache), a tool for caching response fragments. -- [`graphql-ruby-persisted_queries`](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries), the implementation of [Apollo persisted queries](https://github.com/apollographql/apollo-link-persisted-queries). -- [`rubocop-graphql`](https://github.com/DmitryTsepelev/rubocop-graphql), [rubocop](https://github.com/rubocop-hq/rubocop) extension for enforcing best practices. -- [`apollo-federation-ruby`](https://github.com/Gusto/apollo-federation-ruby), a Ruby implementation of the Apollo Federation [subgraph spec](https://www.apollographql.com/docs/federation/subgraph-spec/). - -## Blog Posts - -- Building a blog in GraphQL and Relay on Rails [Introduction](https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-getting-started-955a49d251de), [Part 1]( https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-creating-types-and-schema-b3f9b232ccfc), [Part 2](https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-first-relay-powered-react-component-cb3f9ee95eca) -- https://medium.com/@khor/relay-facebook-on-rails-8b4af2057152 -- https://blog.jacobwgillespie.com/from-rest-to-graphql-b4e95e94c26b#.4cjtklrwt -- https://jonsimpson.ca/parallel-graphql-resolvers-with-futures/ -- Active Storage meets GraphQL: [Direct uploads](https://evilmartians.com/chronicles/active-storage-meets-graphql-direct-uploads) and [Exposing attachment URLs](https://evilmartians.com/chronicles/active-storage-meets-graphql-pt-2-exposing-attachment-urls) -- [Exposing permissions in GraphQL APIs with Action Policy](https://evilmartians.com/chronicles/exposing-permissions-in-graphql-apis-with-action-policy) -- [Reporting non-nullable violations in graphql-ruby properly](https://evilmartians.com/chronicles/reporting-non-nullable-violations-in-graphql-ruby-properly) -- [How to GraphQL with Ruby, Rails, Active Record, and no N+1](https://evilmartians.com/chronicles/how-to-graphql-with-ruby-rails-active-record-and-no-n-plus-one) - -## Screencasts - -- [GraphQL Basics in Rails 5](https://rubyplus.com/episodes/271-GraphQL-Basics-in-Rails-5) - -## Presentations -- [Rescuing Legacy Codebases with GraphQL](https://speakerdeck.com/nettofarah/rescuing-legacy-codebases-with-graphql-1) by [@nettofarah](https://twitter.com/nettofarah) - -## Tutorials -- [How To GraphQL](https://www.howtographql.com/graphql-ruby/0-introduction/) by [@rstankov](https://github.com/rstankov) - -- [GraphQL Ruby CRUD Tutorial](https://www.blook.pub/books/graphql-rails-tutorial) by [@kohheepeace](https://twitter.com/kohheepeace) - -- Rails/GraphQL + React/Apollo Tutorial ([Part 1](https://evilmartians.com/chronicles/graphql-on-rails-1-from-zero-to-the-first-query), [Part 2](https://evilmartians.com/chronicles/graphql-on-rails-2-updating-the-data), [Part 3](https://evilmartians.com/chronicles/graphql-on-rails-3-on-the-way-to-perfection)) by [@evilmartians](https://twitter.com/evilmartians) diff --git a/vendor/gems/graphql/guides/relay/range_add.md b/vendor/gems/graphql/guides/relay/range_add.md deleted file mode 100644 index fd81f922e8d..00000000000 --- a/vendor/gems/graphql/guides/relay/range_add.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Relay -title: RangeAdd helper for mutations -desc: A helper for Relay's RANGE_ADD operations -index: 1 ---- - -Relay specifies `RANGE_ADD` operations for adding items to connections. GraphQL-Ruby ships with {{ "GraphQL::Relay::RangeAdd" | api_doc }} to help implement this. Check the API docs for a usage example. diff --git a/vendor/gems/graphql/guides/schema/definition.md b/vendor/gems/graphql/guides/schema/definition.md deleted file mode 100644 index bb1573120d2..00000000000 --- a/vendor/gems/graphql/guides/schema/definition.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Schema -title: Definition -desc: Defining your schema -index: 1 ---- - - -A GraphQL system is called a _schema_. The schema contains all the types and fields in the system. The schema executes queries and publishes an {% internal_link "introspection system","/schema/introspection" %}. - -Your GraphQL schema is a class that extends {{ "GraphQL::Schema" | api_doc }}, for example: - -```ruby -class MyAppSchema < GraphQL::Schema - max_complexity 400 - query Types::Query - use GraphQL::Dataloader - - # Define hooks as class methods: - def self.resolve_type(type, obj, ctx) - # ... - end - - def self.object_from_id(node_id, ctx) - # ... - end - - def self.id_from_object(object, type, ctx) - # ... - end -end -``` - -There are lots of schema configuration methods. - -For defining GraphQL types, see the guides for those types: {% internal_link "object types", "/type_definitions/objects" %}, {% internal_link "interface types", "/type_definitions/interfaces" %}, {% internal_link "union types", "/type_definitions/unions" %}, {% internal_link "input object types", "/type_definitions/input_objects" %}, {% internal_link "enum types", "/type_definitions/enums" %}, and {% internal_link "scalar types", "/type_definitions/scalars" %}. - -## Types in the Schema - -- {{ "Schema.query" | api_doc }}, {{ "Schema.mutation" | api_doc }}, and {{ "Schema.subscription" | api_doc}} declare the [entry-point types](https://graphql.org/learn/schema/#the-query-and-mutation-types) of the schema. -- {{ "Schema.orphan_types" | api_doc }} declares object types which implement {% internal_link "Interfaces", "/type_definitions/interfaces" %} but aren't used as field return types in the schema. For more about this specific scenario, see {% internal_link "Orphan Types", "/type_definitions/interfaces#orphan-types" %} - -### Lazy-loading types - -In development, GraphQL-Ruby can defer loading your type definitions until they're needed. This requires some configuration to opt in: - -- Add `use GraphQL::Schema::Visibility` to your schema. ({{ "GraphQL::Schema::Visibility" | api_doc }} supports lazy loading and will be the default in a future GraphQL-Ruby version. See {% internal_link "Migration Notes", "/authorization/visibility#migration-notes" %} if you have an existing visibility implementation.) -- Move your entry-point type definitions into a block, for example: - - ```diff - - query Types::Query - + query { Types::Query } - ``` - -- Optionally, move field types into blocks, too: - - ```diff - - field :posts, [Types::Post] # Loads `types/post.rb` immediately - + field :posts do - + type([Types::Post]) # Loads `types/post.rb` when this field is used in a query - + end - ``` - -To enforce these patterns, you can enable two Rubocop rules that ship with GraphQL-Ruby: - -- `GraphQL/RootTypesInBlock` will make sure that `query`, `mutation`, and `subscription` are all defined in a block. -- `GraphQL/FieldTypeInBlock` will make sure that non-built-in field return types are defined in blocks. - -## Object Identification - -Some GraphQL features use unique IDs to load objects: - -- the `node(id:)` field looks up objects by ID (See {% internal_link "Object Identification", "/schema/object_identification" %} for more about Relay-style object identification.) -- any arguments with `loads:` configurations look up objects by ID -- the {% internal_link "ObjectCache", "/object_cache/overview" %} uses IDs in its caching scheme - -To use these features, you must provide some methods for generating UUIDs and fetching objects with them: - -{{ "Schema.object_from_id" | api_doc }} is called by GraphQL-Ruby to load objects directly from the database. It's usually used by the `node(id: ID!): Node` field (see {{ "GraphQL::Types::Relay::Node" | api_doc }}), Argument {% internal_link "loads:", "/mutations/mutation_classes#auto-loading-arguments" %}, or the {% internal_link "ObjectCache", "/object_cache/overview" %}. It receives a unique ID and must return the object for that ID, or `nil` if the object isn't found (or if it should be hidden from the current user). - -{{ "Schema.id_from_object" | api_doc }} is used to implement `Node.id`. It should return a unique ID for the given object. This ID will later be sent to `object_from_id` to refetch the object. - -Additionally, {{ "Schema.resolve_type" | api_doc }} is called by GraphQL-Ruby to get the runtime Object type for fields that return return {% internal_link "interface", "/type_definitions/interfaces" %} or {% internal_link "union", "/type_definitions/unions" %} types. - -## Error Handling - -- {{ "Schema.type_error" | api_doc }} handles type errors at runtime, read more in the {% internal_link "Type errors guide", "/errors/type_errors" %}. -- {{ "Schema.rescue_from" | api_doc }} defines error handlers for application errors. See the {% internal_link "error handling guide", "/errors/error_handling" %} for more. -- {{ "Schema.parse_error" | api_doc }} and {{ "Schema.query_stack_error" | api_doc }} provide hooks for reporting errors to your bug tracker. - -## Default Limits - -- {{ "Schema.max_depth" | api_doc }} and {{ "Schema.max_complexity" | api_doc }} apply some limits to incoming queries. See {% internal_link "Complexity and Depth", "/queries/complexity_and_depth" %} for more. -- {{ "Schema.default_max_page_size" | api_doc }} applies limits to {% internal_link "connection fields", "/pagination/overview" %}. -- {{ "Schema.validate_timeout" | api_doc }}, {{ "Schema.validate_max_errors" | api_doc }} and {{ "Schema.max_query_string_tokens" | api_doc }} all apply limits to query execution. See {% internal_link "Timeout", "/queries/timeout" %} for more. - -## Introspection - -- {{ "Schema.extra_types" | api_doc }} declares types which should be printed in the SDL and returned in introspection queries, but aren't otherwise used in the schema. -- {{ "Schema.introspection" | api_doc }} can attach a {% internal_link "custom introspection system", "/schema/introspection" %} to the schema. - -## Authorization - -- {{ "Schema.unauthorized_object" | api_doc }} and {{ "Schema.unauthorized_field" | api_doc }} are called when {% internal_link "authorization hooks", "/authorization/authorization" %} return `false` during query execution. - -## Execution Configuration - -- {{ "Schema.trace_with" | api_doc }} attaches tracer modules. See {% internal_link "Tracing", "/queries/tracing" %} for more. -- {{ "Schema.query_analyzer" | api_doc }} and {{ "Schema.multiplex_analyzer" }} accept processors for ahead-of-time query analysis, see {% internal_link "Analysis", "/queries/ast_analysis" %} for more. -- {{ "Schema.default_logger" | api_doc }} configures a logger for runtime. See {% internal_link "Logging", "/queries/logging" %}. -- {{ "Schema.context_class" | api_doc }} and {{ "Schema.query_class" | api_doc }} attach custom subclasses to your schema to use during execution. -- {{ "Schema.lazy_resolve" | api_doc }} registers classes with {% internal_link "lazy execution", "/schema/lazy_execution" %}. - -## Plugins - -- {{ "Schema.use" | api_doc }} adds plugins to your schema. For example, {{ "GraphQL::Dataloader" | api_doc }} and {{ "GraphQL::Schema::Visibility" | api_doc }} are installed this way. - -## Production Considerations - -- __Parser caching__: if your application parses GraphQL _files_ (queries or schema definition), it may benefit from enabling {{ "GraphQL::Parser::Cache" | api_doc }}. -- __Eager loading the library__: by default, GraphQL-Ruby autoloads its constants as-needed. In production, they should be eager loaded instead, using `GraphQL.eager_load!`. - - - Rails: enabled automatically. (ActiveSupport calls `.eager_load!`.) - - Sinatra: add `configure(:production) { GraphQL.eager_load! }` to your application file. - - Hanami: add `environment(:production) { GraphQL.eager_load! }` to your application file. - - Other frameworks: call `GraphQL.eager_load!` when your application is booting in production mode. - - See {{"GraphQL::Autoload#eager_load!" | api_doc }} for more details. diff --git a/vendor/gems/graphql/guides/schema/dynamic_types.md b/vendor/gems/graphql/guides/schema/dynamic_types.md deleted file mode 100644 index 9497b5d2e86..00000000000 --- a/vendor/gems/graphql/guides/schema/dynamic_types.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Schema -title: Dynamic types and fields -desc: Using different schema members for each request -index: 8 ---- - -You can use different versions of your GraphQL schema for each operation. To do this, add `use GraphQL::Schema::Visibility` and implement `visible?(context)` on the parts of your schema that will be conditionally accessible. Additionally, many schema elements have definition methods which are called at runtime by GraphQL-Ruby. You can re-implement those to return any valid schema objects. - - -GraphQL-Ruby caches schema elements for the duration of the operation, but if you're making external service calls to implement the methods below, consider adding a cache layer to improve the client experience and reduce load on your backend. - -At runtime, ensure that only one object is visible per name (type name, field name, etc.). (If `.visible?(context)` returns `false`, then that part of the schema will be hidden for the current operation.) - -When using dynamic schema members, be sure to include the relevant `context: ...` when [generating schema definition files](#schema-dumps). - -## Different fields - -You can customize which field definitions are used for each operation. - -### Using `#visible?(context)` - -To serve different fields to different clients, implement `def visible?(context)` in your {% internal_link "base field class", "/type_definitions/extensions#customizing-fields" %}: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - def initialize(*args, for_staff: false, **kwargs, &block) - super(*args, **kwargs, &block) - @for_staff = for_staff - end - - def visible?(context) - super && case @for_staff - when true - !!context[:current_user]&.staff? - when false - !context[:current_user]&.staff? - else - true - end - end -end -``` - -Then, you can configure fields with `for_staff: true|false`: - -```ruby -field :comments, Types::Comment.connection_type, null: false, - description: "Comments on this blog post", - resolver_method: :moderated_comments, - for_staff: false - -field :comments, Types::Comment.connection_type, null: false, - description: "Comments on this blog post, including unmoderated comments", - resolver_method: :all_comments, - for_staff: true -``` - -With that configuration, `post { comments { ... } }` will use `def moderated_comments` when `context[:current_user]` is `nil` or is not `.staff?`, but when `context[:current_user].staff?` is `true`, it will use `def all_comments` instead. - -### Using `.fields(context)` and `.get_field(name, context)` - -To customize the set of fields used at runtime, you can implement `def self.fields(context)` in your type classes. It should return a Hash of `{ String => GraphQL::Schema::Field }`. - -Along with this, you should implement `.get_field(name, context)` to return a field for `name`, if it should exist. For example: - -```ruby -class Types::User < Types::BaseObject - def self.fields(context) - all_fields = super - if !context[:current_user]&.staff? - all_fields.delete("isSpammy") # this is staff-only - end - all_fields - end - - def self.get_field(name, context) - field = super - if field.graphql_name == "isSpammy" && !context[:current_user]&.staff? - nil # don't show this field to non-staff - else - field - end - end -end -``` - -### Hidden Return Types - -Besides field visibility described above, if an field's return type is hidden (that is, it implements `self.visible?(context)` to return `false`), then the field will be hidden too. - -## Different arguments - -As with fields, you can use different sets of argument definitions for different GraphQL operations. - -### Using `#visible?(context)` - -To serve different arguments to different clients, implement `def visible?(context)` in your {% internal_link "base argument class", "/type_definitions/extensions#customizing-arguments" %}: - -```ruby -class Types::BaseArgument < GraphQL::Schema::Argument - def initialize(*args, for_staff: false, **kwargs, &block) - super(*args, **kwargs, &block) - @for_staff = for_staff - end - - def visible?(context) - super && case @for_staff - when true - !!context[:current_user]&.staff? - when false - !context[:current_user]&.staff? - else - true - end - end -end -``` - -Then, you can configure arguments with `for_staff: true|false`: - -```ruby -field :user, Types::User, null: true, description: "Look up a user" do - # Require a UUID-style ID from non-staff clients: - argument :id, ID, required: true, for_staff: false - # Support database primary key lookups for staff clients: - argument :id, ID, required: false, for_staff: true - argument :database_id, Int, required: false, for_staff: true -end - -def user(id: nil, database_id: nil) - # ... -end -``` - -That way, any staff client will have the option of `id` or `databaseId` while non-staff clients must use `id`. - -### Using `def arguments(context)` and `def get_argument(name, context)` - -Also, you can implement `def arguments(context)` on your base field class to return a Hash of `{ String => GraphQL::Schema::Argument }` and `def get_argument(name, context)` to return a {{ "GraphQL::Schema::Argument" | api_doc }} or `nil`. . If you take this approach, you might want some custom field classes for any types or resolvers that use these methods. That way, you don't have to reimplement the method for _all_ the fields in the schema. - -### Hidden Input Types - -Besides argument visibility described above, if an argument's input type is hidden (that is, it implements `self.visible?(context)` to return `false`), then the argument will be hidden too. - -## Different enum values - -### Using `#visible?(context)` - -You can implement `def visible?(context)` in your {% internal_link "base enum value class", "/type_definitions/extensions#customizing-enum-values" %} to hide some enum values from some clients. For example: - -```ruby -class BaseEnumValue < GraphQL::Schema::EnumValue - def initialize(*args, for_staff: false, **kwargs, &block) - super(*args, **kwargs, &block) - @for_staff = for_staff - end - - def visible?(context) - super && case @for_staff - when true - !!context[:current_user]&.staff? - when false - !context[:current_user]&.staff? - else - true - end - end -end -``` - -With this base class, you can configure some enum values to be _just_ for staff or non-staff viewers: - -```ruby -class AccountStatus < Types::BaseEnum - value "ACTIVE" - value "INACTIVE" - # Use this for sensitive account statuses when the viewer is public: - value "OTHER", for_staff: false - # Staff-only sensitive account statuses: - value "BANNED", for_staff: true - value "PAYMENT_FAILED", for_staff: true - value "PENDING_VERIFICATION", for_staff: true -end -``` - -### Using `.enum_values(context)` - -Alternatively, you can implement `def self.enum_values(context)` in your enum types to return an Array of {{ "GraphQL::Schema::EnumValue" | api_doc }}s. For example, to return a dynamic set of enum values: - -```ruby -class ProjectStatus < Types::BaseEnum - def self.enum_values(context = {}) - # Fetch the values from the database - status_names = context[:tenant].project_statuses.pluck("name") - - # Then build an Array of Enum values - status_names.map do |name| - # Be sure to include `owner: self`, the back-reference from the EnumValue to its parent Enum - GraphQL::Schema::EnumValue.new(name, owner: self) - end - end -end -``` - -## Different types - -You can also use different types for each query. A few behaviors depend on the methods defined above: - -- If a type is not used as a return type, an argument type, or as a member of a union or implementer of an interface, it will be hidden -- If an interface or union has members, it will be hidden -- If a field's return type is hidden, the field will be hidden -- If an argument's input type is hidden, the argument will be hidden - -As you can imagine, these different hiding behaviors influence one another and they can cause some real head-scratchers when used simultaneously. - -### Using `.visible?(context)` - -Type classes can implement `def self.visible?(context)` to hide themselves at runtime: - -```ruby -class Types::BanReason < Types::BaseEnum - # Hide any arguments or fields that use this enum - # unless the current user is staff - def self.visible?(context) - super && !!context[:current_user]&.staff? - end - - # ... -end -``` - -### Different definitions for the same type - -You can provide different implementations of the same type by: - -- Implementing `def self.visible?(context)` to return `true` and `false` in complementary contexts. (They should never both be `.visible? => true`). -- Hooking the types up to the schema with different field or argument definitions, as described above - -For example, to migrate your `Money` scalar to a `Money` object type: - -```ruby -# Previously, we used a simple string to describe money: -class Types::LegacyMoney < Types::BaseScalar - # This graphql name will conflict with `Types::Money`, - # so we have to be careful not to use them at the same time. - # (GraphQL-Ruby will raise an error if it finds two definitions with the same name at runtime.) - graphql_name "Money" - describe "A string describing an amount of money." - - # Use this type definition if the current request - # explicitly opted in to the legacy money representation: - def self.visible?(context) - !!context[:requests_legacy_money] - end -end - -# But we want to improve the client experience with a dedicated object type: -class Types::Money < Types::BaseObject - field :amount, Integer, null: false - field :currency, Types::Currency, null: false - - # Use this new definition if the client - # didn't explicitly ask for the legacy definition: - def self.visible?(context) - !context[:requests_legacy_money] - end -end -``` - -Then, hook the definitions up to the schema using field definitions: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - def initialize(*args, legacy_money: false, **kwargs, &block) - super(*args, **kwargs, &block) - @legacy_money = legacy_money - end - - def visible?(context) - super && (@legacy_money ? !!context[:requests_legacy_money] : !context[:requests_legacy_money]) - end -end - -class Types::Invoice < Types::BaseObject - # Add one definition for each possible return type - # (one definition will be hidden at runtime) - field :amount, Types::LegacyMoney, null: false, legacy_money: true - field :amount, Types::Money, null: false, legacy_money: false -end -``` - -Input types (like input objects, scalars, and enums) work the same way with argument definitions. - -## Schema Dumps - -To dump a certain _version_ of the schema, provide the applicable `context: ...` to {{ "Schema.to_definition" | api_doc }}. For example: - -```ruby -# Legacy money schema: -MySchema.to_definition(context: { requests_legacy_money: true }) -``` - -or - -```ruby -# Staff-only schema: -MySchema.to_definition(context: { current_user: OpenStruct.new(staff?: true) }) -``` - -That way, the given `context` will be passed to `visible?(context)` calls and other relevant methods. diff --git a/vendor/gems/graphql/guides/schema/generators.md b/vendor/gems/graphql/guides/schema/generators.md deleted file mode 100644 index 20f97dd7b33..00000000000 --- a/vendor/gems/graphql/guides/schema/generators.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Generators -section: Schema -desc: Use Rails generators to install GraphQL and scaffold new types. -index: 3 ---- - -If you're using GraphQL with Ruby on Rails, you can use generators to: - -- [setup GraphQL](#graphqlinstall), including [GraphiQL](https://github.com/graphql/graphiql), [GraphQL::Batch](https://github.com/Shopify/graphql-batch), and [Relay](https://facebook.github.io/relay/) -- [scaffold types](#scaffolding-types) -- [scaffold Relay mutations](#scaffolding-mutations) -- [scaffold ActiveRecord create/update/delete mutations](#scaffolding-activerecord-mutations) -- [scaffold GraphQL::Batch loaders](#scaffolding-loaders) - -## graphql:install - -You can add GraphQL to a Rails app with `graphql:install`: - -``` -rails generate graphql:install -``` - -This will: - -- Set up a folder structure in `app/graphql/` -- Add schema definition -- Add base type classes -- Add a `Query` type definition -- Add a `Mutation` type definition with a base mutation class -- Add a route and controller for executing queries -- Install [`graphiql-rails`](https://github.com/rmosolgo/graphiql-rails) -- Enable [`ActiveRecord::QueryLogs`](https://api.rubyonrails.org/classes/ActiveRecord/QueryLogs.html) and add GraphQL-related metadata (using {{ "GraphQL::Current" | api_doc }}) -After installing you can see your new schema by: - -- `bundle install` -- `rails server` -- Open `localhost:3000/graphiql` - -### Options - -- `--directory=DIRECTORY` will directory where generated files should be saved (default is `app/graphql`) -- `--schema=MySchemaName` will be used for naming the schema (default is `#{app_name}Schema`) -- `--skip-graphiql` will exclude `graphiql-rails` from the setup -- `--skip-mutation-root-type` will not create of the mutation root type -- `--skip-query-logs` will skip the QueryLogs setup -- `--relay` will add [Relay](https://facebook.github.io/relay/)-specific code to your schema -- `--batch` will add [GraphQL::Batch](https://github.com/Shopify/graphql-batch) to your gemfile and include the setup in your schema -- `--playground` will include `graphql_playground-rails` in the setup (mounted at `/playground`) -- `--api` will create smaller stack for API only apps - -## Scaffolding Types - -Several generators will add GraphQL types to your project. Run them with `-h` to see the options: - -- `rails g graphql:object` -- `rails g graphql:input` -- `rails g graphql:interface` -- `rails g graphql:union` -- `rails g graphql:enum` -- `rails g graphql:scalar` - -### ActiveRecord columns auto-extraction - -The `graphql:object` and `graphql:input` generators can detect the existence of an ActiveRecord class with the same name, and scaffold all database columns as fields/arguments using appropriate GraphQL types and nullability detection - -### Options - -- `--namespaced-types` will generate each one of the `object`/`input`/`interface`/... types under separate `Types::Objects::*`/`Types::Inputs::*`/`Types::Interfaces::*`/... namespaces and folders - -## Scaffolding Mutations - -You can prepare a Relay Classic mutation with - -``` -rails g graphql:mutation #{mutation_name} -``` - -## Scaffolding ActiveRecord Mutations - -You can generate a Relay Classic create, update or delete mutation for a given model with - -``` -rails g graphql:mutation_create #{model_class_name} -rails g graphql:mutation_update #{model_class_name} -rails g graphql:mutation_delete #{model_class_name} -``` - -`model_class_name` accepts both `namespace/class_type` and `Namespace::ClassType` formats. -This mutation also accepts the `--namespaced-types` flag, to keep it consistent with the scaffolded Object and Input classes from the type generators - -## Scaffolding Loaders - -You can prepare a GraphQL::Batch loader with - -``` -rails g graphql:loader -``` diff --git a/vendor/gems/graphql/guides/schema/introspection.md b/vendor/gems/graphql/guides/schema/introspection.md deleted file mode 100644 index 0dd56307952..00000000000 --- a/vendor/gems/graphql/guides/schema/introspection.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Introspection -section: Schema -desc: GraphQL has an introspection system that tells about the schema. -index: 3 ---- - -A GraphQL schema has a [built-in introspection system](https://graphql.org/learn/introspection/) that publishes the schema's structure. In fact, the introspection system can be queried using GraphQL, for example: - -```graphql -{ - __schema { - queryType { - name - } - } -} -# Returns: -# { -# "data": { -# "__schema": { -# "queryType": { -# "name": "Query" -# } -# } -# } -# } -``` - -This system is used for GraphQL tooling like the [GraphiQL editor](https://github.com/graphql/graphiql). - -Here are the default parts of the introspection system: - -- `__schema` is a root-level field that contains data about the schema: its entry points, types, and directives. -- `__type(name: String!)` is a root-level field that returns data about a type with the given `name`, if there is a type with that name. -- `__typename` works a bit differently: it can be added to _any_ selection, and it will return the type of object being queried. - -Here are some `__typename` examples: - -```graphql -{ - user(id: "1") { - handle - __typename - } -} -# Returns: -# { -# "data": { -# "user": { -# "handle": "rmosolgo", -# "__typename": "User" -# } -# } -# } -``` - -For unions and interfaces, `__typename` returns the _object_ type for the current object, for example: - -```graphql -{ - search(term: "puppies") { - title - __typename - } -} -# Returns: -# { -# "data": { -# "search": [ -# { -# "title": "Sound of Dogs Barking", -# "__typename": "AudioClip", -# }, -# { -# "title": "Cute puppies playing with a stick", -# "__typename": "VideoClip", -# }, -# { -# "title": "The names of my favorite pets", -# "__typename": "TextSnippet" -# }, -# ] -# } -# } -``` - -## Customizing Introspection - -You can use custom introspection types. - -```ruby -# create a module namespace for your custom types: -module Introspection - # described below ... -end - -class MySchema < GraphQL::Schema - # ... - # then pass the module as `introspection` - introspection Introspection -end -``` - -Keep in mind that off-the-shelf tooling may not support your custom introspection fields. You may have to modify existing tooling or create your own tools to make use of your extensions. - -### Introspection Namespace - -The introspection namespace may contain a few different customizations: - -- Class-based {% internal_link "object definitions", "/type_definitions/objects" %} which replace the built-in introspection types (such as `__Schema` and `__Type`) -- `EntryPoints`, A class-based {% internal_link "object definition", "/type_definitions/objects" %} containing introspection entry points (like `__schema` and `__type(name:)`). -- `DynamicFields`, A class-based {% internal_link "object definition", "/type_definitions/objects" %} containing dynamic, globally-available fields (like `__typename`.) - -### Custom Introspection Types - -The `module` passed as `introspection` may contain classes with the following names, which replace the built-in introspection types: - -Custom class name | GraphQL type | Built-in class name ---|--|-- -`SchemaType` | `__Schema` | {{ "GraphQL::Introspection::SchemaType" | api_doc }} -`TypeType` | `__Type` | {{ "GraphQL::Introspection::TypeType" | api_doc }} -`DirectiveType` | `__Directive` | {{ "GraphQL::Introspection::DirectiveType" | api_doc }} -`DirectiveLocationType` | `__DirectiveLocation` | {{ "GraphQL::Introspection::DirectiveLocationEnum" | api_doc }} -`EnumValueType` | `__EnumValue` | {{ "GraphQL::Introspection::EnumValueType" | api_doc }} -`FieldType` | `__Field` | {{ "GraphQL::Introspection::FieldType" | api_doc }} -`InputValueType` | `__InputValue` | {{ "GraphQL::Introspection::InputValueType" | api_doc }} -`TypeKindType` | `__TypeKind` | {{ "GraphQL::Introspection::TypeKindEnum" | api_doc }} - -The class-based definitions' names _must_ match the names of the types they replace. - -#### Extending a Built-in Type - -The built-in classes listed above may be extended: - -```ruby -module Introspection - class SchemaType < GraphQL::Introspection::SchemaType - # ... - end -end -``` - -Inside the class definition, you may: - -- add new fields by calling `field(...)` and providing implementations -- redefine field structure by calling `field(...)` -- provide new field implementations by defining methods -- provide new descriptions by calling `description(...)` - -### Introspection Entry Points - -The GraphQL spec describes two entry points to the introspection system: - -- `__schema` returns data about the schema (as type `__Schema`) -- `__type(name:)` returns data about a type, if one is found by name (as type `__Type`) - -You can re-implement these fields or create new ones by creating a custom `EntryPoints` class in your introspection namespace: - -```ruby -module Introspection - class EntryPoints < GraphQL::Introspection::EntryPoints - # ... - end -end -``` - -This class an object type definition, so you can override fields or add new ones here. They'll be available on the root `query` object, but ignored in introspection (just like `__schema` and `__type`). - -### Dynamic Fields - -The GraphQL spec describes a field which may be added to _any_ selection: `__typename`. It returns the name of the current GraphQL type. - -You can add fields like this (or override `__typename`) by creating a custom `DynamicFields` definition: - -```ruby -module Introspection - class DynamicFields < GraphQL::Introspection::DynamicFields - # ... - end -end -``` - -Any fields defined there will be available in any selection, but ignored in introspection (just like `__typename`). - -## Disabling Introspection - -In case you want to turn off introspection entry points `__schema` and `__type` (for instance in the production environment) you can use a `#disable_introspection_entry_points` shorthand method: - -```ruby -class MySchema < GraphQL::Schema - disable_introspection_entry_points if Rails.env.production? -end -``` - -Where `disable_introspection_entry_points` will disable both the `__schema` and `__type` introspection entry points, you can also individually disable the introspection entry points using the `disable_schema_introspection_entry_point` and `disable_type_introspection_entry_point` shorthand methods: - -```ruby -class MySchema < GraphQL::Schema - disable_schema_introspection_entry_point - disable_type_introspection_entry_point -end -``` diff --git a/vendor/gems/graphql/guides/schema/lazy_execution.md b/vendor/gems/graphql/guides/schema/lazy_execution.md deleted file mode 100644 index ebedea0112e..00000000000 --- a/vendor/gems/graphql/guides/schema/lazy_execution.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Lazy Execution -section: Schema -desc: Resolvers can return "unfinished" results that are deferred for batch resolution. -index: 4 ---- - -With lazy execution, you can optimize access to external services (such as databases) by making batched calls. Building a lazy loader has three steps: - -- Define a lazy-loading class with _one_ method for loading & returning a value -- Connect it to your schema with {{ "GraphQL::Schema#lazy_resolve" | api_doc }} -- In `resolve` methods, return instances of the lazy-loading class - -## Example: Batched Find - -Here's a way to find many objects by ID using one database call, preventing N+1 queries. - -1. Lazy-loading class which finds models by ID. - -```ruby -class LazyFindPerson - def initialize(query_ctx, person_id) - @person_id = person_id - # Initialize the loading state for this query, - # or get the previously-initiated state - @lazy_state = query_ctx[:lazy_find_person] ||= { - pending_ids: Set.new, - loaded_ids: {}, - } - # Register this ID to be loaded later: - @lazy_state[:pending_ids] << person_id - end - - # Return the loaded record, hitting the database if needed - def person - # Check if the record was already loaded: - loaded_record = @lazy_state[:loaded_ids][@person_id] - if loaded_record - # The pending IDs were already loaded, - # so return the result of that previous load - loaded_record - else - # The record hasn't been loaded yet, so - # hit the database with all pending IDs - pending_ids = @lazy_state[:pending_ids].to_a - people = Person.where(id: pending_ids) - people.each { |person| @lazy_state[:loaded_ids][person.id] = person } - @lazy_state[:pending_ids].clear - # Now, get the matching person from the loaded result: - @lazy_state[:loaded_ids][@person_id] - end - end -``` - -2. Connect the lazy resolve method - -```ruby -class MySchema < GraphQL::Schema - # ... - lazy_resolve(LazyFindPerson, :person) -end -``` - -3. Return lazy objects from `resolve` - -```ruby -field :author, PersonType - -def author - LazyFindPerson.new(context, object.author_id) -end -``` - -Now, calls to `author` will use batched database access. For example, this query: - -```graphql -{ - p1: post(id: 1) { author { name } } - p2: post(id: 2) { author { name } } - p3: post(id: 3) { author { name } } -} -``` - -Will only make one query to load the `author` values. - -## Gems for batching - -The example above is simple and has some shortcomings. Consider the following gems for a robust solution to batched resolution: - -* {{ "GraphQL::Dataloader" | api_doc }} is a built-in, Fiber-based approach to batching. See the {% internal_link "Dataloader guide", "/dataloader/overview" %} for more information. -* [`graphql-batch`](https://github.com/shopify/graphql-batch) provides a powerful, flexible toolkit for lazy resolution with GraphQL. -* [`dataloader`](https://github.com/sheerun/dataloader) is more general promise-based utility for batching queries within the same thread. -* [`batch-loader`](https://github.com/exAspArk/batch-loader) works with any Ruby code including GraphQL, no extra dependencies or primitives. diff --git a/vendor/gems/graphql/guides/schema/object_identification.md b/vendor/gems/graphql/guides/schema/object_identification.md deleted file mode 100644 index 9ae80c4d8d1..00000000000 --- a/vendor/gems/graphql/guides/schema/object_identification.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -title: Object Identification -section: Schema -desc: Working with unique global IDs -index: 8 ---- - -GraphQL-Ruby ships with some helpers to implement [Relay-style object identification](https://relay.dev/graphql/objectidentification.htm). - -## Schema methods - -See {% internal_link "the Schema definition guide", "/schema/definition#object-identification" %} for required top-level hooks. - -## Node interface - -One requirement for Relay's object management is implementing the `"Node"` interface. - -To implement the node interface, add {{ "GraphQL::Types::Relay::Node" | api_doc }} to your definition: - -```ruby -class Types::PostType < GraphQL::Schema::Object - # Implement the "Node" interface for Relay - implements GraphQL::Types::Relay::Node - # ... -end -``` - -To tell GraphQL how to resolve members of the `Node` interface, you must also define `Schema.resolve_type`: - -```ruby -class MySchema < GraphQL::Schema - # You'll also need to define `resolve_type` for - # telling the schema what type Relay `Node` objects are - def self.resolve_type(type, obj, ctx) - case obj - when Post - Types::PostType - when Comment - Types::CommentType - else - raise("Unexpected object: #{obj}") - end - end -end -``` - -## UUID fields - -Nodes must have a field named `"id"` which returns a globally unique ID. - -To add a UUID field named `"id"`, implement the {{ "GraphQL::Types::Relay::Node" | api_doc }} interface:: - -```ruby -class Types::PostType < GraphQL::Schema::Object - implements GraphQL::Types::Relay::Node -end -``` - -This field will call the previously-defined `id_from_object` class method. - -## `node` field (find-by-UUID) - -You should also provide a root-level `node` field so that Relay can refetch objects from your schema. You can attach it like this: - -```ruby -class Types::QueryType < GraphQL::Schema::Object - # Used by Relay to lookup objects by UUID: - # Add `node(id: ID!) - include GraphQL::Types::Relay::HasNodeField - # ... -end -``` - -## `nodes` field - -You can also provide a root-level `nodes` field so that Relay can refetch objects by IDs: - -```ruby -class Types::QueryType < GraphQL::Schema::Object - # Fetches a list of objects given a list of IDs - # Add `nodes(ids: [ID!]!)` - include GraphQL::Types::Relay::HasNodesField - # ... -end -``` diff --git a/vendor/gems/graphql/guides/schema/root_types.md b/vendor/gems/graphql/guides/schema/root_types.md deleted file mode 100644 index 7a588a4f825..00000000000 --- a/vendor/gems/graphql/guides/schema/root_types.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Schema -title: Root Types -desc: Root types are the entry points for queries, mutations and subscriptions. -index: 2 ---- - -GraphQL queries begin from [root types](https://graphql.org/learn/schema/#the-query-and-mutation-types): `query`, `mutation`, and `subscription`. - -Attach these to your schema using methods with the same name: - -```ruby -class MySchema < GraphQL::Schema - # required - query Types::QueryType - # optional - mutation Types::MutationType - subscription Types::SubscriptionType -end -``` - -The types are `GraphQL::Schema::Object` classes, for example: - -```ruby -# app/graphql/types/query_type.rb -class Types::QueryType < GraphQL::Schema::Object - field :posts, [PostType], 'Returns all blog posts', null: false -end - -# Similarly: -class Types::MutationType < GraphQL::Schema::Object - field :create_post, mutation: Mutations::AddPost -end -# and -class Types::SubscriptionType < GraphQL::Schema::Object - field :comment_added, subscription: Subscriptions::CommentAdded -end -``` - -Each type is the entry point for the corresponding GraphQL query: - -```ruby -query Posts { - # `Query.posts` - posts { ... } -} - -mutation AddPost($postAttrs: PostInput!){ - # `Mutation.createPost` - createPost(attrs: $postAttrs) -} - -subscription CommentAdded { - # `Subscription.commentAdded` - commentAdded(postId: 1) -} -``` diff --git a/vendor/gems/graphql/guides/schema/sdl.md b/vendor/gems/graphql/guides/schema/sdl.md deleted file mode 100644 index db2884cf9b0..00000000000 --- a/vendor/gems/graphql/guides/schema/sdl.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Schema -title: Parsing GraphQL Schema Definition Language into a Ruby Schema -desc: Defining a schema from a string or .graphql file -index: 7 ---- - -GraphQL-Ruby includes a way to build a runable schema from the GraphQL Schema Definition Language (SDL). {{ "GraphQL::Schema.from_definition" | api_doc }} returns a schema class based on a filename or string containing GraphQL SDL. For example: - -```ruby -# From a file: -MySchema = GraphQL::Schema.from_definition("path/to/schema.graphql") -# Or, from a string: -MySchema = GraphQL::Schema.from_definition(<<~GRAPHQL) - type Query { - # ... - } - # ... -GRAPHQL -``` - -Definitions from the SDL are converted into Ruby classes, similar to those defined in plain Ruby code. - -## Execution - -You can provide execution behaviors to a generated schema as `default_resolve:`, which accepts two kinds of values: - -- __Implementation Object__: an object that implements several methods used at runtime -- __Implementation Hash__: keys and nested hashes provide procs for execution - -### Implementation Object - -By providing an object that implements several runtime methods, you can define the execution behaviors of a schema loaded from SDL: - -```ruby -class SchemaImplementation - # see below for methods -end - -# Pass the object as `default_resolve:` -MySchema = GraphQL::Schema.from_definition( - "path/to/schema.graphql", - default_resolve: SchemaImplementation.new -) -``` - -The `default_resolve:` object may implement: - -- `#call(type, field, obj, args, ctx)` for resolving fields -- `#resolve_type(abstract_type, obj, ctx)` for resolving `obj` as one of `abstract_type`'s possible object types -- `#coerce_input(type, value, ctx)` for coercing scalar input -- `#coerce_result(type, value, ctx)` for coercing scalar return values - -### Implementation Hash - -Alternatively, you can provide a Hash containing callable behaviors, for example: - -```ruby -schema_implementation = { - # ... see below for hash structure -} - -# Pass the hash as `default_resolve:` -MySchema = GraphQL::Schema.from_definition( - "path/to/schema.graphql", - default_resolve: schema_implementation -) -``` - -The hash may contain: - -- A key for each object type name, containing a sub-hash of `{ field_name => ->(obj, args, ctx) { ... } }` for resolving object fields -- A key for each scalar type name, containing a sub-hash with keys `"coerce_result"` and `"coerce_input"`, each pointing to a `->(value, ctx) { ... }` for handling scalar values at runtime -- A `"resolve_type"` key pointing to a `->(abstract_type, object, ctx) { ... }` callable, used for resolving `object` to one of `abstract_type`'s possible types - -## Plugins - -{{ "GraphQL::Schema.from_definition" | api_doc }} accepts a `using:` argument, which may be given as a map of `plugin => args` pairs. For example: - -```ruby -MySchema = GraphQL::Schema.from_definition("path/to/schema.graphql", using: { - GraphQL::Pro::PusherSubscriptions => { redis: $redis }, - GraphQL::Pro::OperationStore => nil, # no options here -}) -``` - -## Directives - -Although GraphQL-Ruby doesn't have special handling for directives in the SDL, you can build custom behavior in your own app. If part of the schema had a directive, you can access it using `.ast_node.directives`. For example: - -```ruby -schema = GraphQL::Schema.from_definition <<-GRAPHQL -type Query @flagged { - secret: Boolean @privacy(secret: true) -} -GRAPHQL - -pp schema.query.ast_node.directives.map(&:to_query_string) -# => ["@flagged"] -pp schema.get_field("Query", "secret").ast_node.directives.map(&:to_query_string) -# => ["@privacy(secret: true)"] -``` - -See {{ "GraphQL::Language::Nodes::Directive" | api_doc }} for available methods. diff --git a/vendor/gems/graphql/guides/search.html b/vendor/gems/graphql/guides/search.html deleted file mode 100644 index f674cdb7dd8..00000000000 --- a/vendor/gems/graphql/guides/search.html +++ /dev/null @@ -1,74 +0,0 @@ ---- -layout: default -title: Search ---- - -

    Search:

    -
    - - - - diff --git a/vendor/gems/graphql/guides/subscriptions/ably_implementation.md b/vendor/gems/graphql/guides/subscriptions/ably_implementation.md deleted file mode 100644 index dd4e54fbd13..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/ably_implementation.md +++ /dev/null @@ -1,326 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Ably Implementation -desc: GraphQL subscriptions over Ably -index: 7 -pro: true ---- - -[GraphQL Pro](https://graphql.pro) includes a subscription system based on [Redis](https://redis.io) and [Ably](https://ably.io) which works with any Ruby web framework. - -After creating an app on Ably, you can hook it up to your GraphQL schema. - -## How it Works - -This subscription implementation uses a hybrid approach: - -- __Your app__ takes GraphQL queries an runs them -- __Redis__ stores subscription data for later updates -- __Ably__ sends updates to subscribed clients - -So, the lifecycle goes like this: - -- A `subscription` query is sent by HTTP Post to your server (just like a `query` or `mutation`) -- The response contains an Ably channel ID (as an HTTP header) which the client may subscribe to -- The client opens that Ably channel -- When the server triggers updates, they're delivered over the Ably channel -- When the client unsubscribes, the server receives a webhook and responds by removing its subscription data - -Here's another look: - -``` -1. Subscription is created in your app - - HTTP POST - .----------> write to Redis - 📱 ⚙️ -----> 💾 - <---------' - X-Subscription-ID: 1234 - - -2. Client opens a connection to Ably - - websocket - 📱 <---------> ☁️ - - -3. The app sends updates via Ably - - ⚙️ ---------> ☁️ ------> 📱 - POST update - (via gem) (via websocket) - - -4. When the client unsubscribes, Ably notifies the app - - webhook - ⚙️ <-------- ☁️ (disconnect) 📱 -``` - - -By using this configuration, you can use GraphQL subscriptions without hosting a push server yourself! - -## Ably setup -Add `ably-rest` to your `Gemfile`: - -```ruby -gem 'ably-rest' -``` - -and `bundle install`. - -## Database setup - -Subscriptions require a _persistent_ Redis database, configured with: - -```sh -maxmemory-policy noeviction -# optional, more durable persistence: -appendonly yes -``` - -Otherwise, Redis will drop data that doesn't fit in memory (read more in ["Redis persistence"](https://redis.io/topics/persistence)). - -If you're already using Redis in your application, see ["Storing Data in Redis"](https://www.mikeperham.com/2015/09/24/storing-data-with-redis/) for options to isolate data and tune your configuration. - -## Schema configuration - -Add `redis` to your `Gemfile`: - -```ruby -gem 'redis' -``` - -and `bundle install`. Then create a Redis instance: - -```ruby -# for example, in an initializer: -$graphql_subscriptions_redis = Redis.new # default connection -``` - -Then, that Redis client is passed to the Subscription configuration: - -```ruby -class MySchema < GraphQL::Schema - use GraphQL::Pro::AblySubscriptions, - redis: $graphql_subscriptions_redis, - ably: Ably::Rest.new(key: ABLY_API_KEY) -end -``` - -That connection will be used for managing subscription state. All writes to Redis are prefixed with `graphql:sub:`. - -There are also two configurations for managing persistence: - -- `stale_ttl_s:` expires subscription data after the given number of seconds without any update. After `stale_ttl_s` has passed, the data will expire from Redis. Each time a subscription receives an update, its TTL is refreshed. (Generally, this isn't required because the backend is built to clean itself up. But, if you find that Redis is collecting stale queries, you can set them to expire after some very long time as a safeguard.) -- `cleanup_delay_s:` (default: `5`) prevents deleting a subscription during those first seconds after it's created. Usually, a longer delay isn't necessary, but if you observe latency between the subscription's initial response and the client's subscription to the delivery channel, you can set this configuration to account for it. - -### Connection Pool - -For better performance reading and writing to Redis, you can pass a `connection_pool:` instead of `redis:`, using the [`connection_pool` gem](): - -```ruby - use GraphQL::Pro::AblySubscriptions, - connection_pool: ConnectionPool.new(size: 5, timeout: 5) { Redis.new }, - ably: Ably::Rest.new(key: ABLY_API_KEY) -``` - -### Broadcasts - -If you set up {% internal_link "Broadcasts", "/subscriptions/broadcast" %}, then you can update many clients over a single Ably channel. - -Broadcast channels have stable, predictable IDs. To prevent unauthorized clients from "listening in," use [token authorization](#authorization) for transport. Broadcasts channels use the namespace `gqlbdcst:`, so you can provide capabilities to receive them using `"gqlbdcst:*" => [ ... ]` in your authorization code. (If you're using [encryption](#encryption), the prefix will be `ablyencr-gqlbdcst:` instead.) - -## Execution configuration - -During execution, GraphQL will assign a `subscription_id` to the `context` hash. The client will use that ID to listen for updates, so you must return the `subscription_id` in the response headers. - -Return `result.context[:subscription_id]` as the `X-Subscription-ID` header. For example: - -```ruby -result = MySchema.execute(...) -# For subscriptions, return the subscription_id as a header -if result.subscription? - response.headers["X-Subscription-ID"] = result.context[:subscription_id] -end -render json: result -``` - -This way, the client can use that ID as a Ably channel. - -For __CORS requests__, you need a special header so that clients can read the custom header: - -```ruby -if result.subscription? - response.headers["X-Subscription-ID"] = result.context[:subscription_id] - # Required for CORS requests: - response.headers["Access-Control-Expose-Headers"] = "X-Subscription-ID" -end -``` - -Read more here: ["Using CORS"](https://www.html5rocks.com/en/tutorials/cors/). - -## Webhook configuration - -Your server needs to receive webhooks from Ably when clients disconnect. This keeps your local subscription database in sync with Ably. - -### Server - -*Note: if you're setting up in a development environment you should follow the [Developing with webhooks](#Developing-with-webhooks) section first* - -Mount the Rack app for handling webhooks from Ably. For example, on Rails: - -```ruby -# config/routes.rb - -# Include GraphQL::Pro's routing extensions: -using GraphQL::Pro::Routes - -Rails.application.routes.draw do - # ... - # Handle webhooks for subscriptions: - mount MySchema.ably_webhooks_client, at: "/ably_webhooks" -end -``` - -__Alternatively__, you can configure the routes to load your schema lazily, during the first request: - -```ruby -# Provide the fully-qualified class name of your schema: -lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema") -mount lazy_routes.ably_webhooks_client, at: "/ably_webhooks" -``` - -### Ably - -1. Go to the Ably dashboard -2. Click on your application -3. Select the **"Integrations"** tab -4. Click on the **"+ New Integration Rule"** button -5. Click on the "Choose" button for **"Webhook"** -6. Click on the "Choose" button for **"Webhook"** (again) -7. Enter **your URL (including the webhooks path from above)** in the URL field. -8. Select **"Batch request"** for "Request Mode" -9. Under "Source", select **"Presence"** -10. Under "Sign with key", select the API Key prefix that matches the prefix of the `ABLY_API_KEY` you provided -11. Click **"Create"** - -## Authorization - -You can use Ably's [token authentication](https://www.ably.io/documentation/realtime/authentication#token-authentication) by implementing an endpoint in your app, for example: - -```ruby -class AblyController < ActionController::Base - def auth - render status: 201, json: ably_rest_client.auth.create_token_request( - capability: { '*' => ['presence', 'subscribe'] }, - client_id: 'graphql-subscriber', - ) - end -end -``` - -[Ably's tutorial](https://www.ably.io/tutorials/webhook-chuck-norris#tutorial-step-4) also demonstrates some of the setup for this. - -## Encryption - -You can use Ably's [end-to-end encryption](https://www.ably.io/documentation/realtime/encryption) with GraphQL subscriptions. To enable it, add `cipher_base:` to your setup: - -```ruby - use GraphQL::Pro::AblySubscriptions, - redis: $graphql_subscriptions_redis, - ably: Ably::Rest.new(key: ABLY_API_KEY), - # Add `cipher_base:` to enable end-to-end encryption - cipher_base: "ff16381ae2f2b6c6de6ff696226009f3" -``` - -(Any random string will do, eg `ruby -e "require 'securerandom'; puts SecureRandom.hex"`.) - -Also, return a header to client so that it can decrypt subscription updates. The key is put in `context[:ably_cipher_base64]`, and `graphql-ruby-client` expects to find it in the `X-Subscription-Key` header: - -```ruby -result = MySchema.execute(...) -# For subscriptions, return the subscription_id as a header -if result.subscription? - response.headers["X-Subscription-ID"] = result.context[:subscription_id] - # Also return the encryption key so that clients - # can decode subscription updates - response.headers["X-Subscription-Key"] = result.context[:ably_cipher_base64] -end -``` - -(Also, if you're using CORS requests, update `Access-Control-Expose-Headers` to include `X-Subscription-Key`) - -With this setup, - -- `GraphQL::Pro::AblySubscriptions` will generate per-subscription keys (using `cipher_base` and the subscription ID) and use them to encrypt Ably payloads -- Those keys will be returned to clients in `X-Subscription-Key` -- Clients will use those keys to decrypt incoming messages - -__Backwards compatibility:__ `GraphQL::Pro::AblySubscriptions` will only encrypt payloads whose `query.context[:ably_cipher_base64]` is present. Any subscriptions created _before_ `cipher_base:` was added to the setup will _not_ be encrypted. (There was no key to encrypt them, and clients don't have a key to _decrypt_ them!) - -## Serializing Context - -Since subscription state is stored in the database, then reloaded for pushing updates, you have to serialize and reload your query `context`. - -By default, this is done with {{ "GraphQL::Subscriptions::Serialize" | api_doc }}'s `dump` and `load` methods, but you can provide custom implementations as well. To customize the serialization logic, create a subclass of `GraphQL::Pro::AblySubscriptions` and override `#dump_context(ctx)` and `#load_context(ctx_string)`: - -```ruby -class CustomSubscriptions < GraphQL::Pro::AblySubscriptions - def dump_context(ctx) - context_hash = ctx.to_h - # somehow convert this hash to a string, return the string - end - - def load_context(ctx_string) - # Given the string from the DB, create a new hash - # to use as `context:` - end -end -``` - -Then, use your _custom_ subscriptions class instead of the built-in one for your schema: - -```ruby -class MySchema < GraphQL::Schema - # Use custom subscriptions instead of GraphQL::Pro::AblySubscriptions - # to get custom serialization logic - use CustomSubscriptions, ... -end -``` - -That gives you fine-grained control of context reloading. - -## Dashboard - -You can monitor subscription state in the {% internal_link "GraphQL-Pro Dashboard", "/pro/dashboard" %}: - -{{ "/subscriptions/redis_dashboard_1.png" | link_to_img:"Redis Subscription Dashboard" }} - -{{ "/subscriptions/redis_dashboard_2.png" | link_to_img:"Redis Subscription Detail" }} - -## Development Tips - -#### Clear subscription data - -At any time, you can reset your subscription database with the __"Reset"__ button in the {% internal_link "GraphQL-Pro Dashboard", "/pro/dashboard" %}, or in Ruby: - -```ruby -# Wipe all subscription data from the DB: -MySchema.subscriptions.clear -``` - -#### Developing with webhooks - -To receive webhooks in development, you can [use ngrok](https://www.ably.io/tutorials/webhook-chuck-norris). It gives you a public URL which you can setup with Ably, then any hooks delivered to that URL will be forwarded to your development environment. - -## Client configuration - -Install the [Ably JS client](https://github.com/ably/ably-js) then see docs for: - -- {% internal_link "Apollo Client", "/javascript_client/apollo_subscriptions" %} -- {% internal_link "Relay Modern", "/javascript_client/relay_subscriptions" %}. -- {% internal_link "GraphiQL", "/javascript_client/graphiql_subscriptions" %} diff --git a/vendor/gems/graphql/guides/subscriptions/action_cable_implementation.md b/vendor/gems/graphql/guides/subscriptions/action_cable_implementation.md deleted file mode 100644 index 34641592442..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/action_cable_implementation.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Action Cable Implementation -desc: GraphQL subscriptions over ActionCable -index: 4 ---- - -[ActionCable](https://guides.rubyonrails.org/action_cable_overview.html) is a great platform for delivering GraphQL subscriptions on Rails 5+. It handles message passing (via `broadcast`) and transport (via `transmit` over a websocket). - -To get started, see examples in the API docs: {{ "GraphQL::Subscriptions::ActionCableSubscriptions" | api_doc }}. - -See client usage for: - -- {% internal_link "Apollo Client", "/javascript_client/apollo_subscriptions" %} -- {% internal_link "Relay Modern", "/javascript_client/relay_subscriptions" %}. -- {% internal_link "GraphiQL", "/javascript_client/graphiql_subscriptions" %} diff --git a/vendor/gems/graphql/guides/subscriptions/broadcast.md b/vendor/gems/graphql/guides/subscriptions/broadcast.md deleted file mode 100644 index 9614fe8e590..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/broadcast.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Broadcasts -desc: Delivering the same GraphQL result to multiple subscribers -index: 3 ---- - -GraphQL subscription updates may _broadcast_ data to multiple subscribers. - -A broadcast is a subscription update which is executed _once_, then delivered to _any number_ of subscribers. This reduces the time your server spends running GraphQL queries, since it doesn't have to re-run the query for every subscriber. - -But, __take care__: this approach risks leaking information to subscribers who shouldn't receive it. - -## Setup - -To enable broadcasts, add `broadcast: true` to your subscription setup: - -```ruby -class MyAppSchema < GraphQL::Schema - # ... - use SomeSubscriptionImplementation, - broadcast: true # <---- -end -``` - -Then, any broadcastable field can be configured with `broadcastable: true`: - -```ruby -field :name, String, null: false, - broadcastable: true -``` - -When a subscription comes in where _all_ of its fields are `broadcastable: true`, then it will be handled as a broadcast. - -Additionally, you can set `default_broadcastable: true`: - -```ruby -class MyAppSchema < GraphQL::Schema - # ... - use SomeSubscriptionImplementation, - broadcast: true, - default_broadcastable: true # <---- -end -``` - -With this setting, fields are broadcastable by default. Only a field with `broadcastable: false` in its configuration will cause a subscription to be handled on a subscriber-by-subscriber basis. - -## What fields are broadcastable? - -GraphQL-Ruby can't infer whether a field is broadcastable or not. You must configure it explicitly with `broadcastable: true` or `broadcastable: false`. (The subscription plugin also accepts `default_broadcastable: true|false`.) - -A field is broadcastable if _all clients who request the field will see the same value_. For example: - -- General facts: celebrity names, laws of physics, historical dates -- Public information: object names, document updated-at timestamps, boilerplate info - -For fields like this, you can add `broadcastable: true`. - -A field is __not broadcastable__ if its value is different for different clients. For example: - -- __Viewer-specific information:__ if a field is specifically viewer-based, then it can't be broadcasted to other viewers. For example, `discussion { viewerCanModerate }` might be true for a moderator, but it shouldn't be broadcasted to other viewers. -- __Context-specific information:__ if a field's value takes the request context into consideration, it shouldn't be broadcasted. For example, IP addresses or HTTP header values probably can't be broadcasted. If a field reflects the viewer's timezone, it can't be broadcasted. -- __Restricted information:__ if some viewers see one value, while other viewers see a different value, then it's not broadcastable. Broadcasting this data might leak private information to unauthorized clients. (This includes filtered lists: if the filtering is viewer-by-viewer, it's not broadcastable.) -- __Fields with side effects:__ if the system requires a side effect (eg, logging a metric, updating a database, incrementing a counter) whenever a resolver is executed, it's not a good candidate for broadcasting because some executions will be optimized away. - -These fields can be tagged with `broadcastable: false` so that GraphQL-Ruby will handle them on a subscriber-by-subscriber basis. - -If you want to use subscriptions but have a lot of non-broadcastable fields in your schema, consider building a new set of subscription fields with limited access to other schema objects. Instead, optimize those subscriptions for broacastability. - -## Under the Hood - -GraphQL-Ruby determines which subscribers can receive a broadcast by inspecting: - -- __Query string__. Only exactly-matching query strings will receive the same broadcast. -- __Variables__. Only exactly-matching variable values will receive the same broadcast. -- __Field and Arguments__ given to `.trigger`. They must match the ones initially sent when subscribing. (Subscriptions always worked this way.) -- __Subscription scope__. Only clients with exactly-matching subscription scope can receive the same broadcasts. - -So, take care to {% internal_link "set subscription_scope", "subscriptions/subscription_classes#scope" %} whenever a subscription should be implicitly scoped! - -(See {{ "GraphQL::Subscriptions::Event#fingerprint" | api_doc }} for the implementation of broadcast fingerprints.) - -## Checking for Broadcastable - -For testing purposes, you can confirm that a GraphQL query string is broadcastable by using {{ "Subscriptions#broadcastable?" | api_doc }}: - -```ruby -subscription_string = "subscription { ... }" -MySchema.subscriptions.broadcastable?(subscription_string) -# => true or false -``` - -Use this in your application's tests to make sure that broadcastable fields aren't accidentally made non-broadcastable. - -## Connections and Edges - -You can configure your generated `Connection` and `Edge` types to be broadcastable by setting `default_broadcastable(true)` in their definition: - -```ruby -# app/types/base_connection.rb -class Types::BaseConnection < Types::BaseObject - include GraphQL::Types::Relay::ConnectionBehaviors - default_broadcastable(true) -end - -# app/types/base_edge.rb -class Types::BaseEdge < Types::BaseObject - include GraphQL::Types::Relay::EdgeBehaviors - default_broadcastable(true) -end -``` - -(In your `BaseObject`, you should also have `connection_type_class(Types::BaseConnection)` and `edge_type_class(Types::BaseEdge)`.) - -`PageInfo` is broadcastable by default. diff --git a/vendor/gems/graphql/guides/subscriptions/implementation.md b/vendor/gems/graphql/guides/subscriptions/implementation.md deleted file mode 100644 index c34336c2aa5..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/implementation.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Implementation -desc: Subscription execution and delivery -index: 3 ---- - -The {{ "GraphQL::Subscriptions" | api_doc }} plugin is a base class for implementing subscriptions. - -Each method corresponds to a step in the subscription lifecycle. See the API docs for method-by-method documentation: {{ "GraphQL::Subscriptions" | api_doc }}. - -Also, see the {% internal_link "Pusher implementation guide", "subscriptions/pusher_implementation" %}, the {% internal_link "Ably implementation guide", "subscriptions/ably_implementation" %}, the {% internal_link "ActionCable implementation guide", "subscriptions/action_cable_implementation" %} or {{ "GraphQL::Subscriptions::ActionCableSubscriptions" | api_doc }} docs for an example implementation. - -## Considerations - -Every Ruby application is different, so consider these points when implementing subscriptions: - -- Is your application single-process or multiprocess? Single-process applications can store state in memory while multiprocess applications need a message broker to keep all processes up-to-date. -- What components of your application can be used for persistence and message passing? -- How will you deliver push updates to subscribed clients? (For example, websockets, ActionCable, Pusher, webhooks, or something else?) -- How will you handle [thundering herd](https://en.wikipedia.org/wiki/Thundering_herd_problem)s? When an event is triggered, how will you manage database access to update clients without swamping your system? - -## Broadcasts - -_Broadcasting_ updates to multiple subscribers is supported by GraphQL-Ruby, but requires implementation-specific work, see more in the {% internal_link "Broadcast guide", "subscriptions/broadcast" %}. diff --git a/vendor/gems/graphql/guides/subscriptions/multi_tenant.md b/vendor/gems/graphql/guides/subscriptions/multi_tenant.md deleted file mode 100644 index 55ccde8b405..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/multi_tenant.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Multi-Tenant -desc: Switching tenants in GraphQL Subscription execution -index: 8 ---- - -In a multi-tenant system, data from many different accounts is stored on the same server. (An account might be an organization, a customer, a namespace, a domain, etc -- these are all _tenants_.) Gems like [Apartment](https://github.com/influitive/apartment) assist with this arrangement, but it can also be implemented in the application. Here are a few considerations for this architecture when using GraphQL subscriptions. - -## Add Tenant to `context` - -All the approaches below will use `context[:tenant]` to identify the tenant during GraphQL execution, so make sure to assign it before executing a query: - -```ruby -context = { - viewer: current_user, - tenant: current_user.tenant, - # ... -} - -MySchema.execute(query_str, context: context, ...) -``` - -## Tenant-based `subscription_scope` - -When subscriptions are delivered, {% internal_link "`subscription_scope`", "subscriptions/subscription_classes#scope" %} is one element used to route data to the right subscriber. In short, it's the _implicit_ identifier for the receiver. In a multi-tenant architecture, `subscription_scope` should reference the context key that names the tenant, for example: - -```ruby -class BudgetWasApproved < GraphQL::Schema::Subscription - subscription_scope :tenant # This would work with `context[:tenant] => "acme-corp"` - # ... -end - -# Include the scope when `.trigger`ing: -BudgetSchema.subscriptions.trigger(:budget_was_approved, {}, { ... }, scope: "acme-corp") -``` - - -Alternatively, `subscription_scope` might name something that _belongs_ to the tenant: - -```ruby -class BudgetWasApproved < GraphQL::Schema::Subscription - subscription_scope :project_id # This would work with `context[:project_id] = 1234` -end - -# Include the scope when `.trigger`ing: -BudgetSchema.subscriptions.trigger(:budget_was_approved, {}, { ... }, scope: 1234) -``` - -As long as `project_id` is unique among _all_ tenants, that would work fine too. But _some_ scope is required so that subscriptions can be disambiguated between tenants. - -## Choosing a tenant for execution - -There are a few places where subscriptions might need to load data: - -- When building the payload for the subscription (fetching data to prepare the result) -- `ActionCableSubscriptions`: when deserializing the JSON string broadcasted by `ActionCable` -- `PusherSubscriptions` and `AblySubscriptions`: when deserializing query context - -Each of these operations will need to select the right tenant in order to load data properly. - -For __building the payload__, use a {% internal_link "Trace module", "queries/tracing" %}: - -```ruby -module TenantSelectionTrace - def execute_multiplex(multiplex:) # this is the top-level, umbrella event - context = data[:multiplex].queries.first.context # This assumes that all queries in a multiplex have the same tenant - MultiTenancy.select_tenant(context[:tenant]) do - # ^^ your multi-tenancy implementation here - super # Call through to the rest of execution - end - end -end - -# ... -class MySchema < GraphQL::Schema - trace_with(TenantSelectionTrace) -end -``` - -The tracer above will use `context[:tenant]` to select a tenant for the duration of execution for _all_ queries, mutations, and subscriptions. - -For __deserializing ActionCable messages__, provide a `serializer:` object that implements `.dump(obj)` and `.load(string, context)`: - -```ruby -class MultiTenantSerializer - def self.dump(obj) - GraphQL::Subscriptions::Serialize.dump(obj) - end - - def self.load(string, context) - MultiTenancy.select_tenant(context[:tenant]) do - GraphQL::Subscriptions::Serialize.load(string) - end - end -end - -# ... -class MySchema < GraphQL::Schema - # ... - use GraphQL::Subscriptions::ActionCableSubscriptions, serializer: MultiTenantSerializer -end -``` - -The implementation above will use the built-in serialization algorithms, but it will do so _in the context of_ the selected tenant. - -For __loading query context in Pusher and Ably__, add tenant selection to your `load_context` method, if required: - -```ruby -class CustomSubscriptions < GraphQL::Pro::PusherSubscriptions # or `GraphQL::Pro::AblySubscriptions` - def dump_context(ctx) - JSON.dump(ctx.to_h) - end - - def load_context(ctx_string) - ctx_data = JSON.parse(ctx_string) - MultiTenancy.select_tenant(ctx_data["tenant"]) do - # Build a symbol-keyed hash, loading objects from the database if necessary - # to use a `context: ...` - end - end -end -``` - -With that approach, the selected tenant will be active when building the context hash, in case any objects need to be loaded from the database. diff --git a/vendor/gems/graphql/guides/subscriptions/overview.md b/vendor/gems/graphql/guides/subscriptions/overview.md deleted file mode 100644 index 29a1241f27f..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/overview.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Overview -desc: Introduction to Subscriptions in GraphQL-Ruby -index: 0 ---- - -_Subscriptions_ allow GraphQL clients to observe specific events and receive updates from the server when those events occur. This supports live updates, such as websocket pushes. Subscriptions introduce several new concepts: - -- The __Subscription type__ is the entry point for subscription queries -- __Subscription classes__ are resolvers for handing initial subscription requests and subsequent updates -- __Triggers__ begin the update process -- The __Implementation__ provides application-specific methods for executing & delivering updates. -- __Broadcasts__ can send the same GraphQL result to any number of subscribers. - -## Subscription Type - -`subscription` is an entry point to your GraphQL schema, like `query` or `mutation`. It is defined by your `SubscriptionType`, a root-level `GraphQL::Schema::Object`. - -Read more in the {% internal_link "Subscription Type guide", "subscriptions/subscription_type" %}. - -## Subscription Classes - -{{ "GraphQL::Schema::Subscription" | api_doc }} is a resolver class with subscription-specific behaviors. Each subscription field should be implemented by a subscription class. - -Read more in the {% internal_link "Subscription Classes guide", "subscriptions/subscription_classes" %} - -## Triggers - -After an event occurs in our application, _triggers_ begin the update process by sending a name and payload to GraphQL. - -Read more in the {% internal_link "Triggers guide","subscriptions/triggers" %}. - -## Implementation - -Besides the GraphQL component, your application must provide some subscription-related plumbing, for example: - -- __state management__: How does your application keep track of who is subscribed to what? -- __transport__: How does your application deliver payloads to clients? -- __queueing__: How does your application distribute the work of re-running subscription queries? - -Read more in the {% internal_link "Implementation guide", "subscriptions/implementation" %} or check out the {% internal_link "ActionCable implementation", "subscriptions/action_cable_implementation" %}, {% internal_link "Pusher implementation", "subscriptions/pusher_implementation" %} or {% internal_link "Ably implementation", "subscriptions/ably_implementation" %}. - -## Broadcasts - -By default, the subscription implementations listed above handle each subscription in total isolation. However, this behavior can be optimized by setting up broadcasts. Read more in the {% internal_link "Broadcast guide", "subscriptions/broadcast" %}. - -## Multi-Tenant - -See the {% internal_link "Multi-tenant guide", "subscriptions/multi_tenant" %} for supporting multi-tenancy in GraphQL subscriptions. diff --git a/vendor/gems/graphql/guides/subscriptions/pusher_implementation.md b/vendor/gems/graphql/guides/subscriptions/pusher_implementation.md deleted file mode 100644 index d7ecbf9eb0e..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/pusher_implementation.md +++ /dev/null @@ -1,317 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Pusher Implementation -desc: GraphQL subscriptions over Pusher -index: 6 -pro: true ---- - -[GraphQL Pro](https://graphql.pro) includes a subscription system based on [Redis](https://redis.io) and [Pusher](https://pusher.com) which works with any Ruby web framework. - -After creating an app on Pusher and [configuring the Ruby gem](https://github.com/pusher/pusher-http-ruby#global), you can hook it up to your GraphQL schema. - -## How it Works - -This subscription implementation uses a hybrid approach: - -- __Your app__ takes GraphQL queries an runs them -- __Redis__ stores subscription data for later updates -- __Pusher__ sends updates to subscribed clients - -So, the lifecycle goes like this: - -- A `subscription` query is sent by HTTP Post to your server (just like a `query` or `mutation`) -- The response contains a Pusher channel ID (as an HTTP header) which the client may subscribe to -- The client opens that Pusher channel -- When the server triggers updates, they're delivered over the Pusher channel -- When the client unsubscribes, the server receives a webhook and responds by removing its subscription data - -Here's another look: - -``` -1. Subscription is created in your app - - HTTP POST - .----------> write to Redis - 📱 ⚙️ -----> 💾 - <---------' - X-Subscription-ID: 1234 - - -2. Client opens a connection to Pusher - - websocket - 📱 <---------> ☁️ - - -3. The app sends updates via Pusher - - ⚙️ ---------> ☁️ ------> 📱 - POST update - (via gem) (via websocket) - - -4. When the client unsubscribes, Pusher notifies the app - - webhook - ⚙️ <-------- ☁️ (disconnect) 📱 -``` - - -By using this configuration, you can use GraphQL subscriptions without hosting a push server yourself! - -## Database setup - -Subscriptions require a _persistent_ Redis database, configured with: - -```sh -maxmemory-policy noeviction -# optional, more durable persistence: -appendonly yes -``` - -Otherwise, Redis will drop data that doesn't fit in memory (read more in ["Redis persistence"](https://redis.io/topics/persistence)). - -If you're already using Redis in your application, see ["Storing Data in Redis"](https://www.mikeperham.com/2015/09/24/storing-data-with-redis/) for options to isolate data and tune your configuration. - -## Schema configuration - -Add `redis` to your `Gemfile`: - -```ruby -gem 'redis' -``` - -and `bundle install`. Then create a Redis instance: - -```ruby -# for example, in an initializer: -$graphql_subscriptions_redis = Redis.new # default connection -``` - -Then, that Redis client is passed to the Subscription configuration: - -```ruby -class MySchema < GraphQL::Schema - use GraphQL::Pro::PusherSubscriptions, redis: $graphql_subscriptions_redis -end -``` - -That connection will be used for managing subscription state. All writes to Redis are prefixed with `graphql:sub:`. - -There are also two configurations for managing persistence: - -- `stale_ttl_s:` expires subscription data after the given number of seconds without any update. After `stale_ttl_s` has passed, the data will expire from Redis. Each time a subscription receives an update, its TTL is refreshed. (Generally, this isn't required because the backend is built to clean itself up. But, if you find that Redis is collecting stale queries, you can set them to expire after some very long time as a safeguard.) -- `cleanup_delay_s:` (default: `5`) prevents deleting a subscription during those first seconds after it's created. Usually, a longer delay isn't necessary, but if you observe latency between the subscription's initial response and the client's subscription to the delivery channel, you can set this configuration to account for it. - -### Connection Pool - -For better performance reading and writing to Redis, you can pass a `connection_pool:` instead of `redis:`, using the [`connection_pool` gem](https://github.com/mperham/connection_pool): - -```ruby - use GraphQL::Pro::PusherSubscriptions, - connection_pool: ConnectionPool.new(size: 5, timeout: 5) { Redis.new }, -``` - -### Broadcasts - -If you set up {% internal_link "Broadcasts", "/subscriptions/broadcast" %}, then you can update many clients over a single Pusher channel. - -Broadcast channels have stable, predictable IDs. To prevent unauthorized clients from "listening in," use an [authorized Pusher channel](#authorization) for transport. In your authorization code, you can check for a broadcast using `.broadcast_subscription_id?`: - -```ruby -# In your Pusher authorization endpoint: -channel_name = params[:channel_name] -MySchema.subscriptions.broadcast_subscription_id?(channel_name) -# => true | false -``` - -## Execution configuration - -During execution, GraphQL will assign a `subscription_id` to the `context` hash. The client will use that ID to listen for updates, so you must return the `subscription_id` in the response headers. - -Return `result.context[:subscription_id]` as the `X-Subscription-ID` header. For example: - -```ruby -result = MySchema.execute(...) -# For subscriptions, return the subscription_id as a header -if result.subscription? - response.headers["X-Subscription-ID"] = result.context[:subscription_id] -end -render json: result -``` - -This way, the client can use that ID as a Pusher channel. - -For __CORS requests__, you need a special header so that clients can read the custom header: - -```ruby -if result.subscription? - response.headers["X-Subscription-ID"] = result.context[:subscription_id] - # Required for CORS requests: - response.headers["Access-Control-Expose-Headers"] = "X-Subscription-ID" -end -``` - -Read more here: ["Using CORS"](https://www.html5rocks.com/en/tutorials/cors/). - -### Payload Compression - -To mitigate problems with [Pusher's 10kb message limit](https://support.pusher.com/hc/en-us/articles/4412243423761-What-Is-The-Message-Size-Limit-When-Publishing-an-Event-in-Channels-), you can specify `compress_pusher_payload: true` in the `context` of your subscription. For example: - -```ruby -# app/controllers/graphql_controller.rb -def execute - # ... - # Somehow detect whether the client supports compressed payloads, - # for example, User-Agent, query param, or request header: - if client_supports_compressed_payloads? - context[:compress_pusher_payload] = true - end - # ... -end -``` - -This will cause subscription payloads to include `compressed_result: "..."` instead of `result: "..."` when they're sent over Pusher. See docs for {% internal_link "Apollo Client", "/javascript_client/apollo_subscriptions" %} or {% internal_link "Relay Modern", "/javascript_client/relay_subscriptions" %} to read about preparing clients for compressed payloads. - -By configuring `compress_pusher_payload: true` on a query-by-query basis, the subscription backend can continue to support clients running _old_ client code (by not compressing) while upgrading new clients to compressed payloads. - -### Batched Deliveries - -By default, `PusherSubscriptions` sends updates in batches of up to 10 at a time, using [batch triggers](https://github.com/pusher/pusher-http-ruby#batches). You can customize the batch size by passing `batch_size:` when installing it, for example: - -```ruby -use GraphQL::Pro::PusherSubscriptions, batch_size: 1, ... -``` - -`batch_size: 1` will make `PusherSubscriptions` use the single trigger API instead of batch triggers. - -## Webhook configuration - -Your server needs to receive webhooks from Pusher when clients disconnect. This keeps your local subscription database in sync with Pusher. - -In the Pusher web UI, Add a webhook for "Channel existence" - -{{ "/subscriptions/pusher_webhook_configuration.png" | link_to_img:"Pusher Webhook Configuration" }} - -Then, mount the Rack app for handling webhooks from Pusher. For example, on Rails: - -```ruby -# config/routes.rb - -# Include GraphQL::Pro's routing extensions: -using GraphQL::Pro::Routes - -Rails.application.routes.draw do - # ... - # Handle Pusher webhooks for subscriptions: - mount MySchema.pusher_webhooks_client, at: "/pusher_webhooks" -end -``` - -This way, we'll be kept up-to-date with Pusher's unsubscribe events. - -__Alternatively__, you can configure the routes to load your schema lazily, during the first request: - -```ruby -# Provide the fully-qualified class name of your schema: -lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema") -mount lazy_routes.pusher_webhooks_client, at: "/pusher_webhooks" -``` - -## Authorization - -To ensure the privacy of subscription updates, you should use a [private channel](https://pusher.com/docs/client_api_guide/client_private_channels) for transport. - -To use a private channel, add a `channel_prefix:` key to your query context: - -```ruby -MySchema.execute( - query_string, - context: { - # If this query is a subscription, use this prefix for the Pusher channel: - channel_prefix: "private-user-#{current_user.id}-", - # ... - }, - # ... -) -``` - -That prefix will be applied to GraphQL-related Pusher channel names. (The prefix should begin with `private-`, as required by Pusher.) - -Then, in your [auth endpoint](https://pusher.com/docs/authenticating_users#implementing_private_endpoints), you can assert that the logged-in user matches the channel name: - -```ruby -if params[:channel_name].start_with?("private-user-#{current_user.id}-") - # success, render the auth token -else - # failure, render unauthorized -end -``` - -## Serializing Context - -Since subscription state is stored in the database, then reloaded for pushing updates, you have to serialize and reload your query `context`. - -By default, this is done with {{ "GraphQL::Subscriptions::Serialize" | api_doc }}'s `dump` and `load` methods, but you can provide custom implementations as well. To customize the serialization logic, create a subclass of `GraphQL::Pro::PusherSubscriptions` and override `#dump_context(ctx)` and `#load_context(ctx_string)`: - -```ruby -class CustomSubscriptions < GraphQL::Pro::PusherSubscriptions - def dump_context(ctx) - context_hash = ctx.to_h - # somehow convert this hash to a string, return the string - end - - def load_context(ctx_string) - # Given the string from the DB, create a new hash - # to use as `context:` - end -end -``` - -Then, use your _custom_ subscriptions class instead of the built-in one for your schema: - -```ruby -class MySchema < GraphQL::Schema - # Use custom subscriptions instead of GraphQL::Pro::PusherSubscriptions - # to get custom serialization logic - use CustomSubscriptions, redis: $redis -end -``` - -That gives you fine-grained control of context reloading. - -## Dashboard - -You can monitor subscription state in the {% internal_link "GraphQL-Pro Dashboard", "/pro/dashboard" %}: - -{{ "/subscriptions/redis_dashboard_1.png" | link_to_img:"Redis Subscription Dashboard" }} - -{{ "/subscriptions/redis_dashboard_2.png" | link_to_img:"Redis Subscription Detail" }} - -## Development Tips - -#### Clear subscription data - -At any time, you can reset your subscription database with the __"Reset"__ button in the {% internal_link "GraphQL-Pro Dashboard", "/pro/dashboard" %}, or in Ruby: - -```ruby -# Wipe all subscription data from the DB: -MySchema.subscriptions.clear -``` - -#### Developing with Pusher webhooks - -To receive Pusher's webhooks in development, Pusher [suggests using ngrok](https://support.pusher.com/hc/en-us/articles/203112227-Developing-against-and-testing-WebHooks). It gives you a public URL which you can setup with Pusher, then any hooks delivered to that URL will be forwarded to your development environment. - -## Client configuration - -Install the [Pusher JS client](https://github.com/pusher/pusher-js) then see docs for: - -- {% internal_link "Apollo Client", "/javascript_client/apollo_subscriptions" %} -- {% internal_link "Relay Modern", "/javascript_client/relay_subscriptions" %} -- {% internal_link "GraphiQL", "/javascript_client/graphiql_subscriptions" %} -- {% internal_link "urql", "/javascript_client/urql_subscriptions" %} diff --git a/vendor/gems/graphql/guides/subscriptions/pusher_webhook_configuration.png b/vendor/gems/graphql/guides/subscriptions/pusher_webhook_configuration.png deleted file mode 100644 index f6757cbacdfaca95f8a56b55bfce72fe23811b1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42914 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 P0}yz+`njxgN@xNAbk}6; diff --git a/vendor/gems/graphql/guides/subscriptions/redis_dashboard_2.png b/vendor/gems/graphql/guides/subscriptions/redis_dashboard_2.png deleted file mode 100644 index d378564b3d17bdfe41533e175933a609b5cd7a49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166239 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

    5`tp}~UffNnWx#|Dst zt#b4kI_okykq%|_^EjeQ#$!}8o_lh_M=>XH;2UxsPJ*$U<*H}MOrrLSJ&DlHKxcQs zPHpZ7Gq<5(|tK3|;cdGnpE@CGH#lqVCnBzETxaz8&uI*J?W9Q4 zsqA5(Xta{@{|S6&{13BU=Ej|2Ys-AdAgJA^)WkpA=RX@JTr?m5P=rpoXJw8VHJ+JX zxwuHfq@%MQbm7deb6;GQ;Drb|ct{>W;-}-!u({8j-{sLj_#gIImGwvQ+e5x}!#1j#3tRj3M}ZQi!`WG`g)3@_TLRPISRc08xXb`s819fBdtF zs@mp|Zh{UkcvJePcTBY~+9q#o=|L-236Z<8aMazD$uq4;qSfc7uZ$`m4(Co^uro&h1L=y?J=@YY#`I?1 z1#1`3^v3gi`$f0UKai23yE^1)Dve;^k<`?f0L}T*f^%z1&m_hlG;GB43W_yltYtfA zkcsPljgFzc{STaeeo@Lyv?|Waz=AmxP=2k&bcQnhUFOk;#tWE0kFyO{mLJ77%t5M| zXS~uh??*jQT-1Qt8FE=K`p@Y2sNh*faC``XFP`6J-#E)?Vqq`+cpg*3qWJ%9%|+~i z1Fysiw7IX8CfJpys+gLc-B^~I6-=;Z)nRP3;yd&F(UV4Z?v~r;Zp{aXEKt~<70dG; z`Iw*68@rsl?Pm3L_hlP23D!lxqUi1!$MbvZmBLK7yO#yK{NEY0J4fcXjA$a`Uw#;$ES^n*-dyo?mL6T<5npumhf3a4G$8`u=D>|K73fXxnbfkPVbwSagO>*xd9 zBTr4|RS~ke6gagWpU%9>;bk%reCjk#S06C(LjU;*=QglH!x5W#>h;G2~SJz zO3KO2)9Zc=PbIh*4y=sE&$JKRAlP%t0 zD?&|1pbHy+av78ntP`Su_40M6Kv`T~g(ZyzO~8_tRbn@x`ocjMSn6aiT){`EtPgMm zj$twN+$CtS7%*tI286H^D8&R;C;-En@fd-{Cjtrq+`yoT(pW`k^&W8HHZZIilFbQP y3|yU3qbqWi(AXo37BH;C8#WSbonCQz@SmN-^Mr7;O&m7^5O})!xvX - if comment.author == context[:viewer] - NO_UPDATE - else - # Continue updating this client, since it's not the commenter - super - end - end -end -``` - -### Returning a different object for subscription updates - -By default, whatever object you pass to `.trigger(event_name, args, object)` will be used for responding to subscription fields. But, you can return a different object from `#update` to override this: - -```ruby -field :queue, Types::QueueType, null: false - -# eg, `MySchema.subscriptions.trigger("queueWasUpdated", {name: "low-priority"}, :low_priority)` -def update(name:) - # Make a Queue object which _represents_ the queue with this name - queue = JobQueue.new(name) - - # This object was passed to `.trigger`, but we're ignoring it: - object # => :low_priority - - # return the queue instead: - { queue: queue } -end -``` - -## Terminating the subscription with #unsubscribe - -Within a subscription method, you may call `unsubscribe` to terminate the client's subscription, for example: - -```ruby -def update(room:) - if room.archived? - # Don't let anyone subscribe to messages on an archived room - unsubscribe - else - super - end -end -``` - -`#unsubscribe` has the following effects: - -- The subscription is unregistered from the backend (this is backend-specific) -- The client is told to unsubscribe (this is transport-specific) - -Arguments with `loads:` configurations will call `unsubscribe` if they are `required: true` (which is the default) and their ID doesn't return a value. (It's assumed that the subscribed object was deleted.) - -You can provide a final update value with `unsubscribe` by passing a value to the method: - -```ruby -def update(room:) - if room.archived? - # Don't let anyone subscribe to messages on an archived room - unsubscribe({message: "This room has been archived"}) - else - super - end -end -``` - -## Extras - -Subscription methods can access query-related metadata by configuring `extras [...]` in the class definition. For example, to use a `lookahead` and the `ast_node`: - -```ruby -class Subscriptions::JobFinished < GraphQL::Schema::Subscription - # ... - extras [:lookahead, :ast_node] - - def subscribe(lookahead:, ast_node:) - # ... - end - - def update(lookahead:, ast_node:) - # ... - end -end -``` - -See the {% internal_link "Extra Field Metadata", "/fields/introduction#extra-field-metadata" %} for more information about available metadata. diff --git a/vendor/gems/graphql/guides/subscriptions/subscription_type.md b/vendor/gems/graphql/guides/subscriptions/subscription_type.md deleted file mode 100644 index e8b1c5290e9..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/subscription_type.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Subscription Type -desc: The root type for subscriptions -index: 1 ---- - -`Subscription` is the entry point for all subscriptions in a GraphQL system. Each field corresponds to an event which may be subscribed to: - -```graphql -type Subscription { - # Triggered whenever a post is added - postWasPublished: Post - # Triggered whenever a comment is added; - # to watch a certain post, provide a `postId` - commentWasPublished(postId: ID): Comment -} -``` - -This type is the root for `subscription` operations, for example: - -```graphql -subscription { - postWasPublished { - # This data will be delivered whenever `postWasPublished` - # is triggered by the server: - title - author { - name - } - } -} -``` - -To add subscriptions to your system, define an `ObjectType` named `Subscription`: - -```ruby -# app/graphql/types/subscription_type.rb -class Types::SubscriptionType < GraphQL::Schema::Object - field :post_was_published, subscription: Subscriptions::PostWasPublished - # ... -end -``` - -Then, add it as the subscription root with `subscription(...)`: - -```ruby -# app/graphql/my_schema.rb -class MySchema < GraphQL::Schema - query(Types::QueryType) - # ... - # Add Subscription to - subscription(Types::SubscriptionType) -end -``` - -See {% internal_link "Implementing Subscriptions","subscriptions/implementation" %} for more about actually delivering updates. - -See {% internal_link "Subscription Classes", "subscriptions/subscription_classes" %} for more about implementing subscription root fields. diff --git a/vendor/gems/graphql/guides/subscriptions/triggers.md b/vendor/gems/graphql/guides/subscriptions/triggers.md deleted file mode 100644 index c55cf7e5a53..00000000000 --- a/vendor/gems/graphql/guides/subscriptions/triggers.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Subscriptions -title: Triggers -desc: Sending updates from your application to GraphQL -index: 2 ---- - -From your application, you can push updates to GraphQL clients with `.trigger`. - -Events are triggered _by name_, and the name must match fields on your {% internal_link "Subscription Type","subscriptions/subscription_type" %} - -```ruby -# Update the system with the new blog post: -MySchema.subscriptions.trigger(:post_added, {}, new_post) -``` - -The arguments are: - -- `name`, which corresponds to the field on subscription type -- `arguments`, which corresponds to the arguments on subscription type (for example, if you subscribe to comments on a certain post, the arguments would be `{post_id: comment.post_id}`.) -- `object`, which will be the root object of the subscription update -- `scope:` (shown below) for implicitly scoping the clients who will receive updates. - -## Scope - -To send updates to _certain clients only_, you can use `scope:` to narrow the trigger's reach. - -Scopes are based on query context: a value in `context:` is used as the scope; an equivalent value must be passed with `.trigger(... scope:)` to update that client. (The value is serialized with {{ "GraphQL::Subscriptions::Serialize" | api_doc }}) - -To specify that a topic is scoped, add a `subscription_scope` option to the Subscription class: - -```ruby -class Subscriptions::CommentAdded < Subscription::BaseSubscription - description "A comment was added to one of the viewer's posts" - # For a given viewer, this will be triggered - # whenever one of their posts gets a new comment - subscription_scope :current_user_id - # ... -end -``` - -(Read more in the {% internal_link "Subscription Classes guide", "subscriptions/subscription_classes#scope" %}.) - -Then, subscription operations should have a `context: { current_user_id: ... }` value, for example: - -```ruby -# current_user_id will be the scope for some subscriptions: -MySchema.execute(query_string, context: { current_user_id: current_user.id }) -``` - -Finally, when events happen in your app, you should provide the scoping value as `scope:`, for example: - -```ruby -# A new comment is added -comment = post.comments.create!(attrs) -# notify the author -author_id = post.author.id -MySchema.subscriptions.trigger(:comment_added, {}, comment, scope: author_id) -``` - -Since this trigger has a `scope:`, only subscribers with a matching scope value will be updated. - -## Validation - -By default, subscriptions are re-validated when a trigger causes them to send updates. To disable this, you can pass `validate_update: false` when hooking up subscriptions to your schema. For example: - -```ruby -use SomeSubscriptions, validate_update: false -``` - -If you're sure you won't be releasing breaking changes to your schema, this setting can reduce overhead in evaluating updates. diff --git a/vendor/gems/graphql/guides/testing/helpers.md b/vendor/gems/graphql/guides/testing/helpers.md deleted file mode 100644 index f7e248f8ba2..00000000000 --- a/vendor/gems/graphql/guides/testing/helpers.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Testing -title: Helpers -desc: Running GraphQL fields in isolation -index: 3 ---- - -GraphQL-Ruby ships with a test helper method, `run_graphql_field`, that can execute a GraphQL field in isolation. To use it in your test suite, include the module with your schema class: - -```ruby -# Mix in `run_graphql_field(...)` to run on `MySchema` -include GraphQL::Testing::Helpers.for(MySchema) -``` - -Then, you can run fields using {{ "Testing::Helpers#run_graphql_field" | api_doc }}: - -```ruby -post = Post.first -graphql_post_title = run_graphql_field("Post.title", post) -assert_equal "100 Great Ideas", graphql_post_title -``` - -`run_graphql_field` accepts two required arguments: - -- Field _path_, in `Type.field` format -- Runtime object: some non-`nil` object to resolve the field on. - -Additionally, it accepts some keyword arguments: - -- `arguments:`, GraphQL arguments to the field, in Ruby-style (underscore, symbol) or GraphQL-style (camel-case, string) -- `context:`, the GraphQL context to use for this query - -`run_graphql_field` performs several GraphQL-related steps: - -- Checks `.visible?` on the named Object Type, raising an error if it isn't visible -- Wraps the given runtime object in the GraphQL Object Type -- Checks `.authorized?` on the type, calling {{ "Schema.unauthorized_object" | api_doc }} if authorization fails -- Prepares arguments for field resolution -- Checks `#visible?` on the field, raising an error if the field isn't visible -- Checks `#authorized?` on the field, calling {{ "Schema.unauthorized_field" | api_doc }} if it fails -- Calls any {% internal_link "field extensions", "/type_definitions/field_extensions" %} -- Runs {% internal_link "Dataloader", "/dataloader/overview" %} and/or GraphQL-Batch, as needed - -## Resolving fields on the same object - -You can use {{ "Testing::Helpers#with_resolution_context" | api_doc }} to use the same type, runtime object, and GraphQL context for multiple field resolutions. For example: - -```ruby -# Assuming `include GraphQL::Testing::Helpers.for(MySchema)` -# was used above ... -with_resolution_context(type: "Post", object: example_post, context: { current_user: author }) do |rc| - assert_equal "100 Great Ideas", rc.run_graphql_field("title") - assert_equal true, rc.run_graphql_field("viewerIsAuthor") - assert_equal 5, rc.run_graphql_field("commentsCount") - # Optionally, pass `arguments:` for the field: - assert_equal 9, rc.run_graphql_field("commentsCount", arguments: { include_unmoderated: true }) -end -``` - -The method yields a resolution context (`rc`, above) which responds to `run_graphql_field`. diff --git a/vendor/gems/graphql/guides/testing/integration_tests.md b/vendor/gems/graphql/guides/testing/integration_tests.md deleted file mode 100644 index 6ca8c0c86f4..00000000000 --- a/vendor/gems/graphql/guides/testing/integration_tests.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Testing -title: Integration Tests -desc: Run the whole GraphQL stack in tests -index: 2 ---- - -Besides testing {% internal_link "schema structure", "/testing/schema_structure" %}, you should also test your GraphQL system's behavior. There are really a few levels to this: - -- __Application-level__ behaviors, like business logic, permissions, and persistence. These behaviors may be shared by your API and user interface. -- __Interface-level__ behaviors, like GraphQL fields, mutations, error scenarios, and HTTP-specific behaviors. These are unique to your GraphQL system. -- __Transport-level__ behaviors, like HTTP headers, parameters and status codes - -## Testing Application-Level Behaviors - -When it comes to _how your application behaves_, you should lean on _unit tests_ which exercise application primitives directly. For example, if postings require a title and a body, you should write a test for `Post` which asserts that invalid `Post`s fail to save. Several other components of the application may be tested this way: - -- Permissions: test your authorization system using example resources and actors. Dedicated, high-level frameworks like [Pundit](https://github.com/varvet/pundit) make it easy to test authorization in isolation. -- Business logic: What are the _operations_ that a user can perform in your system? For example, on a blog, they might be: drafting and publishing posts, moderating comments, filtering posts by category, or blocking users. Test these operations in isolation so you can be confident that the core code is correct. -- Persistence (and other external services): how does your app interact with the "outside world", like databases, files, and third-party APIs? These interactions also deserve specific tests. - - -By testing these (and other) application-level behaviors _without_ GraphQL, you can reduce the overhead of your test suite and simplify your testing scenarios. - -## Testing Interface-Level Behaviors - -After building your application, you give it an interface so that people (or other software) can interact with it. Sometimes the interface is a website, other times it's a GraphQL API. The interface has transport-specific primitives that map to your application's primitives. For example, a React app might have components that correspond to `Post`, `Comment`, and a `Moderation` operation. (These components might even be context-specific, like `ThreadComment` or `DraftPost`.) Similarly, a GraphQL interface has types and fields that correspond to the underlying application primitives (like `Post` and `Comment` types, a `Post.isDraft` field, or a `ModerateComment` mutation). - -The best way to test a GraphQL interface is with _integration tests_ which run the whole GraphQL system (using `MySchema.execute(...)`). By using an integration test, you can be sure that all of GraphQL-Ruby's internal systems are engaged (validation, analysis, authorization, data loading, response type-checking, etc.). - -A basic integration test might look like: - -```ruby -it "loads posts by ID" do - query_string = <<-GRAPHQL - query($id: ID!){ - node(id: $id) { - ... on Post { - title - id - isDraft - comments(first: 5) { - nodes { - body - } - } - } - } - } - GRAPHQL - - post = create(:post_with_comments, title: "My Cool Thoughts") - post_id = MySchema.id_from_object(post, Types::Post, {}) - result = MySchema.execute(query_string, variables: { id: post_id }) - - post_result = result["data"]["node"] - # Make sure the query worked - assert_equal post_id, post_result["id"] - assert_equal "My Cool Thoughts", post_result["title"] -end -``` - -To make sure that different parts of the system are properly engaged, you can add integration tests for specific scenarios, too. For example, you could add a test to make sure that data is hidden from some users: - - -```ruby -it "doesn't show draft posts to anyone except their author" do - author = create(:user) - non_author = create(:non_user) - draft_post = create(:post, draft: true, author: author) - - query_string = <<-GRAPHQL - query($id: ID!) { - node(id: $id) { - ... on Post { - isDraft - } - } - } - GRAPHQL - - post_id = MySchema.id_from_object(post, Types::Post, {}) - - # Authors can see their drafts: - author_result = MySchema.execute(query_string, context: { viewer: author }, variables: { id: post_id }) - assert_equal true, author_result["data"]["node"]["isDraft"] - - # Other users can't see others' drafts - non_author_result = MySchema.execute(query_string, context: { viewer: non_author }, variables: { id: post_id }) - assert_nil author_result["data"]["node"] -end -``` - -This test engages the underlying authorization and business logic, and provides a sanity check at the GraphQL interface layer. - -## Testing Transport-Level Behaviors - -GraphQL is usually served over HTTP. You probably want tests that make sure that HTTP inputs are correctly prepared for GraphQL. For example, you might test that: - -- POST data is correctly turned into query variables -- Authentication headers are used to load a `context[:viewer]` - - -In Rails, you might use a [functional test](https://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers) for this, for example: - -```ruby -it "loads user token into the viewer" do - query_string = "{ viewer { username } }" - post graphql_path, params: { query: query_string } - json_response = JSON.parse(@response.body) - assert_nil json_response["data"]["viewer"], "Unauthenticated requests have no viewer" - - # This time, add some authentication to the HTTP request - user = create(:user) - post graphql_path, - params: { query: query_string }, - headers: { "Authorization" => "Bearer #{user.auth_token}" } - - json_response = JSON.parse(@response.body) - assert_equal user.username, json_response["data"]["viewer"]["username"], "Authenticated requests load the viewer" -end -``` diff --git a/vendor/gems/graphql/guides/testing/overview.md b/vendor/gems/graphql/guides/testing/overview.md deleted file mode 100644 index 6c12f827be0..00000000000 --- a/vendor/gems/graphql/guides/testing/overview.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Testing -title: Overview -desc: Testing a GraphQL system -index: 0 -redirect_from: - - /schema/testing ---- - - -So, you've spiked a GraphQL API, and now you're ready to tighten things up and add some proper tests. These guides will help you think about how to ensure stability and compatibility for your GraphQL system. - -- {% internal_link "Structure testing", "/testing/schema_structure" %} verifies that schema changes are backwards-compatible. This way, you don't break existing clients. -- {% internal_link "Integration testing", "/testing/integration_tests" %} exercises the various behaviors of the GraphQL system, making sure that it returns the right data to the right clients. -- {% internal_link "Testing helpers", "/testing/helpers" %} for running GraphQL fields without writing a whole query diff --git a/vendor/gems/graphql/guides/testing/profiling.md b/vendor/gems/graphql/guides/testing/profiling.md deleted file mode 100644 index 68c577a05f3..00000000000 --- a/vendor/gems/graphql/guides/testing/profiling.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Testing -title: Profiling -desc: Profiling the performance of GraphQL-Ruby -index: 4 ---- - -If you want to know more about how time is spent during GraphQL queries, including GraphQL-Ruby internals, you can use Ruby profiling tools to take a closer look. - -If you want to investigate GraphQL-Ruby performance together, prepare a runtime profile and memory profile as described below and {% open_an_issue "Performance investigation" %} on GitHub, including those files. - -## StackProf - -[StackProf](https://github.com/tmm1/stackprof) is a Ruby library for figuring out where an operation's time is spent. To capture a profile, surround a block with `StackProf.run { ... }`. - -```ruby -require "stackprof" - -# Prepare any GraphQL-related data or context: -query_string = "{ someGraphQL ... }" -context = { ... } - -# This will dump a profile in `tmp/graphql-prof.dump` -StackProf.run(mode: :wall, interval: 10, out: "tmp/graphql-prof.dump") do - # Execute the query inside the block: - MySchema.execute(query_string, context: context) -end -``` - -The `out:` option tells StackProf to create a "dump" at the given location. Then, anyone who has that file can investigate the profile using the `stackprof` command, for example: - -``` -$ stackprof tmp/graphql-prof.dump -================================== - Mode: wall(1) - Samples: 2492 (58.06% miss rate) - GC: 0 (0.00%) -================================== - TOTAL (pct) SAMPLES (pct) FRAME - 902 (36.2%) 94 (3.8%) GraphQL::Execution::Interpreter::Runtime#evaluate_selection_with_resolved_keyword_args - 1283 (51.5%) 87 (3.5%) GraphQL::Execution::Interpreter::Runtime#continue_field - 274 (11.0%) 78 (3.1%) GraphQL::Schema::Field#resolve - 1068 (42.9%) 73 (2.9%) GraphQL::Execution::Interpreter::Runtime#evaluate_selection - # ... -``` - -Additionally, `stackprof` accepts a `--method` argument which provides details about the performance and usage of a specific method, for example: - -``` -$ stackprof tmp/small.dump --method #gather_selections -GraphQL::Execution::Interpreter::Runtime#gather_selections (/Users/rmosolgo/code/graphql-ruby/lib/graphql/execution/interpreter/runtime.rb:305) - samples: 17 self (0.7%) / 17 total (0.7%) - callers: - 16 ( 94.1%) GraphQL::Execution::Interpreter::Runtime#continue_field - 6 ( 35.3%) Array#each - 1 ( 5.9%) GraphQL::Execution::Interpreter::Runtime#run_eager - callees (0 total): - 6 ( Inf%) Array#each - code: - 1 (0.0%) / 1 (0.0%) | 305 | when :lookahead - 6 (0.2%) / 6 (0.2%) | 306 | if !field_ast_nodes - 3 (0.1%) / 3 (0.1%) | 307 | field_ast_nodes = [ast_node] - | 308 | end -``` - -Anyone with the `.dump` file can perform this analysis -- it's a really useful file! If you want to investigate GraphQL-Ruby performance together, please share a runtime profile. - - -## MemoryProfiler - -[MemoryProfiler](https://github.com/SamSaffron/memory_profiler) provides insight into where an operation interacts with system memory and the Ruby heap. This is helpful because memory usage problems cause code to run slowly; fixing them can make code run fast. - -To produce a report, wrap a block in `MemoryProfiler.report { ... }` and then call `.pretty_print` on the result. For example, to create a report on a GraphQL query: - -```ruby -require 'memory_profiler' - -# Prepare any GraphQL-related data or context: -query_string = "{ someGraphQL ... }" -context = { ... } - -report = MemoryProfiler.report do - # Execute the query inside the block: - MySchema.execute(query_string, context: context) -end - -# Write the result to a file -report.pretty_print(to_file: "tmp/graphql-memory.txt") -``` - -The report will include many interesting sections including: - -- Total memory and objects allocated -- Objects allocated by location and by class -- String allocations, including the number of times a string with the same value was allocated - -All of these can indicate "hot spots" in the code and inform refactors to reduce memory use. In turn, this reduces time spent in Ruby GC. - -If you want to investigate GraphQL-Ruby performance together, please share a memory profile. diff --git a/vendor/gems/graphql/guides/testing/schema_structure.md b/vendor/gems/graphql/guides/testing/schema_structure.md deleted file mode 100644 index e2e1e85fc45..00000000000 --- a/vendor/gems/graphql/guides/testing/schema_structure.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Testing -title: Schema Structure -desc: Make sure that your schema changes are backwards-compatible -index: 1 ---- - -Structural changes to a GraphQL schema come in two categories: - -- __Breaking__ changes may cause previously-valid queries to become invalid. For example, if you remove the `title` field, anyone who tries to query that field will get a validation error instead of response data. -- __Non-breaking__ changes _add_ options to a schema without breaking previously-valid queries. - -Making a _breaking_ change can be bad news for your API clients, since their applications may break. But, sometimes they're required. _Non-breaking_ changes don't affect existing queries, since they just _add_ new parts to the schema. - -Here are few tips for managing schema structure changes. - -## Maintain a `.graphql` schema dump - -Make structure changes part of the normal code review process by adding a `schema.graphql` artifact to your project. This way, any changes to schema structure will show up clearly in a pull request as a diff to that file. - -You can read about this approach in ["Tracking Schema Changes with GraphQL-Ruby"](https://rmosolgo.github.io/ruby/graphql/2017/03/16/tracking-schema-changes-with-graphql-ruby) or the built-in {{ "GraphQL::RakeTask" | api_doc }} for generating schema dumps. - -## Automatically check for breaking changes - -You can use [GraphQL::SchemaComparator](https://github.com/xuorig/graphql-schema_comparator) to check for breaking changes during development or CI. If you maintain a dump of queries that typically run against your server, you may also utilize `GraphQL::StaticValidation` to validate these queries directly. A Rake task such as the one below can be used to identify changes that are incompatible with existing queries. - -```ruby -namespace :graphql do - namespace :queries do - desc 'Validates GraphQL queries against the current schema' - task validate: [:environment] do - queries_file = 'test/fixtures/files/queries.json' - queries = Oj.load(File.read(queries_file)) - - Validate.run_validate(queries, MySchema) - end - - module Validate - def self.run_validate(queries, schema) - puts '⏳ Validating queries...' - puts "\n" - - results = queries.map { |query| schema.validate(query) } - errors = results.flatten - - if errors.empty? - puts '✅ All queries are valid' - else - print_errors(errors) - end - end - - def self.print_errors(errors) - puts 'Detected the following errors:' - puts "\n" - - errors.each do |error| - path = error.path.join(', ') - puts "❌ #{path}: #{error.message}" - end - end - end - end -end -``` diff --git a/vendor/gems/graphql/guides/type_definitions/directives.md b/vendor/gems/graphql/guides/type_definitions/directives.md deleted file mode 100644 index 21182f0c454..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/directives.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Directives -desc: Special instructions for the GraphQL runtime -index: 10 ---- - - -Directives are system-defined keywords with two kinds of uses: - -- [runtime directives](#runtime-directives) _modify execution_, so that when they are present, the GraphQL runtime does something different; -- [schema directives](#schema-directives) _annotate schema definitions_, indicating different configurations or metadata about schemas and types. - -## Runtime Directives - -Runtime directives are server-defined keywords that modify GraphQL execution. All GraphQL systems have at least _two_ directives, `@skip` and `@include`. For example: - -```ruby -query ProfileView($renderingDetailedProfile: Boolean!){ - viewer { - handle - # These fields will be included only if the check passes: - ... @include(if: $renderingDetailedProfile) { - location - homepageUrl - } - } -} -``` - -Here's how the two built-in directives work: - -- `@skip(if: ...)` skips the selection if the `if: ...` value is truthy ({{ "GraphQL::Schema::Directive::Skip" | api_doc }}) -- `@include(if: ...)` includes the selection if the `if: ...` value is truthy ({{ "GraphQL::Schema::Directive::Include" | api_doc }}) - -### Custom Runtime Directives - -Custom directives extend {{ "GraphQL::Schema::Directive" | api_doc }}: - -```ruby -# app/graphql/directives/my_directive.rb -class Directives::MyDirective < GraphQL::Schema::Directive - description "A nice runtime customization" - location FIELD -end -``` - -Then, they're hooked up to the schema using `directive(...)`: - -```ruby -class MySchema < GraphQL::Schema - # Attach the custom directive to the schema - directive(Directives::MyDirective) -end -``` - -And you can reference them in the query with `@myDirective(...)`: - -```ruby -query { - field @myDirective { - id - } -} -``` - -{{ "GraphQL::Schema::Directive::Feature" | api_doc }} and {{ "GraphQL::Schema::Directive::Transform" | api_doc }} are included in the library as examples. - -### Runtime hooks - -Directive classes may implement the following class methods to interact with the runtime: - -- `def self.include?(obj, args, ctx)`: If this hook returns `false`, the nodes flagged by this directive will be skipped at runtime. -- `def self.resolve(obj, args, ctx)`: Wraps the resolution of flagged nodes. Resolution is passed as a __block__, so `yield` will continue resolution. - -Looking for a runtime hook that isn't listed here? {% open_an_issue "New directive hook: @something", " " %} to start the conversation! - -## Schema Directives - -Schema directives are used in GraphQL's interface definition language (IDL). For example, `@deprecated` is built in to GraphQL-Ruby: - -```ruby -type User { - firstName @deprecated(reason: "Use `name` instead") - lastName @deprecated(reason: "Use `name` instead") - name -} -``` - -In the schema definition, directives express metadata about types, fields, and arguments. - -### Custom Schema Directives - -To make a custom schema directive, extend {{ "GraphQL::Schema::Directive" | api_doc }}: - -```ruby -# app/graphql/directives/permission.rb -class Directives::Permission < GraphQL::Schema::Directive - argument :level, String - locations FIELD_DEFINITION, OBJECT -end -``` - -Then, attach it to parts of your schema with `directive(...)`: - -```ruby -class Types::JobPosting < Types::BaseObject - directive Directives::Permission, level: "manager" -end -``` - -Arguments and fields also accept a `directives:` keyword: - -```ruby -field :salary, Integer, null: false, - directives: { Directives::Permission => { level: "manager" } } -``` - -After that: - -- the configured object's `.directives` method will return an array containing an instance of the specified directive -- IDL dumps (from {{ "Schema.to_definition" | api_doc }}) will include the configured directives - -Similarly, {{ "Schema.from_definition" | api_doc }} parses directives from IDL strings. - -For a couple of built-in examples, check out: - -- {{ "GraphQL::Schema::Directive::Deprecated" | api_doc }} which implements `deprecation_reason` (via {{ "GraphQL::Schema::Member::HasDeprecationReason" | api_doc}}) -- {{ "GraphQL::Schema::Directive::Flagged" | api_doc }}, which is an example of using schema directives to implement {% internal_link "visibility", "/authorization/visibility" %} - -## Custom Name - -By default, the directive's name is taken from the class name. You can override this with `graphql_name`, for example: - -```ruby -class Directives::IsPrivate < GraphQL::Schema::Directive - graphql_name "someOtherName" -end -``` - -## Arguments - -Like fields, directives may have {% internal_link "arguments", "/fields/arguments" %} : - -```ruby -argument :if, Boolean, - description: "Skips the selection if this condition is true" -``` diff --git a/vendor/gems/graphql/guides/type_definitions/enums.md b/vendor/gems/graphql/guides/type_definitions/enums.md deleted file mode 100644 index ccf440b52e0..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/enums.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Enums -desc: Enums are sets of discrete values -index: 2 ---- - -Enum types are sets of discrete values. An enum field must return one of the possible values of the enum. In the [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL), enums are described like this: - -```ruby -enum MediaCategory { - AUDIO - IMAGE - TEXT - VIDEO -} -``` - -So, a `MediaCategory` value is one of: `AUDIO`, `IMAGE`, `TEXT`, or `VIDEO`. This is similar to [ActiveRecord enums](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html). - -In a GraphQL query, enums are written as identifiers (not strings), for example: - -```ruby -search(term: "puppies", mediaType: IMAGE) { ... } -``` - -(Notice that `IMAGE` doesn't have quotes.) - -But, when GraphQL responses or variables are transported using JSON, enum values are expressed as strings, for example: - -```ruby -# in a graphql controller: -params["variables"] -# { "mediaType" => "IMAGE" } -``` - -## Defining Enum Types - -In your application, enums extend {{ "GraphQL::Schema::Enum" | api_doc }} and define values with the `value(...)` method: - -```ruby -# First, a base class -# app/graphql/types/base_enum.rb -class Types::BaseEnum < GraphQL::Schema::Enum -end - -# app/graphql/types/media_category.rb -class Types::MediaCategory < Types::BaseEnum - value "AUDIO", "An audio file, such as music or spoken word" - value "IMAGE", "A still image, such as a photo or graphic" - value "TEXT", "Written words" - value "VIDEO", "Motion picture, may have audio" -end -``` - -Each value may have: - -- A description (as the second argument or `description:` keyword) -- A comment (as a `comment:` keyword) -- A deprecation reason (as `deprecation_reason:`), marking this value as deprecated -- A corresponding Ruby value (as `value:`), see below - -By default, Ruby strings correspond to GraphQL enum values. But, you can provide `value:` options to specify a different mapping. For example, if you use symbols instead of strings, you can say: - -```ruby -value "AUDIO", value: :audio -``` - -Then, GraphQL inputs of `AUDIO` will be converted to `:audio` and Ruby values of `:audio` will be converted to `"AUDIO"` in GraphQL responses. - -Enum classes are never instantiated and their methods are never called. - -You can get the GraphQL name of the enum value using the method matching its downcased name: - -```ruby -Types::MediaCategory.audio # => "AUDIO" -``` - -You can pass a `value_method:` to override the value of the generated method: - -```ruby -value "AUDIO", value: :audio, value_method: :lo_fi_audio - -# ... - -Types::MediaCategory.lo_fi_audio # => "AUDIO" -``` - -Also, you can completely skip the method generation by setting `value_method` to `false` - -```ruby -value "AUDIO", value: :audio, value_method: false -``` diff --git a/vendor/gems/graphql/guides/type_definitions/extensions.md b/vendor/gems/graphql/guides/type_definitions/extensions.md deleted file mode 100644 index 214570d734b..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/extensions.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Extending the GraphQL-Ruby Type Definition System -desc: Adding metadata and custom helpers to the DSL -index: 8 -redirect_from: - - /schema/extending_the_dsl/ ---- - -While integrating GraphQL into your app, you can customize the definition DSL. For example, you might: - -- Assign "area of responsibility" to different types and fields -- DRY up shared logic between types and fields -- Attach metadata for use during authorization - -This guide describes various options for extending the class-based definition API. Keep in mind that these approaches may change as the API matures. If you're having trouble, consider opening an issue on GitHub to get help. - -**Note**: This document describes best practice with GraphQL-Ruby 1.10+. For customizing schemas on older versions, use GitHub to browse older versions of this page. - -## Customization Overview - -In general, the schema definition process goes like this: - -- The application defines lots of classes for the GraphQL types -- Starting from root types (`query`, `mutation`, and `subscription`) and any defined `orphan_types`, the schema discovers all types, fields, arguments, enum values, and directives in the schema -- Non-type objects (fields, arguments, enum values) are initialized when they're attached to the classes or instances they belong to. - -## Customizing type definitions - -In your custom classes, you can add class-level instance variables that hold configuration. For example: - -```ruby -class Types::BaseObject < GraphQL::Schema::Object - # Call this method in an Object class to get or set the permission level: - def self.required_permission(permission_level = nil) - if permission_level.nil? - # return the configured value - @required_permission - else - @required_permission = permission_level - end - end -end - -# Then, in concrete classes -class Dossier < BaseObject - # The Dossier object type will have `.metadata[:required_permission] # => :admin` - required_permission :admin -end - -# Now, the type responds to that method: -Dossier.required_permission -# => :admin -``` - -Now, any runtime code which calls `type.required_permission` will get the configured value. - -### Customizing fields - -Fields are generated in a different way. Instead of using classes, they are generated with instances of `GraphQL::Schema::Field` (or a subclass). In short, the definition process works like this: - -```ruby -# This is what happens under the hood, roughly: -# In an object class: -field :name, String, null: false -# ... -# Leads to: -field_config = GraphQL::Schema::Field.new(name: :name, type: String, null: false) -``` - -So, you can customize this process by: - -- creating a custom class which extends `GraphQL::Schema::Field` -- overriding `#initialize` on that class (instance methods) -- registering that class as the `field_class` on Objects and Interfaces which should use the customized field. - -For example, you can create a custom class which accepts a new parameter to `initialize`: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - # Override #initialize to take a new argument: - def initialize(*args, required_permission: nil, **kwargs, &block) - @required_permission = required_permission - # Pass on the default args: - super(*args, **kwargs, &block) - end - - attr_reader :required_permission -end -``` - -Then, pass the field class as `field_class(...)` wherever it should be used: - -```ruby -class Types::BaseObject < GraphQL::Schema::Object - # Use this class for defining fields - field_class BaseField -end - -# And.... -class Types::BaseInterface < GraphQL::Schema::Interface - field_class BaseField -end - -class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation - field_class BaseField -end -``` - -Now, `BaseField.new(*args, &block)` will be used to create `GraphQL::Schema::Field`s on those types. At runtime `field.required_permission` will return the configured value. - -### Customizing Connections - -Connections may be customized in a similar way to Fields. - -- Create a new class extending 'GraphQL::Types::Relay::BaseConnection' -- Assign it to your object/interface type with `connection_type_class(MyCustomConnection)` - -For example, you can create a custom connection: - -```ruby -class Types::MyCustomConnection < GraphQL::Types::Relay::BaseConnection - # BaseConnection has these nullable configurations - # and the nodes field by default, but you can change - # these options if you want - edges_nullable(true) - edge_nullable(true) - node_nullable(true) - has_nodes_field(true) - - field :total_count, Integer, null: false - - def total_count - object.items.size - end -end -``` - -Then, pass the field class as `connection_type_class(...)` wherever it should be used: - -```ruby -module Types - class Types::BaseObject < GraphQL::Schema::Object - # Use this class for defining connections - connection_type_class MyCustomConnection - end -end -``` - -Now, all type classes that extend `BaseObject` will have a connection_type with the additional field `totalCount`. - -### Customizing Edges - -Edges may be customized in a similar way to Connections. - -- Create a new class extending 'GraphQL::Types::Relay::BaseEdge' -- Assign it to your object/interface type with `edge_type_class(MyCustomEdge)` - -### Customizing Arguments - -Arguments may be customized in a similar way to Fields. - -- Create a new class extending `GraphQL::Schema::Argument` -- Use `argument_class(MyArgClass)` to assign it to your base field class, base resolver class, and base mutation class - -Then, in your custom argument class, you can use `#initialize(name, type, desc = nil, **kwargs)` to take input from the DSL. - -### Customizing Enum Values - -Enum values may be customized in a similar way to Fields. - -- Create a new class extending `GraphQL::Schema::EnumValue` -- Assign it to your base `Enum` class with `enum_value_class(MyEnumValueClass)` - -Then, in your custom enum class, you can use `#initialize(name, desc = nil, **kwargs)` to take input from the DSL. diff --git a/vendor/gems/graphql/guides/type_definitions/field_extensions.md b/vendor/gems/graphql/guides/type_definitions/field_extensions.md deleted file mode 100644 index 45a06faba74..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/field_extensions.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Field Extensions -desc: Programmatically modify field configuration and resolution -index: 10 ---- - -{{ "GraphQL::Schema::FieldExtension" | api_doc }} provides a way to modify user-defined fields in a programmatic way. For example, Relay connections are implemented as a field extension ({{ "GraphQL::Schema::Field::ConnectionExtension" | api_doc }}). - -## Making a new extension - -Field extensions are subclasses of {{ "GraphQL::Schema::FieldExtension" | api_doc }}: - -```ruby -class MyExtension < GraphQL::Schema::FieldExtension -end -``` - -## Using an extension - -Defined extensions can be added to fields using the `extensions: [...]` option or the `extension(...)` method: - -```ruby -field :name, String, null: false, extensions: [UpcaseExtension] -# or: -field :description, String, null: false do - extension(UpcaseExtension) -end -``` - -See below for how extensions may modify fields. - -## Modifying field configuration - -When extensions are attached, they are initialized with a `field:` and `options:`. Then, `#apply` is called, when they may extend the field they're attached to. For example: - -```ruby -class SearchableExtension < GraphQL::Schema::FieldExtension - def apply - # add an argument to this field: - field.argument(:query, String, required: false, description: "A search query") - end -end -``` - -This way, an extension can encapsulate a behavior requiring several configuration options. - -## Adding default argument configurations - -Extensions may provide _default_ argument configurations which are applied if the field doesn't define the argument for itself. The configuration is passed to {{ "Schema::FieldExtension.default_argument" | api_doc }}. For example, to define a `:query` argument if the field doesn't already have one: - -```ruby -class SearchableExtension < GraphQL::Schema::FieldExtension - # Any field which uses this extension and _doesn't_ define - # its own `:query` argument will get an argument configured with this: - default_argument(:query, String, required: false, description: "A search query") -end -``` - -Additionally, extensions may implement `def after_define` which is called _after_ the field's `do .. . end` block. This is helpful when an extension should provide _default_ configurations without overriding anything in the field definition. (When extensions are added by calling `field.extension(...)` on an already-defined field `def after_define` is called immediately.) - -## Modifying field execution - -Extensions have two hooks that wrap field resolution. Since GraphQL-Ruby supports deferred execution, these hooks _might not_ be called back-to-back. - -First, {{ "GraphQL::Schema::FieldExtension#resolve" | api_doc }} is called. `resolve` should `yield(object, arguments)` to continue execution. If it doesn't `yield`, then the underlying field won't be called. Whatever `#resolve` returns will be used for continuing execution. - -After resolution and _after_ syncing lazy values (like `Promise`s from `graphql-batch`), {{ "GraphQL::Schema::FieldExtension#after_resolve" | api_doc }} is called. Whatever that method returns will be used as the field's return value. - -See the linked API docs for the parameters of those methods. - -### Execution "memo" - -One parameter to `after_resolve` deserves special attention: `memo:`. `resolve` _may_ yield a third value. For example: - -```ruby -def resolve(object:, arguments:, **rest) - # yield the current time as `memo` - yield(object, arguments, Time.now.to_i) -end -``` - -If a third value is yielded, it will be passed to `after_resolve` as `memo:`, for example: - -```ruby -def after_resolve(value:, memo:, **rest) - puts "Elapsed: #{Time.now.to_i - memo}" - # Return the original value - value -end -``` - -This allows the `resolve` hook to pass data to `after_resolve`. - -Instance variables may not be used because, in a given GraphQL query, the same field may be resolved several times concurrently, and that would result in overriding the instance variable in an unpredictable way. (In fact, extensions are frozen to prevent instance variable writes.) - -## Extension options - -The `extension(...)` method takes an optional second argument, for example: - -```ruby -extension(LimitExtension, limit: 20) -``` - -In this case, `{limit: 20}` will be passed as `options:` to `#initialize` and `options[:limit]` will be `20`. - -For example, options can be used for modifying execution: - -```ruby -def after_resolve(value:, **rest) - # Apply the limit from the options, a readable attribute on the class - value.limit(options[:limit]) -end -``` - -If you use the `extensions: [...]` option, you can pass options using a hash: - -```ruby -field :name, String, null: false, extensions: [LimitExtension => { limit: 20 }] -``` - -## Using `extras` - -Extensions can have the same `extras` as fields (see {% internal_link "Extra Field Metadata", "fields/introduction#extra-field-metadata" %}). Add them by calling `extras` in the class definition: - -```ruby -class MyExtension < GraphQL::Schema::FieldExtension - extras [:ast_node, :errors, ...] -end -``` - -Any configured `extras` will be present in the given `arguments`, but removed before the field is resolved. (However, `extras` from _any_ extension will be present in `arguments` for _all_ extensions.) - -## Adding an extension by default - -If you want to apply an extension to _all_ your fields, you can do this in your {% internal_link "BaseField", "/type_definitions/extensions.html#customizing-fields" %}'s `def initialize`, for example: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - def initialize(*args, **kwargs, &block) - super - # Add this to all fields based on this class: - extension(MyDefaultExtension) - end -end -``` - -You can also _conditionally_ apply extensions in `def initialize` by adding keywords to the method definition, for example: - -```ruby -class Types::BaseField < GraphQL::Schema::Field - # @param custom_extension [Boolean] if false, `MyCustomExtension` won't be added - # @example skipping `MyCustomExtension` - # field :no_extension, String, custom_extension: false - def initialize(*args, custom_extension: true, **kwargs, &block) - super(*args, **kwargs, &block) - # Don't apply this extension if the field is configured with `custom_extension: false`: - if custom_extension - extension(MyCustomExtensions) - end - end -end -``` diff --git a/vendor/gems/graphql/guides/type_definitions/input_objects.md b/vendor/gems/graphql/guides/type_definitions/input_objects.md deleted file mode 100644 index 82b0c3128b4..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/input_objects.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Input Objects -desc: Input objects are sets of key-value pairs which can be used as field arguments. -index: 3 ---- - -Input object types are complex inputs for GraphQL operations. They're great for fields that need a lot of structured input, like mutations or search fields. In a GraphQL request, it might look like this: - -```ruby -mutation { - createPost(attributes: { title: "Hello World", fullText: "This is my first post", categories: [GENERAL] }) { - # ^ Here is the input object ..................................................... ^ - } -} -``` - -Like a Ruby `Hash`, an input object consists of keys and values. Unlike a Hash, its keys and value types must be defined statically, as part of the GraphQL system. For example, here's an input object, expressed in the [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL): - -```ruby -input PostAttributes { - title: String! - fullText: String! - categories: [PostCategory!] -} -``` - -This input object has three possible keys: - -- `title` is required (denoted by `!`), and must be a `String` -- `fullText` is also a required String -- `categories` is optional (it doesn't have `!`), and if present, it must be a list of `PostCategory` values. - -## Defining Input Object Types - -Input object types extend {{ "GraphQL::Schema::InputObject" | api_doc }} and define key-value pairs with the `argument(...)` method. For example: - -```ruby -# app/graphql/types/base_input_object.rb -# Add a base class -class Types::BaseInputObject < GraphQL::Schema::InputObject -end - -class Types::PostAttributes < Types::BaseInputObject - description "Attributes for creating or updating a blog post" - argument :title, String, "Header for the post" - argument :full_text, String, "Full body of the post" - argument :categories, [Types::PostCategory], required: false -end -``` - -For a full description of the `argument(...)` method, see the {% internal_link "argument section of the Objects guide","/fields/arguments.html" %}. - -## Using Input Objects - -Input objects are passed to field methods as an instance of their definition class. So, inside the field method, you can access any key of the object by: - -- calling its method, corresponding to the name (underscore-cased) -- calling `#[]` with the _camel-cased_ name of the argument (this is for compatibility with previous GraphQL-Ruby versions) - -```ruby -class Types::MutationType < GraphQL::Schema::Object - # This field takes an argument called `attributes` - # which will be an instance of `PostAttributes` - field :create_post, Types::Post, null: false do - argument :attributes, Types::PostAttributes - end - - def create_post(attributes:) - puts attributes.class.name - # => "Types::PostAttributes" - # Access a value by method (underscore-cased): - puts attributes.full_text - # => "This is my first post" - # Or by hash-style lookup (camel-cased, for compatibility): - puts attributes[:fullText] - # => "This is my first post" - end -end -``` - -## Customizing Input Objects - -You can customize the `GraphQL::Schema::Argument` class which is used for input objects: - -```ruby -class Types::BaseArgument < GraphQL::Schema::Argument - # your customization here ... -end - - -class Types::BaseInputObject < GraphQL::Schema::InputObject - # Hook up the customized argument class - argument_class(Types::BaseArgument) -end -``` - - -You can also add or override methods on input object classes to customize them. They have two instance variables by default: - -- `@arguments`: A {{ "GraphQL::Execution::Interpreter::Arguments" | api_doc }} instance -- `@context`: The current {{ "GraphQL::Query::Context" | api_doc }} - -Any extra methods you define on the class can be used for field resolution, as demonstrated above. - -## Converting to Other Ruby Objects - -Your input objects can be automatically converted to other Ruby types before they're passed to your application code. This is an easy way to use `Range`'s in your schema: - -```ruby -class Types::DateRangeInput < Types::BaseInputObject - description "Range of dates" - argument :min, Types::Date, "Minimum value of the range" - argument :max, Types::Date, "Maximum value of the range" - - def prepare - min..max - end -end - -class Types::CalendarType < Types::BaseObject - field :appointments, [Types::Appointment], "Appointments on your calendar", null: false do - argument :during, Types::DateRangeInput, "Only show appointments within this range" - end - - def appointments(during:) - # during will be an instance of Range - object.appointments.select { |appointment| during.cover?(appointment.date) } - end -end -``` - -## `@oneOf` - -You can make input objects that require _exactly one_ field to be provided using `one_of`: - -```ruby -class FindUserInput < Types::BaseInput - one_of - # Either `{ id: ... }` or `{ username: ... }` may be given, - # but not both -- and one of them _must_ be given. - argument :id, ID, required: false - argument :username, String, required: false -end -``` - -An input object with `one_of` will require exactly one given argument and it will require that the given argument's value is not `nil`. With `one_of`, arguments must have `required: false`, since any _individual_ argument is not required. - -When you use `one_of`, it will appear in schema print-outs with `input ... @oneOf` and you can query it using `{ __type(name: $typename) { isOneOf } }`. - -This behavior is described in a [proposed change](https://github.com/graphql/graphql-spec/pull/825) to the GraphQL specification. diff --git a/vendor/gems/graphql/guides/type_definitions/interfaces.md b/vendor/gems/graphql/guides/type_definitions/interfaces.md deleted file mode 100644 index 254cfd18bf6..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/interfaces.md +++ /dev/null @@ -1,271 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Interfaces -desc: Interfaces are lists of fields which objects may implement -index: 4 -redirect_from: - - /types/abstract_types/ ---- - -Interfaces are lists of fields which may be implemented by object types. - -An interface has fields, but it's never actually instantiated. Instead, objects may _implement_ interfaces, which makes them a _member_ of that interface. Also, fields may _return_ interface types. When this happens, the returned object may be any member of that interface. - -For example, let's say a `Customer` (interface) may be either an `Individual` (object) or a `Company` (object). Here's the structure in the [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL): - -```graphql -interface Customer { - name: String! - outstandingBalance: Int! -} - -type Company implements Customer { - employees: [Individual!]! - name: String! - outstandingBalance: Int! -} - -type Individual implements Customer { - company: Company - name: String! - outstandingBalance: Int! -} -``` - -Notice that the `Customer` interface requires two fields, `name: String!` and `outstandingBalance: Int!`. Both `Company` and `Individual` implement those fields, so they can implement `Customer`. Their implementation of `Customer` is made explicit by `implements Customer` in their definition. - -When querying, you can get the fields on an interface: - -```graphql -{ - customers(first: 5) { - name - outstandingBalance - } -} -``` - -Whether the objects are `Company` or `Individual`, it doesn't matter -- you still get their `name` and `outstandingBalance`. If you want some object-specific fields, you can query them with an _inline fragment_, for example: - -```graphql -{ - customers(first: 5) { - name - ... on Individual { - company { name } - } - } -} -``` - -This means, "if the customer is an `Individual`, also get the customer's company name". - -Interfaces are a good choice whenever a set of objects are used interchangeably, and they have several significant fields in common. When they don't have fields in common, use a {% internal_link "Union", "/type_definitions/unions" %} instead. - -## Defining Interface Types - -Interfaces are Ruby modules which include {{ "GraphQL::Schema::Interface" | api_doc }}. First, make a base module: - -```ruby -module Types::BaseInterface - include GraphQL::Schema::Interface -end -``` - -Then, include that into each interface: - -```ruby -module Types::RetailItem - include Types::BaseInterface - comment "TODO comment in the RetailItem interface" - description "Something that can be bought" - field :price, Types::Price, "How much this item costs", null: false - - def price - # Optional: provide a special implementation of `price` here - end - - - # Optional, see below - definition_methods do - # Optional: if this method is defined, it overrides `Schema.resolve_type` - def resolve_type(object, context) - # ... - end - end -end -``` - -Interface classes are never instantiated. At runtime, only their `.resolve_type` methods are called (if they're defined). - -### Implementing Interfaces - -To define object types that implement this interface use the `implements` method: - -```ruby -class Types::Car < Types::BaseObject - implements Types::RetailItem - - # ... additional fields -end - -class Types::Purse < Types::BaseObject - implements Types::RetailItem - - # ... additional fields -end -``` - -Those object types will _inherit_ field definitions from those interfaces. - -If you add an object type which implements an interface, but that object type doesn't appear in your schema as a field return type, a union member, or a root type, then you need to add that object to the interfaces's `orphan_types`. - -### Implementing Fields - -Interfaces may provide field implementations along with the signatures. For example: - -```ruby -field :price, Types::Price, "How much this item costs", null: false - -# Implement this field to return a `::Price` object -def price - ::Price.from_cents(@object.price_in_cents) -end -``` - -This method will be called by objects who implement the interface. To override this implementation, -object classes can override the `#price` method. - -Read more in the {% internal_link "Fields guide", "/fields/introduction" %}. - -### Definition Methods - -You can use `definition_methods do ... end` to add helper methods to interface modules. By adding methods to `definition_methods`: - -- Those methods will be available as class methods in the interface itself -- These class methods will _also_ be added to interfaces that `include` this interface. - -This way, class methods are inherited when interfaces `include` other interfaces. (`definition_methods` is like `ActiveSupport::Concern`'s `class_methods` in this regard, but it has a different name to avoid naming conflicts). - -For example, you can add definition helpers to your base interface, then use them in concrete interfaces later: - -```ruby -# First, add a helper method to `BaseInterface`'s definition methods -module Types::BaseInterface - include GraphQL::Schema::Interface - - definition_methods do - # Use this to add a price field + default implementation - def price_field - field(:price, ::Types::Price, null: false) - define_method(:price) do - ::Price.from_cents(@object.price_in_cents) - end - end - end -end - -# Then call it later -module Types::ForSale - include Types::BaseInterface - # This calls `price_field` from definition methods - price_field -end -``` - -The type definition DSL uses this mechanism, too, so you can override those methods here also. - -Note: Under the hood, `definition_methods` causes a module to be `extend`ed by the interface. Any calls to `extend` or `implement` may override methods from `definition_methods`. - -### Resolve Type - -When a field's return type is an interface, GraphQL has to figure out what _specific_ object type to use for the return value. In the example above, each `customer` must be categorized as an `Individual` or `Company`. You can do this by: - -- Providing a top-level `Schema.resolve_type` method; _OR_ -- Providing an interface-level `.resolve_type` method in `definition_methods`. - -This method will be called whenever an object must be disambiguated. For example: - -```ruby -module Types::RetailItem - include Types::BaseInterface - definition_methods do - # Determine what object type to use for `object` - def resolve_type(object, context) - if object.is_a?(::Car) || object.is_a?(::Truck) - Types::Car - elsif object.is_a?(::Purse) - Types::Purse - else - raise "Unexpected RetailItem: #{object.inspect}" - end - end - end -end -``` - -You can also optionally return a "resolved" object in addition the resolved type by returning an array: - -```ruby -module Types::Claim - include Types::BaseInterface - definition_methods do - def resolve_type(object, context) - type = case object.value - when Success - Types::Approved - when Error - Types::Rejected - else - raise "Unexpected Claim: #{object.inspect}" - end - - [type, object.value] - end - end -end -``` - -The returned array must be a tuple of `[Type, object]`. -This is useful for interface or union types which are backed by a domain object which should be unwrapped before resolving the next field. - -## Orphan Types - -If you add an object type which implements an interface, but that object type doesn't properly appear in your schema, then you need to add that object to the interfaces's `orphan_types`, for example: - -```ruby -module Types::RetailItem - include Types::BaseInterface - # ... - orphan_types Types::Car -end -``` - -Alternatively you can add the object types to the schema's `orphan_types`: - -```ruby -class MySchema < GraphQL::Schema - orphan_types Types::Car -end -``` - -This is required because a schema finds it types by traversing its fields, starting with `query`, `mutation` and `subscription`. If an object is _never_ the return type of a field, but only connected via an interface, then it must be explicitly connected to the schema via `orphan_types`. For example, given this schema: - -```graphql -type Query { - node(id: ID!): Node -} - -interface Node { - id: ID! -} - -type Comment implements Node { - id: ID! -} -``` - -`Comment` must be added via `orphan_types` since it's never used as the return type of a field. (Only `Node` and `ID` are used as return types.) diff --git a/vendor/gems/graphql/guides/type_definitions/lists.md b/vendor/gems/graphql/guides/type_definitions/lists.md deleted file mode 100644 index c6e6519665c..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/lists.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Lists -desc: Ordered lists containing other types -index: 6 ---- - -GraphQL has _list types_ which are ordered lists containing items of other types. The following examples use the [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL). - -Fields may return a single scalar value (eg `String`), or a _list_ of scalar values (eg, `[String]`, a list of strings): - -```ruby -type Spy { - # This spy's real name - realName: String! - # Any other names that this spy goes by - aliases: [String!] -} -``` - -Fields may also return lists of other types as well: - -```ruby -enum PostCategory { - SOFTWARE - UPHOLSTERY - MAGIC_THE_GATHERING -} - -type BlogPost { - # Zero or more categories this post belongs to - categories: [PostCategory!] - # Other posts related to this one - relatedPosts: [BlogPost!] -} -``` - -Inputs may also be lists. Arguments can accept list types, for example: - -```ruby -type Query { - # Return the latest posts, filtered by `categories` - posts(categories: [PostCategory!]): [BlogPost!] -} -``` - -When GraphQL is sent and received with JSON, GraphQL lists are expressed in JSON arrays. - -## List Types in Ruby - -To define a list type in Ruby use `[...]` (a Ruby array with one member, the inner type). For example: - -```ruby -# A field returning a list type: -# Equivalent to `aliases: [String!]` above -field :aliases, [String] - -# An argument which accepts a list type: -argument :categories, [Types::PostCategory], required: false -``` - -For input, GraphQL lists are converted to Ruby arrays. - -For fields that return list types, any object responding to `#each` may be returned. It will be enumerated as a GraphQL list. - -To define lists where `nil` may be a member of the list, use `null: true` in the definition array, for example: - -```ruby -# Equivalent to `previousEmployers: [Employer]!` -field :previous_employers, [Types::Employer, null: true], "Previous employers; `null` represents a period of self-employment or unemployment" null: false -``` - -## Lists, Nullable Lists, and Lists of Nulls - -Combining list types and non-null types can be a bit tricky. There are four possible combinations, based on two parameters: - -- Nullability of the field: can this field return `null`, or does it always return a list? -- Nullability of the list items: when a list is present, may it include `null`? - -Here's how those combinations play out: - -   | nullable field | non-null field - ------|------|------ -nullable items | [Integer, null: true], null: true
    # => [Int] | [Integer, null: true], null: false
    # => [Int]! -non-null items | [Integer]
    # => [Int!] | [Integer], null: false
    # => [Int!]! - -(The first line is GraphQL-Ruby code. The second line, beginning with `# =>`, is the corresponding GraphQL SDL code.) - - -Let's look at some examples. - -#### Non-null lists with non-null items - -Here's an example field: - -```ruby -field :scores, [Integer], null: false -# In GraphQL, -# scores: [Int!]! -``` - -In this example, `scores` may not return `null`. It must _always_ return a list. Additionally, the list may _never_ contain `null` -- it may only contain `Int`s. (It may be empty, but it cannot have `null` in it.) - -Here are values the field may return: - -| Valid | Invalid | -| ------ | ------ | -| `[]` | `null` | -| `[1, 2, ...]` | `[null]` | -| | `[1, null, 2, ...]` | - -### Non-null lists with nullable items - -Here's an example field: - -```ruby -field :scores, [Integer, null: true], null: false -# In GraphQL, -# scores: [Int]! -``` - -In this example, `scores` may not return `null`. It must _always_ return a list. However, the list _may_ contain `null`s and/or `Int`s. - -Here are values the field may return: - -Valid | Invalid -------|------ -`[]` | `null` -`[1, 2, ...]`| -`[null]` | - `[1, null, 2, ...]` | - -### Nullable lists with nullable items - -Here's an example field: - -```ruby -field :scores, [Integer, null: true] -# In GraphQL, -# scores: [Int] -``` - -In this example, `scores` return `null` _or_ a list. Additionally, the list _may_ contain `null`s and/or `Int`s. - -Here are values the field may return: - -Valid | Invalid -------|------ -`null` | -`[]` | -`[1, 2, ...]`| -`[null]` | - `[1, null, 2, ...]` | - - -### Nullable lists with non-null items - -Here's an example field: - -```ruby -field :scores, [Integer] -# In GraphQL, -# scores: [Int!] -``` - -In this example, `scores` return `null` _or_ a list. However, if a list is present, it may _not_ contain `null` -- only `Int`s. - -Here are values the field may return: - -Valid | Invalid -------|------ -`null` | `[null]` -`[]` | `[1, null, 2, ...]` -`[1, 2, ...]` | diff --git a/vendor/gems/graphql/guides/type_definitions/non_nulls.md b/vendor/gems/graphql/guides/type_definitions/non_nulls.md deleted file mode 100644 index 2c18c6b3ae6..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/non_nulls.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Non-Null Types -desc: Values which must be present -index: 7 ---- - -GraphQL's concept of _non-null_ is expressed in the [Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL) with `!`, for example: - -```graphql -type User { - # This field _always_ returns a String, never returns `null` - handle: String! - # `since:` _must_ be passed a `DateTime` value, it can never be omitted or passed `null` - followers(since: DateTime!): [User!]! -} -``` - -In Ruby, this concept is expressed with `null:` for fields and `required:` for arguments. - -## Non-null return types - -When `!` is used for field return types (like `handle: String!` above), it means that the field will _never_ (and may never) return `nil`. - -To make a field non-null in Ruby, use `null: false` in the field definition: - -```ruby -# equivalent to `handle: String!` above -field :handle, String, null: false -``` - -This means that the field will _never_ be `nil` (and if it is, it will be removed from the response, as described below). - -### Non-null error propagation - - If a non-null field ever returns `nil`, then the entire selection will be removed from the response and replaced with `nil`. If this removal would result in _another_ invalid `nil`, then it cascades upward, until it reaches the root `"data"` key. This is to support clients in strongly-typed languages. Any non-null field will _never_ return `null`, and client developers can depend on that. - -## Non-null argument types - -When `!` is used for arguments (like `followers(since: DateTime!)` above), it means that the argument is _required_ for the query to execute. Any query which doesn't have a value for that argument will be rejected immediately. - -Arguments are non-null by default. You can use `required: false` to mark arguments as optional: - -```ruby -# This will be `since: DateTime` instead of `since: DateTime!` -argument :since, Types::DateTime, required: false -``` - -Without `required: false`, any query _without_ a value for `since:` will be rejected. diff --git a/vendor/gems/graphql/guides/type_definitions/objects.md b/vendor/gems/graphql/guides/type_definitions/objects.md deleted file mode 100644 index 1718c2810b4..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/objects.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Objects -desc: Objects expose data and link to other objects -index: 0 ---- - -GraphQL object types are the bread and butter of GraphQL APIs. Each object has _fields_ which expose data and may be queried by name. For example, we can query a `User` like this: - -```ruby -user { - handle - email -} -``` - -And get back values like this: - -```ruby -{ - "user" => { - "handle" => "rmosolgo", - "email" => nil, - } -} -``` - -Generally speaking, GraphQL object types correspond to models in your application, like `User`, `Product`, or `Comment`. Sometimes, object types are described using the [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL): - -```ruby -type User { - email: String - handle: String! - friends: [User!]! -} -``` - -This means that `User` objects have three fields: - -- `email`, which may return a `String` _or_ `nil`. -- `handle`, which returns a `String` but _never_ `nil` (`!` means the field never returns `nil`) -- `friends`, which returns a list of other `User`s (`[...]` means the field returns a list of values; `User!` means the list contains `User` objects, and never contains `nil`.) - -The same object can be defined using Ruby: - -```ruby -class Types::User < GraphQL::Schema::Object - field :email, String - field :handle, String, null: false - field :friends, [User], null: false -end -``` - -The rest of this guide will describe how to define GraphQL object types in Ruby. To learn more about GraphQL object types in general, see the [GraphQL docs](https://graphql.org/learn/schema/#object-types-and-fields). - -## Object classes - -Classes extending {{ "GraphQL::Schema::Object" | api_doc }} describe [Object types](https://graphql.org/learn/schema/#object-types-and-fields) and customize their behavior. - -Object fields can be created with the `field(...)` class method, [described in detail below](#fields) - -Field and argument names should be underscored as a convention. They will be converted to camelCase in the underlying GraphQL type and be camelCase in the schema itself. - -```ruby -# first, somewhere, a base class: -class Types::BaseObject < GraphQL::Schema::Object -end - -# then... -class Types::TodoList < Types::BaseObject - comment "Comment of the TodoList type" - description "A list of items which may be completed" - - field :name, String, "The unique name of this list", null: false - field :is_completed, String, "Completed status depending on all tasks being done.", null: false - # Related Object: - field :owner, Types::User, "The creator of this list", null: false - # List field: - field :viewers, [Types::User], "Users who can see this list", null: false - # Connection: - field :items, Types::TodoItem.connection_type, "Tasks on this list", null: false do - argument :status, TodoStatus, "Restrict items to this status", required: false - end -end -``` - -## Fields - -Object fields expose data about that object or connect the object to other objects. You can add fields to your object types with the `field(...)` class method. - -See the {% internal_link "Fields guide", "/fields/introduction" %} for details about object fields. - -## Implementing interfaces - -If an object implements any interfaces, they can be added with `implements`, for example: - -```ruby -# This object implements some interfaces: -implements GraphQL::Types::Relay::Node -implements Types::UserAssignableType -``` - -When an object `implements` interfaces, it: - -- inherits the GraphQL field definitions from that object -- includes that module into the object definition - -Read more about interfaces in the {% internal_link "Interfaces guide", "/type_definitions/interfaces" %} diff --git a/vendor/gems/graphql/guides/type_definitions/scalars.md b/vendor/gems/graphql/guides/type_definitions/scalars.md deleted file mode 100644 index 209ef1cead5..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/scalars.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Scalars -desc: Scalars are "simple" data types like integers and strings -index: 1 ---- - -Scalars are "leaf" values in GraphQL. There are several built-in scalars, and you can define custom scalars, too. ({% internal_link "Enums", "/type_definitions/enums" %} are also leaf values.) The built-in scalars are: - -- `String`, like a JSON or Ruby string -- `Int`, like a JSON or Ruby integer -- `Float`, like a JSON or Ruby floating point decimal -- `Boolean`, like a JSON or Ruby boolean (`true` or `false`) -- `ID`, which a specialized `String` for representing unique object identifiers -- `ISO8601DateTime`, an ISO 8601-encoded datetime -- `ISO8601Date`, an ISO 8601-encoded date -- `ISO8601Duration`, an ISO 8601-encoded duration. ⚠ This requires `ActiveSupport::Duration` to be loaded and will raise {{ "GraphQL::Error" | api_doc }} if it's `.coerce_*` methods are called when it is not defined. -- `JSON`, ⚠ This returns arbitrary JSON (Ruby hashes, arrays, strings, integers, floats, booleans and nils). Take care: by using this type, you completely lose all GraphQL type safety. Consider building object types for your data instead. -- `BigInt`, a numeric value which may exceed the size of a 32-bit integer - -Fields can return built-in scalars by referencing them by name: - -```ruby -# String field: -field :name, String, -# Integer field: -field :top_score, Int, null: false -# or: -field :top_score, Integer, null: false -# Float field -field :avg_points_per_game, Float, null: false -# Boolean field -field :is_top_ranked, Boolean, null: false -# ID field -field :id, ID, null: false -# ISO8601DateTime field -field :created_at, GraphQL::Types::ISO8601DateTime, null: false -# ISO8601Date field -field :birthday, GraphQL::Types::ISO8601Date, null: false -# ISO8601Duration field -field :age, GraphQL::Types::ISO8601Duration, null: false -# JSON field ⚠ -field :parameters, GraphQL::Types::JSON, null: false -# BigInt field -field :sales, GraphQL::Types::BigInt, null: false -``` - -Custom scalars (see below) can also be used by name: - -```ruby -# `homepage: Url` -field :homepage, Types::Url -``` - -In the [Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL), scalars are simply named: - -```ruby -scalar DateTime -``` - -## Custom Scalars - -You can implement your own scalars by extending {{ "GraphQL::Schema::Scalar" | api_doc }}. For example: - -```ruby -# app/graphql/types/base_scalar.rb -# Make a base class: -class Types::BaseScalar < GraphQL::Schema::Scalar -end - -# app/graphql/types/url.rb -class Types::Url < Types::BaseScalar - comment "TODO comment of the scalar" - description "A valid URL, transported as a string" - - def self.coerce_input(input_value, context) - # Parse the incoming object into a `URI` - url = URI.parse(input_value) - if url.is_a?(URI::HTTP) || url.is_a?(URI::HTTPS) - # It's valid, return the URI object - url - else - raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid URL" - end - end - - def self.coerce_result(ruby_value, context) - # It's transported as a string, so stringify it - ruby_value.to_s - end -end -``` - -Your class must define two class methods: - -- `self.coerce_input` takes a GraphQL input and converts it into a Ruby value -- `self.coerce_result` takes the return value of a field and prepares it for the GraphQL response JSON - -When incoming data is incorrect, the method may raise {{ "GraphQL::CoercionError" | api_doc }}, which will be returned to the client in the `"errors"` key. - -Scalar classes are never initialized; only their `.coerce_*` methods are called at runtime. diff --git a/vendor/gems/graphql/guides/type_definitions/unions.md b/vendor/gems/graphql/guides/type_definitions/unions.md deleted file mode 100644 index 2c03a5b122a..00000000000 --- a/vendor/gems/graphql/guides/type_definitions/unions.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -layout: guide -doc_stub: false -search: true -section: Type Definitions -title: Unions -desc: Unions are sets of types which may appear in the same place (but don't share fields). -index: 5 ---- - -A union type is a set of object types which may appear in the same spot. Here's a union, expressed in [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#type-language) (SDL): - -```ruby -union MediaItem = AudioClip | VideoClip | Image | TextSnippet -``` - -This might be used on a search field, for example: - -```ruby -searchMedia(term: "puppies") { - ... on AudioClip { - duration - } - ... on VideoClip { - previewURL - resolution - } - ... on Image { - thumbnailURL - } - ... on TextSnippet { - teaserText - } -} -``` - -Here, the `searchMedia` field returns `[MediaItem!]`, a list where each member is part of the `MediaItem` union. So, for each member, we want to select different fields depending on which kind of object that member is. - -{% internal_link "Interfaces", "/type_definitions/interfaces" %} are a similar concept, but in an interface, all types must share some common fields. Unions are a good choice when the object types don't have any significant fields in common. - -Since union members share _no_ fields, selections are _always_ made with typed fragments (`... on SomeType`, as seen above). - -## Defining Union Types - - -Unions extend `GraphQL::Schema::Union`. First, make a base class: - -```ruby -class Types::BaseUnion < GraphQL::Schema::Union -end -``` - -Then, extend that one for each union in your schema: - -```ruby -class Types::CommentSubject < Types::BaseUnion - comment "TODO comment on the union" - description "Objects which may be commented on" - possible_types Types::Post, Types::Image - - # Optional: if this method is defined, it will override `Schema.resolve_type` - def self.resolve_type(object, context) - if object.is_a?(BlogPost) - Types::Post - else - Types::Image - end - end -end -``` - -The `possible_types(*types)` method accepts one or more types which belong to this union. - -Union classes are never instantiated; At runtime, only their `.resolve_type` methods may be called (if defined). - -For information about `.resolve_type`, see the {% internal_link "Interfaces guide", "/type_definitions/interfaces#resolve-type" %}. diff --git a/vendor/gems/graphql/javascript_client/CHANGELOG.md b/vendor/gems/graphql/javascript_client/CHANGELOG.md deleted file mode 100644 index 0b8969b02a7..00000000000 --- a/vendor/gems/graphql/javascript_client/CHANGELOG.md +++ /dev/null @@ -1,306 +0,0 @@ -# graphql-ruby-client - -# 1.14.5 (8 Nov 2024) - -- `sync`: Fix `--dump-payload` with `--outfile` #5152 - -# 1.14.4 (8 Nov 2024) - -- ActionCable: prevent unsubscribe being called twice with Relay and Urql #5150 - -# 1.14.3 (5 Nov 2024) - -- `createActionCableHandler`: Make sure `unsubscribe` is only called once #5109 - -# 1.14.2 (4 Nov 2024) - -- `sync`: Add a `--dump-payload` option for printing out the HTTP Post data #5143 - -# 1.14.1 (30 Sept 2024) - -- `AblyLink`: don't set up an Ably subscription when no Subscription header is present #5113 - -# 1.14.0 (3 Jul 2024) - -- Subscriptions: with Relay and ActionCable, don't send an empty query string (`""`) when using persisted operations #5008 - -# 1.13.3 (20 Mar 2024) - -- Subscriptions: Support `urql` + ActionCable #4886 - -# 1.13.2 (28 Feb 2024) - -- Update `glob` to v10+ to eliminate dependency on `inflight` #4859 - -# 1.13.1 (23 Feb 2024) - -- createAblyHandler: add typing for `onError` handler #4845 - -# 1.13.0 (23 Jan 2024) - -- Sync: add support for `generate-persisted-query-manifest` files #4798 -- createActionCableHandler: remove needless `perform("send", ...)` call #4793 - -# 1.12.1 (29 Dec 2023) - -- GraphiQL: support custom `channelName` and `url` in ActionCable fetcher #4756 - -# 1.12.0 (7 Dec 2023) - -- Add GraphiQL support for subscriptions #4724 - -# 1.11.10 (17 Nov 2023) - -- `createRelaySubscriptionHandler`: Support Relay persisted queries with ActionCable #4705 - -# 1.11.9 (1 Sept 2023) - -- `createRelaySubscriptionHandler`: fix error handling in handler functions #4603 - -# 1.11.8 (9 May 2023) - -- ActionCable: accept a custom `channelName` for `createActionCableHandler` and `addGraphQLSubscriptions` #4463 - -# 1.11.7 (24 February 2023) - -- ActionCableLink: fix race condition #4359 - -# 1.11.6 (14 February 2023) - -- Sync: fix `--changeset-version` #4328 -- Improve verbose logging #4328 - -# 1.11.5 (27 January 2023) - -- Sync: add a `--changeset-version` for use with Changesets #4304 -- Sync: fix handling of `--header` with a single header - -# 1.11.4 (4 January 2023) - -- PusherLink: pass initial response along to the client #4282 - -# 1.11.3 (13 October 2022) - -- `createAblySubscriptions`: don't use `Error.captureStackTrace` which isn't supported in all JS runtimes #4223 -- `createAblySubscriptions`: properly handle empty initial response from the interpreter (`{}`) #4226 - -# 1.11.2 (26 August 2022) - -- Sync: Add a `--header` option for custom headers #4171 - -# 1.11.1 (19 July 2022) - -- Subscriptions: ActionCableLink: only forward the result if `data` or `errors` is present #4114 - -# 1.11.0 (4 July 2022) - -- Subscriptions: Add `urql` support for Pusher #4129 - -# 1.10.7 (29 Mar 2022) - -- Dependencies: loosen apollo client and graphql version requirements to accept newer versions #4008 - -# 1.10.6 (10 Jan 2022) - -- Pusher Link: Don't pass along the `complete` handler because Apollo unsubscribes if you do #3830 - -# 1.10.5 (17 Dec 2021) - -- Dependencies: replace `actioncable` with `@rails/actioncable` #3773 - -# 1.10.4 (19 Nov 2021) - -- Sync: Also make sure documents are valid after removing `@client` fields #3715 - -# 1.10.3 (18 Nov 2021) - -- Sync: Remove any fields with `@client` before sending operations to the server #3712 - -# 1.10.2 (25 Oct 2021) - -- Pusher Link: Properly forward network errors to subscribers #3638 - -# 1.10.1 (22 Sept 2021) - -- Sync: Add `--apollo-codegen-json-output=...` option #3616 - -# 1.10.0 (25 Aug 2021) - -- Remove direct dependency on `request` #3594 -- Update `createRelaySubscriptionHandler` to support Relay 11. Use `createLegacyRelaySubscriptionHandler` to get the old behavior. #3594 - -# 1.9.3 (31 Mar 2021) - -- Move `graphql` and `@apollo/client` to `peerDeps` for more flexible versions #3395 - -# 1.9.2 (19 Feb 2021) - -- Remove dependency on React by changing imports to `@apollo/client/core` #3349 - -# 1.9.1 (11 Feb 2021) - -- Support graphql 15.x in dependencies #3334 - -## 1.9.0 (10 Feb 2021) - -- Move "compiled" `.js` files into the root directory of the published NPM package (instead of `dist/`). To upgrade, remove `dist/` from your import paths. (These files will be treated as public APIs in their own right, exposed independently to support smaller bundle sizes.) #2768 -- Upgrade dependency from `apollo-link` to `@apollo/client` #3270 - -## 1.8.2 (2 Feb 2021) - -- Pusher: Accept a `decompress:` function for handling compressed payloads #3311 - -## 1.8.1 (16 Nov 2020) - -- Sync: When `--url` is omitted, generate an outfile without syncing with a server - -## 1.8.0 (10 Nov 2020) - -- Ably: Support server-side `cipher_base:` config in the client - -## 1.7.12 (3 Nov 2020) - -- Ably: Add `rewind:` config so messages aren't lost between subscribe and registration of listener. #3210 -- Ably: Fix race condition where error was raised before the channel was available. #3210 - -## 1.7.11 (15 June 2020) - -- Ably: Improve channel state handling in case the initial subscription result contains errors #2993 - -## 1.7.10 (13 June 2020) - -- Ably: Improve error handling and channel cleanup #2991 - -## 1.7.9 (15 May 2020) - -- Ably: _completely_ unsubscribe when subscriptions are done #2944 -- Ably: propagate errors from subscriptions #2944 - -## 1.7.8 (1 May 2020) - -- `sync`: Add support for Apollo-Android's `OperationOutput.json` #2914 - -## 1.7.7 (15 Apr 2020) - -- Ably handler: dispatch initial response #2866 -- Ably handler: catch any error in initial HTTP call #2877 - -## 1.7.6 (3 Apr 2020) - -- Fix ActionCableLink sending unsubcribe to ActionCable #2842 - -## 1.7.5 (4 Mar 2020) - -- Add missing dependency declarations - -## 1.7.4 (18 Feb 2020) - -- Move all exports to top level -- Fix sync body handling: wait for all chunks, improve verbose output - -## 1.7.3 (17 Feb 2020) - -- Fix CLI for TypeScript - -## 1.7.2 (17 Feb 2020) - -- Convert outfile generators to TypeScript and include them in published package - -## 1.7.1 (17 Feb 2020) - -- Fix `bin` configuration in package.json - -## 1.7.0 (17 Feb 2020) - -- Rewrite in TypeScript - -## 1.6.8 (18 Sept 2019) - -- Properly send `Content-Type: application/json` when posting persisted operations - -## 1.6.7 (18 Sept 2019) - -- Add post data to `--verbose` output of `sync` - -## 1.6.6 (6 Aug 2019) - -- Add `--relay-persisted-output` for working with Relay Compiler's new `--persist-output` option #2415 - -## 1.6.5 (17 July 2019) - -- Update dependencies #2335 - -## 1.6.4 (11 May 2019) - -- Add `--verbose` option to `sync` #2075 -- Support Relay 2.0.0 #2121 -- ActionCableLink: support subscriber when there are errors but no data #2176 - -## 1.6.3 (11 Jan 2019) - -- Fix `.unsubscribe()` for PusherLink #2042 - -## 1.6.2 (14 Dec 2018) - -- Support identified Ably client #2003 - -## 1.6.1 (30 Nov 2018) - -- Support `ably:` option for Relay subscriptions - -## 1.6.0 (19 Nov 2018) - -- Fix unused requires #1943 -- Add `generateClient` function to generate code _without_ the HTTP call #1941 - -## 1.5.0 (27 October 2018) - -- Fix `export` usage in PusherLink, use `require` and `module.exports` instead #1889 -- Add `AblyLink` #1925 - -## 1.4.1 (19 Sept 2018) - -- Add `connectionOptions` to ActionCableLink #1857 - -## 1.4.0 (12 Apr 2018) - -- Add `PusherLink` for Apollo 2 Subscriptions on Pusher -- Add `OperationStoreLink` for Apollo 2 persisted queries - -## 1.3.0 (30 Nov 2017) - -- Support HTTPS, basic auth, query string and port in `sync` #1053 -- Add Apollo 2 support for ActionCable subscriptions #1120 -- Add `--outfile-type=json` for stored operation manifest #1142 - -## 1.2.0 (15 Nov 2017) - -- Support Apollo batching middleware #1092 - -## 1.1.3 (11 Oct 2017) - -- Fix Apollo + ActionCable unsubscribe function #1019 - -## 1.1.2 (9 Oct 2017) - -- Add channel IDs to ActionCable subscriptions #1004 - -## 1.1.1 (21 Sept 2017) - -- Add `--add-typename` option to `sync` #967 - -## 1.1.0 (18 Sept 2017) - -- Add subscription clients for Apollo and Relay Modern - -## 1.0.2 (22 Aug 2017) - -- Remove debug output - -## 1.0.1 (21 Aug 2017) - -- Rename from `graphql-pro-js` to `graphql-ruby-client` - -## 1.0.0 (31 Jul 2017) - -- Add `sync` task diff --git a/vendor/gems/graphql/javascript_client/LICENSE.md b/vendor/gems/graphql/javascript_client/LICENSE.md deleted file mode 100644 index 49e2d7a2d52..00000000000 --- a/vendor/gems/graphql/javascript_client/LICENSE.md +++ /dev/null @@ -1,10 +0,0 @@ -Copyright (c) Minimum Viable Software - -`graphql-ruby-client` is an Open Source project licensed under the terms of -the LGPLv3 license. Please see https://www.gnu.org/licenses/lgpl-3.0.html -for license text. - -`GraphQL::Pro` customers are granted a commercial-friendly license -allowing private forks and modifications of `graphql-ruby-client`. -Please see https://graphql.pro/ for more detail. You can find the -commercial license terms at https://graphql.pro/COMM-LICENSE.html. diff --git a/vendor/gems/graphql/javascript_client/jest.config.js b/vendor/gems/graphql/javascript_client/jest.config.js deleted file mode 100644 index c4a8c395165..00000000000 --- a/vendor/gems/graphql/javascript_client/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - roots: [ - "/src" - ], - verbose: true, - testMatch: [ - "**/__tests__/**/[^.]+Test.ts", - ], - transform: { - "^.+\\.ts$": "ts-jest" - }, -} diff --git a/vendor/gems/graphql/javascript_client/package.json b/vendor/gems/graphql/javascript_client/package.json deleted file mode 100644 index e01d134c93b..00000000000 --- a/vendor/gems/graphql/javascript_client/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "graphql-ruby-client", - "version": "1.14.5", - "description": "JavaScript client for graphql-ruby", - "main": "index.js", - "types": "index.d.ts", - "repository": "https://github.com/rmosolgo/graphql-ruby", - "author": "Robert Mosolgo", - "license": "LGPL-3.0", - "bin": "./cli.js", - "devDependencies": { - "@apollo/client": ">=3.3.13", - "@rails/actioncable": "^7.0.0", - "@types/glob": "^7.1.1", - "@types/jest": "^25.1.2", - "@types/minimist": "^1.2.0", - "@types/node": "^13.7.1", - "@types/pako": "^1.0.1", - "@types/pusher-js": "^4.2.2", - "@types/rails__actioncable": "^6.1.6", - "@types/react": "^17.0.0", - "@types/relay-runtime": "^14.0.0", - "@types/zen-observable": "^0.8.2", - "ably": "1.2.50", - "graphql": ">=15.0.0", - "jest": "^29.0.0", - "nock": "^11.0.0", - "pako": "^2.0.3", - "prettier": "^1.19.1", - "pusher-js": "^7.0.3", - "relay-runtime": "11.0.2", - "ts-jest": "^29.0.0", - "typescript": "5.3.3", - "urql": "^2.2.2" - }, - "scripts": { - "test": "tsc && jest", - "prepublishOnly": "tsc" - }, - "dependencies": { - "glob": "^10.0.0", - "minimist": "^1.2.0" - }, - "peerDependencies": { - "@apollo/client": ">=3.3.6", - "graphql": ">=14.3.1" - }, - "prettier": { - "semi": false, - "trailingComma": "none" - }, - "engines": { - "node": ">=10" - } -} diff --git a/vendor/gems/graphql/javascript_client/readme.md b/vendor/gems/graphql/javascript_client/readme.md deleted file mode 100644 index e10e6183222..00000000000 --- a/vendor/gems/graphql/javascript_client/readme.md +++ /dev/null @@ -1,17 +0,0 @@ -Find the `graphql-ruby-client` docs on the [GraphQL-Ruby website](https://graphql-ruby.org/javascript_client/overview). - -## License - -`graphql-ruby-client` is available under the LGPLv3 license; -[graphql-pro](https://graphql.pro) customers are granted a special commercial license. - -## Development - -- With GraphQL-Ruby: - - Install the dependencies with `rake js:install` - - Run the tests with `rake js:test` -- Stand-alone: - - Install dependencies `yarn install` - - Run the tests `yarn run test` -- Run the TypeScript compiler: `yarn tsc -w` -- Install for local development with `npm link .` diff --git a/vendor/gems/graphql/javascript_client/src/__generated__/Card_card.graphql.js b/vendor/gems/graphql/javascript_client/src/__generated__/Card_card.graphql.js deleted file mode 100644 index 157a5925093..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__generated__/Card_card.graphql.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @flow - */ - -/* eslint-disable */ - -'use strict'; - -/*:: -import type {ConcreteFragment} from 'relay-runtime'; -export type Card_card = {| - +id: string; - +name: string; - +imageUrl: string; -|}; -*/ - - -const fragment /*: ConcreteFragment*/ = { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "Card_card", - "selections": [ - { - "kind": "ScalarField", - "alias": null, - "args": null, - "name": "id", - "storageKey": null - }, - { - "kind": "ScalarField", - "alias": null, - "args": null, - "name": "name", - "storageKey": null - }, - { - "kind": "ScalarField", - "alias": "imageUrl", - "args": null, - "name": "image_url", - "storageKey": null - } - ], - "type": "Card" -}; - -module.exports = fragment; diff --git a/vendor/gems/graphql/javascript_client/src/__generated__/GetStuff.graphql.js b/vendor/gems/graphql/javascript_client/src/__generated__/GetStuff.graphql.js deleted file mode 100644 index 4ea3b5bf838..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__generated__/GetStuff.graphql.js +++ /dev/null @@ -1,194 +0,0 @@ -/** - * This file was generated by: - * relay-compiler - * - * @providesModule AppFeedQuery.graphql - * @generated SignedSource<> - * @relayHash 353e010cb78d082b29cb63ee7e9027b3 - * @flow - * @nogrep - */ - -'use strict'; - -/*:: -import type {ConcreteBatch} from 'relay-runtime'; -*/ - -/* eslint-disable comma-dangle, quotes */ - -/* -query AppFeedQuery { - feed(type: NEW, limit: 5) { - ...Feed - } -} -fragment Feed on Entry { - repository { - owner { - login - } - name - } - ...FeedEntry -} -fragment FeedEntry on Entry { - repository { - owner { - login - } - name - stargazers_count - } - postedBy { - login - } -} -*/ - -const batch /*: ConcreteBatch*/ = { - "fragment": { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "AppFeedQuery", - "selections": [ - { - "kind": "LinkedField", - "alias": null, - "args": [ - { - "kind": "Literal", - "name": "limit", - "value": 5, - "type": "Int" - }, - { - "kind": "Literal", - "name": "type", - "value": "NEW", - "type": "FeedType!" - } - ], - "concreteType": "Entry", - "name": "feed", - "plural": true, - "selections": [ - { - "kind": "FragmentSpread", - "name": "Feed", - "args": null - } - ], - "storageKey": "feed{\"limit\":5,\"type\":\"NEW\"}" - } - ], - "type": "Query" - }, - "id": null, - "kind": "Batch", - "metadata": {}, - "name": "AppFeedQuery", - "query": { - "argumentDefinitions": [], - "kind": "Root", - "name": "AppFeedQuery", - "operation": "query", - "selections": [ - { - "kind": "LinkedField", - "alias": null, - "args": [ - { - "kind": "Literal", - "name": "limit", - "value": 5, - "type": "Int" - }, - { - "kind": "Literal", - "name": "type", - "value": "NEW", - "type": "FeedType!" - } - ], - "concreteType": "Entry", - "name": "feed", - "plural": true, - "selections": [ - { - "kind": "InlineFragment", - "type": "Entry", - "selections": [ - { - "kind": "LinkedField", - "alias": null, - "args": null, - "concreteType": "Repository", - "name": "repository", - "plural": false, - "selections": [ - { - "kind": "LinkedField", - "alias": null, - "args": null, - "concreteType": "User", - "name": "owner", - "plural": false, - "selections": [ - { - "kind": "ScalarField", - "alias": null, - "args": null, - "name": "login", - "storageKey": null - } - ], - "storageKey": null - }, - { - "kind": "ScalarField", - "alias": null, - "args": null, - "name": "name", - "storageKey": null - }, - { - "kind": "ScalarField", - "alias": null, - "args": null, - "name": "stargazers_count", - "storageKey": null - } - ], - "storageKey": null - }, - { - "kind": "LinkedField", - "alias": null, - "args": null, - "concreteType": "User", - "name": "postedBy", - "plural": false, - "selections": [ - { - "kind": "ScalarField", - "alias": null, - "args": null, - "name": "login", - "storageKey": null - } - ], - "storageKey": null - } - ] - } - ], - "storageKey": "feed{\"limit\":5,\"type\":\"NEW\"}" - } - ] - }, - "text": "query AppFeedQuery {\n feed(type: NEW, limit: 5) {\n ...Feed\n }\n}\n\nfragment Feed on Entry {\n repository {\n owner {\n login\n }\n name\n }\n ...FeedEntry\n}\n\nfragment FeedEntry on Entry {\n repository {\n owner {\n login\n }\n name\n stargazers_count\n }\n postedBy {\n login\n }\n}\n" -}; - -module.exports = batch; diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/__snapshots__/syncTest.ts.snap b/vendor/gems/graphql/javascript_client/src/__tests__/__snapshots__/syncTest.ts.snap deleted file mode 100644 index baefca5d212..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/__snapshots__/syncTest.ts.snap +++ /dev/null @@ -1,1026 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`sync operations Input files Merges fragments and operations across files 1`] = ` -[ - { - "alias": "4568c28d403794e011363caf815ec827", - "body": "fragment Frag1 on Query { - moreStuff -} - -query GetStuff { - ...Frag1 -}", - "name": "GetStuff", - }, - { - "alias": "faf462be033e16dd2a56130d56a9192f", - "body": "fragment Frag1 on Query { - moreStuff -} - -fragment Frag2 on Query { - ...Frag3 -} - -fragment Frag3 on Query { - evenMoreStuff -} - -query GetStuff2 { - stuff - ...Frag1 - ...Frag2 -}", - "name": "GetStuff2", - }, - { - "alias": "aab385a1685772ad520fc70d468030fa", - "body": "fragment Frag2 on Query { - ...Frag3 -} - -fragment Frag3 on Query { - evenMoreStuff -} - -fragment Frag4 on Query { - evenMoreStuff { - stuffInside - } -} - -query GetStuff3 { - stuff { - withStuffInside - } - ...Frag2 - ...Frag4 -}", - "name": "GetStuff3", - }, - { - "alias": "b2cb0b317d071f9f38905fba21d73258", - "body": "query GetStuffIsolated { - ...FragIsolated - things { - existHere - } -} - -fragment FragIsolated on Query { - evenMoreStuff { - stuffInside - } -}", - "name": "GetStuffIsolated", - }, - { - "alias": "6cdae165fd6dc5dc5900e5a2bba90cc2", - "body": "query GetStuffIsolated2 { - things { - existHere - } -}", - "name": "GetStuffIsolated2", - }, -] -`; - -exports[`sync operations Input files Uses mode: file to process each file separately 1`] = ` -[ - { - "alias": "664225b943e29ea8c6aae40bbde8923a", - "body": "fragment Frag1 on Query { - moreStuff -}", - "name": "", - }, - { - "alias": "269bbe8bbe7f6a0b9dae7f98b45a9675", - "body": "fragment Frag2 on Query { - ...Frag3 -}", - "name": "", - }, - { - "alias": "d12578840c6518c746b125ae2e7a8ab1", - "body": "fragment Frag3 on Query { - evenMoreStuff -}", - "name": "", - }, - { - "alias": "1a1b6154fb1db8bc6652edfbd7d9ac8a", - "body": "fragment Frag4 on Query { - evenMoreStuff { - stuffInside - } -}", - "name": "", - }, - { - "alias": "8ab1711fcbb7befc98d06ef7d155fd81", - "body": "query GetStuff { - ...Frag1 -}", - "name": "GetStuff", - }, - { - "alias": "cf517696fbd9ec204cd402f48c831090", - "body": "query GetStuff2 { - stuff - ...Frag1 - ...Frag2 -}", - "name": "GetStuff2", - }, - { - "alias": "1e5290206d87a4da749118d84f7e2c65", - "body": "query GetStuff3 { - stuff { - withStuffInside - } - ...Frag2 - ...Frag4 -}", - "name": "GetStuff3", - }, - { - "alias": "b2cb0b317d071f9f38905fba21d73258", - "body": "query GetStuffIsolated { - ...FragIsolated - things { - existHere - } -} - -fragment FragIsolated on Query { - evenMoreStuff { - stuffInside - } -}", - "name": "GetStuffIsolated", - }, - { - "alias": "6cdae165fd6dc5dc5900e5a2bba90cc2", - "body": "query GetStuffIsolated2 { - things { - existHere - } -}", - "name": "GetStuffIsolated2", - }, -] -`; - -exports[`sync operations Logging Can be quieted with quiet: true 1`] = `[]`; - -exports[`sync operations Logging Logs progress 1`] = ` -[ - [ - "Syncing 5 operations to bogus...", - ], - [ - "Generating client module in src/OperationStoreClient.js...", - ], - [ - "✓ Done!", - ], -] -`; - -exports[`sync operations Printing the result prints failure and sends the message to the promise 1`] = ` -[ - [ - "Syncing 5 operations to http://example.com/stored_operations/sync...", - ], - [ - "[Sync] 2 Headers:", - ], - [ - "[Sync] Content-Type: application/json", - ], - [ - "[Sync] Content-Length: 1132", - ], - [ - "[Sync] Data:", - "{"operations":[{"name":"GetStuff","body":"fragment Frag1 on Query {\\n moreStuff\\n}\\n\\nquery GetStuff {\\n ...Frag1\\n}","alias":"4568c28d403794e011363caf815ec827"},{"name":"GetStuff2","body":"fragment Frag1 on Query {\\n moreStuff\\n}\\n\\nfragment Frag2 on Query {\\n ...Frag3\\n}\\n\\nfragment Frag3 on Query {\\n evenMoreStuff\\n}\\n\\nquery GetStuff2 {\\n stuff\\n ...Frag1\\n ...Frag2\\n}","alias":"faf462be033e16dd2a56130d56a9192f"},{"name":"GetStuff3","body":"fragment Frag2 on Query {\\n ...Frag3\\n}\\n\\nfragment Frag3 on Query {\\n evenMoreStuff\\n}\\n\\nfragment Frag4 on Query {\\n evenMoreStuff {\\n stuffInside\\n }\\n}\\n\\nquery GetStuff3 {\\n stuff {\\n withStuffInside\\n }\\n ...Frag2\\n ...Frag4\\n}","alias":"aab385a1685772ad520fc70d468030fa"},{"name":"GetStuffIsolated","body":"query GetStuffIsolated {\\n ...FragIsolated\\n things {\\n existHere\\n }\\n}\\n\\nfragment FragIsolated on Query {\\n evenMoreStuff {\\n stuffInside\\n }\\n}","alias":"b2cb0b317d071f9f38905fba21d73258"},{"name":"GetStuffIsolated2","body":"query GetStuffIsolated2 {\\n things {\\n existHere\\n }\\n}","alias":"6cdae165fd6dc5dc5900e5a2bba90cc2"}]}", - ], - [ - "[Sync] Response Headers: ", - "{"content-type":"application/json"}", - ], - [ - "[Sync] Response Body: ", - "{"errors":{"4568c28d403794e011363caf815ec827":["something"]},"failed":["4568c28d403794e011363caf815ec827"],"added":["defg"],"not_modified":[]}", - ], - [ - " 0 added", - ], - [ - " 0 not modified", - ], - [ - " 1 failed", - ], -] -`; - -exports[`sync operations Printing the result prints failure and sends the message to the promise 2`] = ` -[ - [ - "Sync failed, errors:", - ], - [ - " GetStuff:", - ], - [ - " ✘ something", - ], -] -`; - -exports[`sync operations Printing the result prints success 1`] = ` -[ - [ - "Syncing 5 operations to http://example.com/stored_operations/sync...", - ], - [ - "[Sync] 2 Headers:", - ], - [ - "[Sync] Content-Type: application/json", - ], - [ - "[Sync] Content-Length: 1132", - ], - [ - "[Sync] Data:", - "{"operations":[{"name":"GetStuff","body":"fragment Frag1 on Query {\\n moreStuff\\n}\\n\\nquery GetStuff {\\n ...Frag1\\n}","alias":"4568c28d403794e011363caf815ec827"},{"name":"GetStuff2","body":"fragment Frag1 on Query {\\n moreStuff\\n}\\n\\nfragment Frag2 on Query {\\n ...Frag3\\n}\\n\\nfragment Frag3 on Query {\\n evenMoreStuff\\n}\\n\\nquery GetStuff2 {\\n stuff\\n ...Frag1\\n ...Frag2\\n}","alias":"faf462be033e16dd2a56130d56a9192f"},{"name":"GetStuff3","body":"fragment Frag2 on Query {\\n ...Frag3\\n}\\n\\nfragment Frag3 on Query {\\n evenMoreStuff\\n}\\n\\nfragment Frag4 on Query {\\n evenMoreStuff {\\n stuffInside\\n }\\n}\\n\\nquery GetStuff3 {\\n stuff {\\n withStuffInside\\n }\\n ...Frag2\\n ...Frag4\\n}","alias":"aab385a1685772ad520fc70d468030fa"},{"name":"GetStuffIsolated","body":"query GetStuffIsolated {\\n ...FragIsolated\\n things {\\n existHere\\n }\\n}\\n\\nfragment FragIsolated on Query {\\n evenMoreStuff {\\n stuffInside\\n }\\n}","alias":"b2cb0b317d071f9f38905fba21d73258"},{"name":"GetStuffIsolated2","body":"query GetStuffIsolated2 {\\n things {\\n existHere\\n }\\n}","alias":"6cdae165fd6dc5dc5900e5a2bba90cc2"}]}", - ], -] -`; - -exports[`sync operations Printing the result prints success 2`] = ` -[ - [ - "[Sync] Response Headers: ", - "{"content-type":"application/json"}", - ], - [ - "[Sync] Response Body: ", - "{"errors":{},"failed":[],"added":["defg"],"not_modified":["xyz","123"]}", - ], - [ - " 1 added", - ], - [ - " 2 not modified", - ], - [ - " 0 failed", - ], - [ - "Generating client module in src/OperationStoreClient.js...", - ], - [ - "✓ Done!", - ], -] -`; - -exports[`sync operations Printing the result prints success 3`] = `[]`; - -exports[`sync operations Relay support Uses Apollo Android OperationOutput JSON files 1`] = ` -[ - { - "alias": "aba626ea9bdf465954e89e5590eb2c1a", - "body": "mutation RemoveTodoMutation($input: RemoveTodoInput!) { - removeTodo(input: $input) { - deletedTodoId - user { - completedCount - totalCount - id - } - } -}", - }, - { - "alias": "67c2bc8aa3185a209d6651b4feb63c04", - "body": "query appQuery( - $userId: String -) { - user(id: $userId) { - ...TodoApp_user - id - } -} - -fragment TodoApp_user on User { - id - userId - totalCount - ...TodoListFooter_user - ...TodoList_user -} - -fragment TodoListFooter_user on User { - id - userId - completedCount - todos(first: 2147483647) { - edges { - node { - id - complete - __typename - } - cursor - } - pageInfo { - endCursor - hasNextPage - } - } - totalCount -} - -fragment TodoList_user on User { - todos(first: 2147483647) { - edges { - node { - id - complete - ...Todo_todo - __typename - } - cursor - } - pageInfo { - endCursor - hasNextPage - } - } - id - userId - totalCount - completedCount - ...Todo_user -} - -fragment Todo_todo on Todo { - complete - id - text -} - -fragment Todo_user on User { - id - userId - totalCount - completedCount -} -", - }, - { - "alias": "db9904c31d91416f21d45fe3d153884c", - "body": "mutation MarkAllTodosMutation( - $input: MarkAllTodosInput! -) { - markAllTodos(input: $input) { - changedTodos { - id - complete - } - user { - id - completedCount - } - } -} -", - }, - { - "alias": "2eb8c9941fdb3117fdbc08d15fab62d0", - "body": "mutation AddTodoMutation( - $input: AddTodoInput! -) { - addTodo(input: $input) { - todoEdge { - __typename - cursor - node { - complete - id - text - } - } - user { - id - totalCount - } - } -} -", - }, - { - "alias": "d970fd7dbf118794415dec7324d463e3", - "body": "mutation RenameTodoMutation( - $input: RenameTodoInput! -) { - renameTodo(input: $input) { - todo { - id - text - } - } -} -", - }, - { - "alias": "a49217db31a8be3f4107763b957d5fca", - "body": "mutation RemoveCompletedTodosMutation( - $input: RemoveCompletedTodosInput! -) { - removeCompletedTodos(input: $input) { - deletedTodoIds - user { - completedCount - totalCount - id - } - } -} -", - }, - { - "alias": "d7dda774dcfa32fe0d9661e01cac9a4a", - "body": "mutation ChangeTodoStatusMutation( - $input: ChangeTodoStatusInput! -) { - changeTodoStatus(input: $input) { - todo { - id - complete - } - user { - id - completedCount - } - } -} -", - }, -] -`; - -exports[`sync operations Relay support Uses Apollo Codegen JSON files 1`] = ` -[ - { - "alias": "22cc98c61c1402c92b230b7c515e07eb793a5152c388b015e86df4652ec58156", - "body": "mutation UpdateSomething($name: String!) { - updateSomething(name: $name) { - __typename - name - } -}", - "name": "UpdateSomething", - }, - { - "alias": "688df2ea182541c70a34c55ca056dc249014bf9f33c64eee527120c714e936fc", - "body": "query getHelloWorld { - helloWorld - ...MoreFields -} -fragment MoreFields on Query { - __typename -}", - "name": "getHelloWorld", - }, -] -`; - -exports[`sync operations Relay support Uses Relay generated .js files 1`] = ` -[ - { - "alias": "353e010cb78d082b29cb63ee7e9027b3", - "body": "query AppFeedQuery { - feed(type: NEW, limit: 5) { - ...Feed - } -} - -fragment Feed on Entry { - repository { - owner { - login - } - name - } - ...FeedEntry -} - -fragment FeedEntry on Entry { - repository { - owner { - login - } - name - stargazers_count - } - postedBy { - login - } -} -", - "name": "AppFeedQuery", - }, -] -`; - -exports[`sync operations Relay support Uses relay --persist-output JSON files 1`] = ` -[ - { - "alias": "aba626ea9bdf465954e89e5590eb2c1a", - "body": "mutation RemoveTodoMutation( - $input: RemoveTodoInput! -) { - removeTodo(input: $input) { - deletedTodoId - user { - completedCount - totalCount - id - } - } -} -", - }, - { - "alias": "67c2bc8aa3185a209d6651b4feb63c04", - "body": "query appQuery( - $userId: String -) { - user(id: $userId) { - ...TodoApp_user - id - } -} - -fragment TodoApp_user on User { - id - userId - totalCount - ...TodoListFooter_user - ...TodoList_user -} - -fragment TodoListFooter_user on User { - id - userId - completedCount - todos(first: 2147483647) { - edges { - node { - id - complete - __typename - } - cursor - } - pageInfo { - endCursor - hasNextPage - } - } - totalCount -} - -fragment TodoList_user on User { - todos(first: 2147483647) { - edges { - node { - id - complete - ...Todo_todo - __typename - } - cursor - } - pageInfo { - endCursor - hasNextPage - } - } - id - userId - totalCount - completedCount - ...Todo_user -} - -fragment Todo_todo on Todo { - complete - id - text -} - -fragment Todo_user on User { - id - userId - totalCount - completedCount -} -", - }, - { - "alias": "db9904c31d91416f21d45fe3d153884c", - "body": "mutation MarkAllTodosMutation( - $input: MarkAllTodosInput! -) { - markAllTodos(input: $input) { - changedTodos { - id - complete - } - user { - id - completedCount - } - } -} -", - }, - { - "alias": "2eb8c9941fdb3117fdbc08d15fab62d0", - "body": "mutation AddTodoMutation( - $input: AddTodoInput! -) { - addTodo(input: $input) { - todoEdge { - __typename - cursor - node { - complete - id - text - } - } - user { - id - totalCount - } - } -} -", - }, - { - "alias": "d970fd7dbf118794415dec7324d463e3", - "body": "mutation RenameTodoMutation( - $input: RenameTodoInput! -) { - renameTodo(input: $input) { - todo { - id - text - } - } -} -", - }, - { - "alias": "a49217db31a8be3f4107763b957d5fca", - "body": "mutation RemoveCompletedTodosMutation( - $input: RemoveCompletedTodosInput! -) { - removeCompletedTodos(input: $input) { - deletedTodoIds - user { - completedCount - totalCount - id - } - } -} -", - }, - { - "alias": "d7dda774dcfa32fe0d9661e01cac9a4a", - "body": "mutation ChangeTodoStatusMutation( - $input: ChangeTodoStatusInput! -) { - changeTodoStatus(input: $input) { - todo { - id - complete - } - user { - id - completedCount - } - } -} -", - }, -] -`; - -exports[`sync operations Sync output Can dump payload and outfile at the same time 1`] = ` -"{ - "operations": [ - { - "name": "GetStuff", - "body": "fragment Frag1 on Query {\\n moreStuff\\n}\\n\\nquery GetStuff {\\n ...Frag1\\n}", - "alias": "4568c28d403794e011363caf815ec827" - }, - { - "name": "GetStuff2", - "body": "fragment Frag1 on Query {\\n moreStuff\\n}\\n\\nfragment Frag2 on Query {\\n ...Frag3\\n}\\n\\nfragment Frag3 on Query {\\n evenMoreStuff\\n}\\n\\nquery GetStuff2 {\\n stuff\\n ...Frag1\\n ...Frag2\\n}", - "alias": "faf462be033e16dd2a56130d56a9192f" - }, - { - "name": "GetStuff3", - "body": "fragment Frag2 on Query {\\n ...Frag3\\n}\\n\\nfragment Frag3 on Query {\\n evenMoreStuff\\n}\\n\\nfragment Frag4 on Query {\\n evenMoreStuff {\\n stuffInside\\n }\\n}\\n\\nquery GetStuff3 {\\n stuff {\\n withStuffInside\\n }\\n ...Frag2\\n ...Frag4\\n}", - "alias": "aab385a1685772ad520fc70d468030fa" - }, - { - "name": "GetStuffIsolated", - "body": "query GetStuffIsolated {\\n ...FragIsolated\\n things {\\n existHere\\n }\\n}\\n\\nfragment FragIsolated on Query {\\n evenMoreStuff {\\n stuffInside\\n }\\n}", - "alias": "b2cb0b317d071f9f38905fba21d73258" - }, - { - "name": "GetStuffIsolated2", - "body": "query GetStuffIsolated2 {\\n things {\\n existHere\\n }\\n}", - "alias": "6cdae165fd6dc5dc5900e5a2bba90cc2" - } - ] -} -" -`; - -exports[`sync operations custom file processing options Adds .graphql to the glob if needed 1`] = ` -[ - { - "alias": "b8086942c2fbb6ac69b97cbade848033", - "body": "query GetStuff { - stuff -}", - "name": "GetStuff", - }, -] -`; - -exports[`sync operations custom file processing options Adds .graphql to the glob if needed 2`] = ` -[ - { - "alias": "b8086942c2fbb6ac69b97cbade848033", - "body": "query GetStuff { - stuff -}", - "name": "GetStuff", - }, -] -`; - -exports[`sync operations custom file processing options Uses a custom hash function if provided 1`] = ` -[ - { - "alias": "GETSTUFF", - "body": "query GetStuff { - stuff -}", - "name": "GetStuff", - }, -] -`; - -exports[`sync operations generating artifacts without syncing works with persisted query manifest 1`] = ` -" - /** - * Generated by graphql-ruby-client - * - */ - - /** - * Map local operation names to persisted keys on the server - * @return {Object} - * @private - */ - var _aliases = { - "TestQuery1": "4a29162b05ee4d82ad02e8f50af4bf112f47181ec558a7100a", - "TestQuery2": "xyz-123" -} - - /** - * The client who synced these operations with the server - * @return {String} - * @private - */ - var _client = "test-1" - - var OperationStoreClient = { - /** - * Build a string for \`params[:operationId]\` - * @param {String} operationName - * @return {String} stored operation ID - */ - getOperationId: function(operationName) { - return _client + "/" + OperationStoreClient.getPersistedQueryAlias(operationName) - }, - - /** - * Fetch a persisted alias from a local operation name - * @param {String} operationName - * @return {String} persisted alias - */ - getPersistedQueryAlias: function(operationName) { - var persistedAlias = _aliases[operationName] - if (!persistedAlias) { - throw new Error("Failed to find persisted alias for operation name: " + operationName) - } else { - return persistedAlias - } - }, - - /** - * Satisfy the Apollo Link API. - * This link checks for an operation name, and if it's present, - * sets the HTTP context to _not_ include the query, - * and instead, include \`extensions.operationId\`. - * (This is inspired by apollo-link-persisted-queries.) - */ - apolloLink: function(operation, forward) { - if (operation.operationName) { - const operationId = OperationStoreClient.getOperationId(operation.operationName) - operation.setContext({ - http: { - includeQuery: false, - includeExtensions: true, - } - }) - operation.extensions.operationId = operationId - } - return forward(operation) - }, - /** - * Satisfy the Apollo middleware API. - * Replace the query with an operationId - */ - apolloMiddleware: { - applyBatchMiddleware: function(options, next) { - options.requests.forEach(function(req) { - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - return req - }) - // Continue the request - next() - }, - - applyMiddleware: function(options, next) { - var req = options.request - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - // Continue the request - next() - } - } - } - - module.exports = OperationStoreClient - " -`; - -exports[`sync operations generating artifacts without syncing works without a URL 1`] = ` -" - /** - * Generated by graphql-ruby-client - * - */ - - /** - * Map local operation names to persisted keys on the server - * @return {Object} - * @private - */ - var _aliases = { - "GetStuff": "b8086942c2fbb6ac69b97cbade848033" -} - - /** - * The client who synced these operations with the server - * @return {String} - * @private - */ - var _client = "test-1" - - var OperationStoreClient = { - /** - * Build a string for \`params[:operationId]\` - * @param {String} operationName - * @return {String} stored operation ID - */ - getOperationId: function(operationName) { - return _client + "/" + OperationStoreClient.getPersistedQueryAlias(operationName) - }, - - /** - * Fetch a persisted alias from a local operation name - * @param {String} operationName - * @return {String} persisted alias - */ - getPersistedQueryAlias: function(operationName) { - var persistedAlias = _aliases[operationName] - if (!persistedAlias) { - throw new Error("Failed to find persisted alias for operation name: " + operationName) - } else { - return persistedAlias - } - }, - - /** - * Satisfy the Apollo Link API. - * This link checks for an operation name, and if it's present, - * sets the HTTP context to _not_ include the query, - * and instead, include \`extensions.operationId\`. - * (This is inspired by apollo-link-persisted-queries.) - */ - apolloLink: function(operation, forward) { - if (operation.operationName) { - const operationId = OperationStoreClient.getOperationId(operation.operationName) - operation.setContext({ - http: { - includeQuery: false, - includeExtensions: true, - } - }) - operation.extensions.operationId = operationId - } - return forward(operation) - }, - /** - * Satisfy the Apollo middleware API. - * Replace the query with an operationId - */ - apolloMiddleware: { - applyBatchMiddleware: function(options, next) { - options.requests.forEach(function(req) { - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - return req - }) - // Continue the request - next() - }, - - applyMiddleware: function(options, next) { - var req = options.request - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - // Continue the request - next() - } - } - } - - module.exports = OperationStoreClient - " -`; - -exports[`sync operations verbose Adds debug output 1`] = ` -[ - [ - "[Sync] glob: ", - "./src/__tests__/documents**/*.graphql*", - ], - [ - "[Sync] 1 files:", - ], - [ - "[Sync] - src/__tests__/documents/doc1.graphql", - ], - [ - "Syncing 1 operations to bogus...", - ], - [ - "Verbose!", - ], - [ - "Generating client module in src/OperationStoreClient.js...", - ], - [ - "✓ Done!", - ], -] -`; diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/apollo.config.js b/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/apollo.config.js deleted file mode 100644 index aa00f7be6d8..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/apollo.config.js +++ /dev/null @@ -1,11 +0,0 @@ -// apollo client:codegen gen/output.json --target json -module.exports = { - client: { - service: { - name: "testSchema", - localSchemaFile: "./schema.graphql", - }, - includes: ["./*.ts"], - mergeInFieldsFromFragmentSpreads: true, - } -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/fragment.ts b/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/fragment.ts deleted file mode 100644 index 2b2e04b705e..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/fragment.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { gql } from '@apollo/client'; - -export const MORE_FIELDS = gql` -fragment MoreFields on Query { __typename } -` diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/gen/output.json b/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/gen/output.json deleted file mode 100644 index e94a91b7fb4..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/gen/output.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "operations": [ - { - "filePath": "file:///Users/rmosolgo/code/graphql-ruby/javascript_client/src/sync/__tests__/apolloExample/mutation.ts", - "operationName": "UpdateSomething", - "operationType": "mutation", - "rootType": "Mutation", - "variables": [ - { - "name": "name", - "type": "String!" - } - ], - "source": "mutation UpdateSomething($name: String!) {\n updateSomething(name: $name) {\n __typename\n name\n somethingElse @client\n }\n}", - "fields": [ - { - "responseName": "updateSomething", - "fieldName": "updateSomething", - "type": "UpdateSomethingPayload", - "args": [ - { - "name": "name", - "value": { - "kind": "Variable", - "variableName": "name" - }, - "type": "String!" - } - ], - "isConditional": false, - "isDeprecated": false, - "fields": [ - { - "responseName": "__typename", - "fieldName": "__typename", - "type": "String!", - "isConditional": false - }, - { - "responseName": "name", - "fieldName": "name", - "type": "String!", - "isConditional": false, - "isDeprecated": false - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "fragmentSpreads": [], - "inlineFragments": [], - "fragmentsReferenced": [], - "sourceWithFragments": "mutation UpdateSomething($name: String!) {\n updateSomething(name: $name) {\n __typename\n name\n }\n}", - "operationId": "22cc98c61c1402c92b230b7c515e07eb793a5152c388b015e86df4652ec58156" - }, - { - "filePath": "file:///Users/rmosolgo/code/graphql-ruby/javascript_client/src/sync/__tests__/apolloExample/query.ts", - "operationName": "getHelloWorld", - "operationType": "query", - "rootType": "Query", - "variables": [], - "source": "query getHelloWorld {\n helloWorld\n ...MoreFields\n}", - "fields": [ - { - "responseName": "helloWorld", - "fieldName": "helloWorld", - "type": "String!", - "isConditional": false, - "isDeprecated": false - } - ], - "fragmentSpreads": [ - "MoreFields" - ], - "inlineFragments": [], - "fragmentsReferenced": [ - "MoreFields" - ], - "sourceWithFragments": "query getHelloWorld {\n helloWorld\n ...MoreFields\n}\nfragment MoreFields on Query {\n __typename\n}", - "operationId": "688df2ea182541c70a34c55ca056dc249014bf9f33c64eee527120c714e936fc" - } - ], - "fragments": [ - { - "typeCondition": "Query", - "possibleTypes": [ - "Query" - ], - "fragmentName": "MoreFields", - "filePath": "file:///Users/rmosolgo/code/graphql-ruby/javascript_client/src/sync/__tests__/apolloExample/fragment.ts", - "source": "fragment MoreFields on Query {\n __typename\n}", - "fields": [ - { - "responseName": "__typename", - "fieldName": "__typename", - "type": "String!", - "isConditional": false - } - ], - "fragmentSpreads": [], - "inlineFragments": [] - } - ], - "typesUsed": [], - "unionTypes": [], - "interfaceTypes": [] -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/mutation.ts b/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/mutation.ts deleted file mode 100644 index 02d7b7d749f..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/mutation.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { gql } from '@apollo/client'; - -export const UPDATE_SOMETHING = gql` -mutation UpdateSomething($name: String!) { - updateSomething(name: $name) { name } -} -` diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/query.ts b/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/query.ts deleted file mode 100644 index 7a13a7deed3..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/query.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { gql } from '@apollo/client'; -import { MORE_FIELDS } from './fragment'; - -export const GET_HELLO_WORLD = gql` -query getHelloWorld { - helloWorld - ... MoreFields -} -${MORE_FIELDS} -` diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/schema.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/schema.graphql deleted file mode 100644 index c1b8f5b1e40..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/apolloExample/schema.graphql +++ /dev/null @@ -1,11 +0,0 @@ -type Query { - helloWorld: String! -} - -type Mutation { - updateSomething(name: String!): UpdateSomethingPayload -} - -type UpdateSomethingPayload { - name: String! -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/cliTest.ts b/vendor/gems/graphql/javascript_client/src/__tests__/cliTest.ts deleted file mode 100644 index b476cdb73b5..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/cliTest.ts +++ /dev/null @@ -1,57 +0,0 @@ -var childProcess = require("child_process") -let fs = require('fs') - -describe("CLI", () => { - it("exits 1 on error", () => { - expect(() => { - childProcess.execSync("node ./cli.js sync", {stdio: "pipe"}) - }).toThrow("Client name must be provided for sync") - }) - - it("exits 0 on OK", () => { - childProcess.execSync("node ./cli.js sync -h", {stdio: "pipe"}) - }) - - it("runs with some options", () => { - var buffer = childProcess.execSync("node ./cli.js sync --client=something --header=Abcd:efgh --header=\"Abc: 123 45\" --changeset-version=2023-01-01 --mode=file --path=\"**/doc1.graphql\" --verbose", {stdio: "pipe"}) - var response = buffer.toString().replace(/\033\[[0-9;]*m/g, "") - expect(response).toEqual("No URL; Generating artifacts without syncing them\n[Sync] glob: **/doc1.graphql\n[Sync] 1 files:\n[Sync] - src/__tests__/documents/doc1.graphql\nGenerating client module in src/OperationStoreClient.js...\n✓ Done!\n") - }) - - it("runs with just one header", () => { - var buffer = childProcess.execSync("node ./cli.js sync --client=something --header=Ab-cd:ef-gh --mode=file --path=\"**/doc1.graphql\"", {stdio: "pipe"}) - var response = buffer.toString().replace(/\033\[[0-9;]*m/g, "") - expect(response).toEqual("No URL; Generating artifacts without syncing them\nGenerating client module in src/OperationStoreClient.js...\n✓ Done!\n") - }) - - it("writes to a dump file", () => { - let buffer = childProcess.execSync("node ./cli.js sync --client=something --header=Ab-cd:ef-gh --dump-payload=./DumpPayloadExample.json --path=\"**/doc1.graphql\"", {stdio: "pipe"}) - console.log(buffer.toString()) - let dumpedJSON = fs.readFileSync("./DumpPayloadExample.json", 'utf8') - expect(dumpedJSON).toEqual(`{ - "operations": [ - { - "name": "GetStuff", - "body": "query GetStuff {\\n stuff\\n}", - "alias": "b8086942c2fbb6ac69b97cbade848033" - } - ] -} -`) - }) - - it("writes to stdout", () => { - let buffer = childProcess.execSync("node ./cli.js sync --client=something --header=Ab-cd:ef-gh --dump-payload --path=\"**/doc1.graphql\"", {stdio: "pipe"}) - let dumpedJSON = buffer.toString().replace(/\033\[[0-9;]*m/g, "") - expect(dumpedJSON).toEqual(`{ - "operations": [ - { - "name": "GetStuff", - "body": "query GetStuff {\\n stuff\\n}", - "alias": "b8086942c2fbb6ac69b97cbade848033" - } - ] -} -`) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/documents/doc1.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/documents/doc1.graphql deleted file mode 100644 index 95d56a491ba..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/documents/doc1.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query GetStuff { - stuff -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/example-apollo-android-operation-output.json b/vendor/gems/graphql/javascript_client/src/__tests__/example-apollo-android-operation-output.json deleted file mode 100644 index 4f93955f2f6..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/example-apollo-android-operation-output.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "aba626ea9bdf465954e89e5590eb2c1a": { - "name": "RemoveTodoMutation", - "source": "mutation RemoveTodoMutation(\n $input: RemoveTodoInput!\n) {\n removeTodo(input: $input) {\n deletedTodoId\n user {\n completedCount\n totalCount\n thing @client\n id\n }\n }\n}\n" - }, - "67c2bc8aa3185a209d6651b4feb63c04": { - "name": "appQuery", - "source": "query appQuery(\n $userId: String\n) {\n user(id: $userId) {\n ...TodoApp_user\n id\n }\n}\n\nfragment TodoApp_user on User {\n id\n userId\n totalCount\n ...TodoListFooter_user\n ...TodoList_user\n}\n\nfragment TodoListFooter_user on User {\n id\n userId\n completedCount\n todos(first: 2147483647) {\n edges {\n node {\n id\n complete\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n totalCount\n}\n\nfragment TodoList_user on User {\n todos(first: 2147483647) {\n edges {\n node {\n id\n complete\n ...Todo_todo\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n userId\n totalCount\n completedCount\n ...Todo_user\n}\n\nfragment Todo_todo on Todo {\n complete\n id\n text\n}\n\nfragment Todo_user on User {\n id\n userId\n totalCount\n completedCount\n}\n" - }, - "db9904c31d91416f21d45fe3d153884c": { - "name": "MarkAllTodosMutation", - "source": "mutation MarkAllTodosMutation(\n $input: MarkAllTodosInput!\n) {\n markAllTodos(input: $input) {\n changedTodos {\n id\n complete\n }\n user {\n id\n completedCount\n }\n }\n}\n" - }, - "2eb8c9941fdb3117fdbc08d15fab62d0": { - "name": "AddTodoMutation", - "source": "mutation AddTodoMutation(\n $input: AddTodoInput!\n) {\n addTodo(input: $input) {\n todoEdge {\n __typename\n cursor\n node {\n complete\n id\n text\n }\n }\n user {\n id\n totalCount\n }\n }\n}\n" - }, - "d970fd7dbf118794415dec7324d463e3": { - "name": "RenameTodoMutation", - "source": "mutation RenameTodoMutation(\n $input: RenameTodoInput!\n) {\n renameTodo(input: $input) {\n todo {\n id\n text\n }\n }\n}\n" - }, - "a49217db31a8be3f4107763b957d5fca": { - "name": "RemoveCompletedTodosMutation", - "source": "mutation RemoveCompletedTodosMutation(\n $input: RemoveCompletedTodosInput!\n) {\n removeCompletedTodos(input: $input) {\n deletedTodoIds\n user {\n completedCount\n totalCount\n id\n }\n }\n}\n" - }, - "d7dda774dcfa32fe0d9661e01cac9a4a": { - "name": "ChangeTodoStatusMutation", - "source": "mutation ChangeTodoStatusMutation(\n $input: ChangeTodoStatusInput!\n) {\n changeTodoStatus(input: $input) {\n todo {\n id\n complete\n }\n user {\n id\n completedCount\n }\n }\n}\n" - } -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/example-relay-persisted-queries.json b/vendor/gems/graphql/javascript_client/src/__tests__/example-relay-persisted-queries.json deleted file mode 100644 index 52f40227c2e..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/example-relay-persisted-queries.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "aba626ea9bdf465954e89e5590eb2c1a": "mutation RemoveTodoMutation(\n $input: RemoveTodoInput!\n) {\n removeTodo(input: $input) {\n deletedTodoId\n user {\n completedCount\n totalCount\n id\n }\n }\n}\n", - "67c2bc8aa3185a209d6651b4feb63c04": "query appQuery(\n $userId: String\n) {\n user(id: $userId) {\n ...TodoApp_user\n id\n }\n}\n\nfragment TodoApp_user on User {\n id\n userId\n totalCount\n ...TodoListFooter_user\n ...TodoList_user\n}\n\nfragment TodoListFooter_user on User {\n id\n userId\n completedCount\n todos(first: 2147483647) {\n edges {\n node {\n id\n complete\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n totalCount\n}\n\nfragment TodoList_user on User {\n todos(first: 2147483647) {\n edges {\n node {\n id\n complete\n ...Todo_todo\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n userId\n totalCount\n completedCount\n ...Todo_user\n}\n\nfragment Todo_todo on Todo {\n complete\n id\n text\n}\n\nfragment Todo_user on User {\n id\n userId\n totalCount\n completedCount\n}\n", - "db9904c31d91416f21d45fe3d153884c": "mutation MarkAllTodosMutation(\n $input: MarkAllTodosInput!\n) {\n markAllTodos(input: $input) {\n changedTodos {\n id\n complete\n }\n user {\n id\n completedCount\n }\n }\n}\n", - "2eb8c9941fdb3117fdbc08d15fab62d0": "mutation AddTodoMutation(\n $input: AddTodoInput!\n) {\n addTodo(input: $input) {\n todoEdge {\n __typename\n cursor\n node {\n complete\n id\n text\n }\n }\n user {\n id\n totalCount\n }\n }\n}\n", - "d970fd7dbf118794415dec7324d463e3": "mutation RenameTodoMutation(\n $input: RenameTodoInput!\n) {\n renameTodo(input: $input) {\n todo {\n id\n text\n }\n }\n}\n", - "a49217db31a8be3f4107763b957d5fca": "mutation RemoveCompletedTodosMutation(\n $input: RemoveCompletedTodosInput!\n) {\n removeCompletedTodos(input: $input) {\n deletedTodoIds\n user {\n completedCount\n totalCount\n id\n }\n }\n}\n", - "d7dda774dcfa32fe0d9661e01cac9a4a": "mutation ChangeTodoStatusMutation(\n $input: ChangeTodoStatusInput!\n) {\n changeTodoStatus(input: $input) {\n todo {\n id\n complete\n }\n user {\n id\n completedCount\n }\n }\n}\n" -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/indexTest.ts b/vendor/gems/graphql/javascript_client/src/__tests__/indexTest.ts deleted file mode 100644 index a9941af2d26..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/indexTest.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {sync} from "../index" -import childProcess from "child_process" - -describe("root module", () => { - it("exports the sync function", () => { - expect(sync).toBeInstanceOf(Function) - }) - - it("exports things at root level", () => { - // Make sure that the compiled JavaScript - // has all the expected exports. - var testScript = "var client = require('./index'); console.log(JSON.stringify({ keys: Object.keys(client).sort() }))" - var output = childProcess.execSync("node -e \"" + testScript + "\"") - var outputData = JSON.parse(output.toString()) - var expectedKeys = [ - "AblyLink", - "ActionCableLink", - "PusherLink", - "addGraphQLSubscriptions", - "createRelaySubscriptionHandler", - "generateClient", - "sync" - ] - expect(outputData.keys).toEqual(expectedKeys) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_1.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_1.graphql deleted file mode 100644 index 044f2a4a2c7..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_1.graphql +++ /dev/null @@ -1,3 +0,0 @@ -fragment Frag1 on Query { - moreStuff -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_2.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_2.graphql deleted file mode 100644 index 9191887588a..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_2.graphql +++ /dev/null @@ -1,3 +0,0 @@ -fragment Frag2 on Query { - ...Frag3 -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_3.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_3.graphql deleted file mode 100644 index 35f3144cb09..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_3.graphql +++ /dev/null @@ -1,3 +0,0 @@ -fragment Frag3 on Query { - evenMoreStuff -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_4.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_4.graphql deleted file mode 100644 index 4165930fd78..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/frag_4.graphql +++ /dev/null @@ -1,5 +0,0 @@ -fragment Frag4 on Query { - evenMoreStuff { - stuffInside - } -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_1.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/op_1.graphql deleted file mode 100644 index de89a4e60fb..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_1.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query GetStuff { - ...Frag1 -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_2.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/op_2.graphql deleted file mode 100644 index 1c354430731..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_2.graphql +++ /dev/null @@ -1,5 +0,0 @@ -query GetStuff2 { - stuff - ...Frag1 - ...Frag2 -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_3.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/op_3.graphql deleted file mode 100644 index 8e5be05372a..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_3.graphql +++ /dev/null @@ -1,7 +0,0 @@ -query GetStuff3 { - stuff { - withStuffInside - } - ...Frag2 - ...Frag4 -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_1.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_1.graphql deleted file mode 100644 index 5ef2239c8e1..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_1.graphql +++ /dev/null @@ -1,12 +0,0 @@ -query GetStuffIsolated { - ...FragIsolated - things { - existHere - } -} - -fragment FragIsolated on Query { - evenMoreStuff { - stuffInside - } -} diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_2.graphql b/vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_2.graphql deleted file mode 100644 index dab8761f204..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/project/op_isolated_2.graphql +++ /dev/null @@ -1,6 +0,0 @@ -query GetStuffIsolated2 { - things { - existHere - } -} - diff --git a/vendor/gems/graphql/javascript_client/src/__tests__/syncTest.ts b/vendor/gems/graphql/javascript_client/src/__tests__/syncTest.ts deleted file mode 100644 index e7497605533..00000000000 --- a/vendor/gems/graphql/javascript_client/src/__tests__/syncTest.ts +++ /dev/null @@ -1,446 +0,0 @@ -import sync from "../sync" -import Logger from "../sync/logger" -var fs = require("fs") -var nock = require("nock") - -interface MockOperation { - alias: string, -} - -interface MockPayload { - operations: MockOperation[], - generatedCode: string, -} - -interface MockedObject { - mock: { calls: object } -} - -describe("sync operations", () => { - beforeEach(() => { - global.console.error = jest.fn() - global.console.log = jest.fn() - if (fs.existsSync("./src/OperationStoreClient.js")) { - fs.unlinkSync("./src/OperationStoreClient.js") - } - }) - - afterEach(() => { - jest.clearAllMocks(); - }) - - describe("generating artifacts without syncing", () => { - it("works without a URL", () => { - var options = { - client: "test-1", - path: "./src/__tests__/documents", - } - - return sync(options).then(function() { - var generatedCode = fs.readFileSync("./src/OperationStoreClient.js", "utf8") - expect(generatedCode).toMatch('"GetStuff": "b8086942c2fbb6ac69b97cbade848033"') - expect(generatedCode).toMatchSnapshot() - }) - }) - - it("works with persisted query manifest", () => { - var options = { - client: "test-1", - outfile: "./src/OperationStoreClient.js", - apolloPersistedQueryManifest: "./src/sync/__tests__/generate-persisted-query-manifest.json", - } - - return sync(options).then(function() { - var generatedCode = fs.readFileSync("./src/OperationStoreClient.js", "utf8") - expect(generatedCode).toMatch('"TestQuery2": "xyz-123"') - expect(generatedCode).toMatchSnapshot() - }) - }) - }) - - describe("custom HTTP options", () => { - it("uses the provided `send` option & provided URL", () => { - var url: string - var options = { - client: "test-1", - path: "./src/__tests__/documents", - url: "bogus", - headers: { - "X-Something-Special": "🎂", - }, - changesetVersion: "2023-05-05", - quiet: true, - send: (_sendPayload: object, options: { url: string, headers: {[key: string]: string}, changesetVersion: string }) => { - url = options.url - Object.keys(options.headers).forEach((h) => { - url += "?" + h + "=" + options.headers[h] - }) - url += "&changesetVersion=" + options.changesetVersion - }, - } - return sync(options).then(function() { - expect(url).toEqual("bogus?X-Something-Special=🎂&changesetVersion=2023-05-05") - }) - }) - }) - - describe("verbose", () => { - it("Adds debug output", () => { - var spy = (console.log as unknown) as MockedObject - var options = { - client: "test-1", - path: "./src/__tests__/documents", - url: "bogus", - verbose: true, - send: (_sendPayload: string, opts: { logger: Logger }) => { - opts.logger.log("Verbose!") - }, - } - return sync(options).then(function() { - expect(spy.mock.calls).toMatchSnapshot() - }) - }) - }) - - describe("custom file processing options", () => { - it("Adds .graphql to the glob if needed", () => { - var payload: MockPayload - var options = { - client: "test-1", - path: "./src/__tests__/documents", - url: "bogus", - quiet: true, - send: (sendPayload: MockPayload, _opts: object) => { payload = sendPayload }, - } - return sync(options).then(function() { - expect(payload.operations).toMatchSnapshot() - - var optionsWithExt = {...options, glob: "./**/*.graphql"} - return sync(optionsWithExt).then(function() { - // Get the same result, even when the glob already has a file extension - expect(payload.operations).toMatchSnapshot() - }) - }) - }) - - it("Uses a custom hash function if provided", () => { - var payload: MockPayload - var options = { - client: "test-1", - path: "./src/__tests__/documents", - url: "bogus", - quiet: true, - hash: (graphQLBody: string) => { - // This is a bad hack to get the operation name - var opName = graphQLBody.match(/query ([A-Za-z]+) \{/) - return opName ? opName[1].toUpperCase() : null - }, - send: (sendPayload: MockPayload, _opts: object) => { payload = sendPayload }, - } - return sync(options).then(function() { - expect(payload.operations).toMatchSnapshot() - }) - }) - }) - - describe("Relay support", () => { - it("Uses Relay generated .js files", () => { - var payload: MockPayload - var options = { - client: "test-1", - quiet: true, - path: "./src/__generated__", - url: "bogus", - send: (sendPayload: MockPayload, _opts: object) => { payload = sendPayload }, - } - return sync(options).then(function () { - expect(payload.operations).toMatchSnapshot() - }) - }) - - it("Uses relay --persist-output JSON files", () => { - var payload: MockPayload - var options = { - client: "test-1", - quiet: true, - relayPersistedOutput: "./src/__tests__/example-relay-persisted-queries.json", - url: "bogus", - send: (sendPayload: MockPayload, _opts: object) => { - payload = sendPayload - }, - } - return sync(options).then(function () { - return expect(payload.operations).toMatchSnapshot() - }) - }) - - it("Uses Apollo Android OperationOutput JSON files", () => { - var payload: MockPayload - var options = { - client: "test-1", - quiet: true, - apolloAndroidOperationOutput: "./src/__tests__/example-apollo-android-operation-output.json", - url: "bogus", - send: (sendPayload: MockPayload, _opts: object) => { - payload = sendPayload - }, - } - return sync(options).then(function () { - expect(payload.operations[0].alias).toEqual("aba626ea9bdf465954e89e5590eb2c1a") - return expect(payload.operations).toMatchSnapshot() - }) - }) - - it("Uses Apollo Codegen JSON files", () => { - var payload: MockPayload - var options = { - client: "test-1", - quiet: true, - apolloCodegenJsonOutput: "./src/__tests__/apolloExample/gen/output.json", - url: "bogus", - send: (sendPayload: MockPayload, _opts: object) => { - payload = sendPayload - }, - } - return sync(options).then(function () { - expect(payload.operations[0].alias).toEqual("22cc98c61c1402c92b230b7c515e07eb793a5152c388b015e86df4652ec58156") - return expect(payload.operations).toMatchSnapshot() - }) - }) - }) - - describe("Input files", () => { - it("Merges fragments and operations across files", () => { - var payload: MockPayload - var options = { - client: "test-1", - quiet: true, - path: "./src/__tests__/project/", - url: "bogus", - // mode: "project" is the default - send: (sendPayload: MockPayload, _opts: object) => { payload = sendPayload }, - } - return sync(options).then(function () { - expect(payload.operations).toMatchSnapshot() - }) - }) - - it("Uses mode: file to process each file separately", () => { - var payload: MockPayload - var options = { - client: "test-1", - quiet: true, - path: "./src/__tests__/project", - url: "bogus", - mode: "file", - send: (sendPayload: MockPayload, _opts: object) => { payload = sendPayload }, - } - return sync(options).then(function() { - expect(payload.operations).toMatchSnapshot() - }) - }) - }) - - describe("Promise result", () => { - it("Yields the payload and generated code", () => { - var options = { - client: "test-1", - path: "./src/__tests__/project", - url: "bogus", - quiet: true, - send: () => { }, - } - - return sync(options).then(function(ppayload: unknown) { - var payload = ppayload as MockPayload - expect(payload.operations.length).toEqual(5) - var generatedCode = fs.readFileSync("./src/OperationStoreClient.js", "utf8") - expect(payload.generatedCode).toEqual(generatedCode) - fs.unlinkSync("./src/OperationStoreClient.js") - }) - }) - }) - describe("Sync output", () => { - it("Generates a usable artifact for middleware", () => { - var options = { - client: "test-1", - path: "./src/__tests__/project", - url: "bogus", - quiet: true, - send: () => { }, - } - return sync(options).then(function() { - var generatedCode = fs.readFileSync("./src/OperationStoreClient.js", "utf8") - expect(generatedCode).toMatch('"GetStuff": "4568c28d403794e011363caf815ec827"') - expect(generatedCode).toMatch('module.exports = OperationStoreClient') - expect(generatedCode).toMatch('var _client = "test-1"') - fs.unlinkSync("./src/OperationStoreClient.js") - }) - }) - - it("Takes an outfile option", () => { - var options = { - client: "test-2", - path: "./src/__tests__/project", - url: "bogus", - quiet: true, - outfile: "__crazy_outfile.js", - send: () => { }, - } - return sync(options).then(function() { - var generatedCode = fs.readFileSync("./__crazy_outfile.js", "utf8") - expect(generatedCode).toMatch('"GetStuff": "4568c28d403794e011363caf815ec827"') - expect(generatedCode).toMatch('module.exports = OperationStoreClient') - expect(generatedCode).toMatch('var _client = "test-2"') - fs.unlinkSync("./__crazy_outfile.js") - }) - }) - - it("Can dump payload and outfile at the same time", () => { - var options = { - client: "test-2", - path: "./src/__tests__/project", - quiet: true, - outfile: "customOutfile.js", - dumpPayload: "customDumpPayload.js" - } - - return sync(options).then(function() { - var generatedCode = fs.readFileSync("./customOutfile.js", "utf8") - expect(generatedCode).toMatch('"GetStuff": "4568c28d403794e011363caf815ec827"') - expect(generatedCode).toMatch('module.exports = OperationStoreClient') - expect(generatedCode).toMatch('var _client = "test-2"') - - var generatedPayload = fs.readFileSync("./customDumpPayload.js", "utf8") - expect(generatedPayload).toMatchSnapshot() - fs.unlinkSync("./customOutfile.js") - fs.unlinkSync("./customDumpPayload.js") - }) - }) - - it("Skips outfile generation when using --persist-output artifact", () => { - var options = { - client: "test-2", - relayPersistedOutput: "./src/__tests__/example-relay-persisted-queries.json", - url: "bogus", - quiet: true, - send: () => { }, - } - return sync(options).then(function() { - // This is the default outfile: - var wasWritten = fs.existsSync("./src/OperationStoreClient.js") - expect(wasWritten).toBe(false) - }) - }) - - it("Skips outfile generation when using --apollo-android-operation-output artifact", () => { - var options = { - client: "test-2", - apolloAndroidOperationOutput: "./src/__tests__/example-apollo-android-operation-output.json", - url: "bogus", - quiet: true, - send: () => { }, - } - return sync(options).then(function() { - // This is the default outfile: - var wasWritten = fs.existsSync("./src/OperationStoreClient.js") - expect(wasWritten).toBe(false) - }) - }) - }) - - describe("Logging", () => { - it("Logs progress", () => { - var spy = (console.log as unknown) as MockedObject - - var options = { - client: "test-1", - path: "./src/__tests__/project", - url: "bogus", - send: () => { }, - } - return sync(options).then(function() { - expect(spy.mock.calls).toMatchSnapshot() - }) - }) - - it("Can be quieted with quiet: true", () => { - var spy = (console.log as unknown) as MockedObject - - var options = { - client: "test-1", - path: "./src/__tests__/project", - url: "bogus", - quiet: true, - send: () => { }, - } - return sync(options).then(function() { - expect(spy.mock.calls).toMatchSnapshot() - }) - }) - }) - - describe("Printing the result", () => { - function buildMockRespondingWith(status: number, data: object) { - return nock("http://example.com").post("/stored_operations/sync").reply(status, data) - } - - it("prints failure and sends the message to the promise", () => { - var spyConsoleLog = (console.log as unknown) as MockedObject - var spyConsoleError = (console.error as unknown) as MockedObject - - buildMockRespondingWith(422, { - errors: { "4568c28d403794e011363caf815ec827": ["something"] }, - failed: ["4568c28d403794e011363caf815ec827"], - added: ["defg"], - not_modified: [], - }) - - var options = { - client: "test-1", - path: "./src/__tests__/project", - url: "http://example.com/stored_operations/sync", - quiet: false, - } - - var syncPromise = sync(options) - - return syncPromise.catch((errmsg: string) => { - expect(errmsg).toEqual("Sync failed: GetStuff: something") - expect(spyConsoleLog.mock.calls).toMatchSnapshot() - expect(spyConsoleError.mock.calls).toMatchSnapshot() - jest.clearAllMocks(); - }) - }) - - it("prints success", () => { - var spyConsoleLog = (console.log as unknown) as MockedObject - var spyConsoleError = (console.error as unknown) as MockedObject - - buildMockRespondingWith(422, { - errors: {}, - failed: [], - added: ["defg"], - not_modified: ["xyz", "123"], - }) - - var options = { - client: "test-1", - path: "./src/__tests__/project", - url: "http://example.com/stored_operations/sync", - quiet: false, - } - - var syncPromise = sync(options) - - expect(spyConsoleLog.mock.calls).toMatchSnapshot() - jest.clearAllMocks(); - - return syncPromise.then(() => { - expect(spyConsoleLog.mock.calls).toMatchSnapshot() - expect(spyConsoleError.mock.calls).toMatchSnapshot() - jest.clearAllMocks(); - }) - }) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/cli.ts b/vendor/gems/graphql/javascript_client/src/cli.ts deleted file mode 100755 index 069457bcd3a..00000000000 --- a/vendor/gems/graphql/javascript_client/src/cli.ts +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env node -import parseArgs from "minimist" -import sync, { SyncOptions } from "./sync/index" -var argv = parseArgs(process.argv.slice(2)) - -if (argv.help || argv.h) { - console.log(`usage: graphql-ruby-client sync - - Read .graphql files and push the contained - operations to a GraphQL::Pro::OperationStore - -required arguments: - --url= URL where data should be POSTed - --client= Identifier for this client application - -optional arguments: - --path= Path to .graphql files (default is "./**/*.graphql") - --outfile= Target file for generated code - --outfile-type= Target type for generated code (default is "js") - --secret= HMAC authentication key - --relay-persisted-output= Path to a .json file from "relay-compiler ... --persist-output" - (Outfile generation is skipped by default.) - --apollo-codegen-json-output= Path to a .json file from "apollo client:codegen ... --target json" - (Outfile generation is skipped by default.) - --apollo-android-operation-output= Path to a .json file from Apollo-Android's "generateOperationOutput" feature. - (Outfile generation is skipped by default.) - --apollo-persisted-query-manifest= Path to a .json file from Apollo's "generate-persisted-query-manifest" tool. - (Outfile generation is skipped by default.) - --mode= Treat files like a certain kind of project: - relay: treat files like relay-compiler output - project: treat files like a cohesive project (fragments are shared, names must be unique) - file: treat each file like a stand-alone operation - - By default, this flag is set to: - - "relay" if "__generated__" in the path - - otherwise, "project" - --header=

    : Add a header to the outgoing HTTP request - (may be repeated) - --changeset-version= Populates \`context[:changeset_version]\` for this sync (for the GraphQL-Enterprise "Changesets" feature) - --add-typename Automatically adds the "__typename" field to your queries - --dump-payload= Print the HTTP Post data to this file, or to stdout if no filename is given - --quiet Suppress status logging - --verbose Print debug output - --help Print this message -`) -} else { - var commandName = argv._[0] - - if (commandName !== "sync") { - console.log("Only `graphql-ruby-client sync` is supported") - } else { - var parsedHeaders: {[key: string]: string} = {} - if (argv.header) { - if (typeof(argv.header) === "string") { - var headerParts = argv.header.split(":") - parsedHeaders[headerParts[0]] = headerParts[1] - } else { - argv.header.forEach((h: string) => { - var headerParts = h.split(":") - parsedHeaders[headerParts[0]] = headerParts[1] - }) - } - } - let syncOptions: SyncOptions = { - path: argv.path, - relayPersistedOutput: argv["relay-persisted-output"], - apolloCodegenJsonOutput: argv["apollo-codegen-json-output"], - apolloAndroidOperationOutput: argv["apollo-android-operation-output"], - apolloPersistedQueryManifest: argv["apollo-persisted-query-manifest"], - url: argv.url, - client: argv.client, - outfile: argv.outfile, - outfileType: argv["outfile-type"], - secret: argv.secret, - mode: argv.mode, - headers: parsedHeaders, - addTypename: argv["add-typename"], - quiet: argv.hasOwnProperty("quiet"), - verbose: argv.hasOwnProperty("verbose"), - changesetVersion: argv["changeset-version"], - } - - if ("dump-payload" in argv) { - syncOptions.dumpPayload = argv["dump-payload"] - } - - var result = sync(syncOptions) - - result.then(function() { - process.exit(0) - }).catch(function() { - // The error is logged by the function - process.exit(1) - }) - } -} diff --git a/vendor/gems/graphql/javascript_client/src/index.ts b/vendor/gems/graphql/javascript_client/src/index.ts deleted file mode 100644 index 3b8f15b734c..00000000000 --- a/vendor/gems/graphql/javascript_client/src/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import sync from "./sync" -import { generateClient } from "./sync/generateClient" -import ActionCableLink from "./subscriptions/ActionCableLink" -import PusherLink from "./subscriptions/PusherLink" -import AblyLink from "./subscriptions/AblyLink" -import addGraphQLSubscriptions from "./subscriptions/addGraphQLSubscriptions" -import createRelaySubscriptionHandler from "./subscriptions/createRelaySubscriptionHandler" - -export { - sync, - generateClient, - ActionCableLink, - PusherLink, - AblyLink, - addGraphQLSubscriptions, - createRelaySubscriptionHandler, -} diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/AblyLink.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/AblyLink.ts deleted file mode 100644 index 97ed81915b3..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/AblyLink.ts +++ /dev/null @@ -1,116 +0,0 @@ -// An Apollo Link for using graphql-pro's Ably subscriptions -// -// @example Adding subscriptions to a HttpLink -// // Load Ably and create a client -// var Ably = require('ably') -// // Be sure to create an API key with "Subscribe" and "Presence" permissions only, -// // and use that limited API key here: -// var ablyClient = new Ably.Realtime({ key: "yourapp.key:secret" }) -// -// // Build a combined link, initialize the client: -// const ablyLink = new AblyLink({ably: ablyClient}) -// const link = ApolloLink.from([authLink, ablyLink, httpLink]) -// const client = new ApolloClient(link: link, ...) -// -// @example Building a subscription, then subscribing to it -// subscription = client.subscribe({ -// variables: { room: roomName}, -// query: gql` -// subscription MessageAdded($room: String!) { -// messageWasAdded(room: $room) { -// room { -// messages { -// id -// body -// author { -// screenname -// } -// } -// } -// } -// } -// ` -// }) -// -// subscription.subscribe({ next: ({data, errors}) => { -// // Do something with `data` and/or `errors` -// }}) -// -import { ApolloLink, Observable, FetchResult, NextLink, Operation } from "@apollo/client/core" -import { Realtime } from "ably" - -type RequestResult = Observable, Record>> - -class AblyLink extends ApolloLink { - ably: Realtime - - constructor(options: { ably: Realtime }) { - super() - // Retain a handle to the Ably client - this.ably = options.ably - } - - request(operation: Operation, forward: NextLink): RequestResult { - return new Observable((observer) => { - // Check the result of the operation - forward(operation).subscribe({ next: (data) => { - // If the operation has the subscription header, it's a subscription - const subscriptionChannelConfig = this._getSubscriptionChannel(operation) - if (subscriptionChannelConfig.channel) { - // This will keep pushing to `.next` - this._createSubscription(subscriptionChannelConfig, observer) - } - else { - // This isn't a subscription, - // So pass the data along and close the observer. - observer.next(data) - observer.complete() - } - }}) - }) - } - - _getSubscriptionChannel(operation: Operation) { - const response = operation.getContext().response - // Check to see if the response has the header - const subscriptionChannel = response.headers.get("X-Subscription-ID") - // The server returns this header when encryption is enabled. - const cipherKey = response.headers.get("X-Subscription-Key") - return { channel: subscriptionChannel, key: cipherKey } - } - - _createSubscription(subscriptionChannelConfig: { channel: string, key: string }, observer: { next: Function, complete: Function}) { - const subscriptionChannel = subscriptionChannelConfig["channel"] - const subscriptionKey = subscriptionChannelConfig["key"] - const ablyOptions = subscriptionKey ? { cipher: { key: subscriptionKey } } : {} - const ablyChannel = this.ably.channels.get(subscriptionChannel, ablyOptions) - const ablyClientId = this.ably.auth.clientId - // Register presence, so that we can detect empty channels and clean them up server-side - if (ablyClientId) { - ablyChannel.presence.enter() - } else { - ablyChannel.presence.enterClient("graphql-subscriber", "subscribed") - } - // Subscribe for more update - ablyChannel.subscribe("update", function(message) { - var payload = message.data - const result = payload.result - if (result) { - // Send the new response to listeners - observer.next(result) - } - if (!payload.more) { - // This is the end, the server says to unsubscribe - if (ablyClientId) { - ablyChannel.presence.leave() - } else { - ablyChannel.presence.leaveClient("graphql-subscriber") - } - ablyChannel.unsubscribe() - observer.complete() - } - }) - } -} - -export default AblyLink diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableLink.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableLink.ts deleted file mode 100644 index 5d8ec844921..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableLink.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ApolloLink, Observable, FetchResult, Operation, NextLink } from "@apollo/client/core" -import type { Consumer } from "@rails/actioncable" -import { print } from "graphql" - -type RequestResult = FetchResult<{ [key: string]: any; }, Record, Record> -type ConnectionParams = object | ((operation: Operation) => object) - -class ActionCableLink extends ApolloLink { - cable: Consumer - channelName: string - actionName: string - connectionParams: ConnectionParams - - constructor(options: { - cable: Consumer, channelName?: string, actionName?: string, connectionParams?: ConnectionParams - }) { - super() - this.cable = options.cable - this.channelName = options.channelName || "GraphqlChannel" - this.actionName = options.actionName || "execute" - this.connectionParams = options.connectionParams || {} - } - - // Interestingly, this link does _not_ call through to `next` because - // instead, it sends the request to ActionCable. - request(operation: Operation, _next: NextLink): Observable { - return new Observable((observer) => { - var channelId = Math.round(Date.now() + Math.random() * 100000).toString(16) - var actionName = this.actionName - var connectionParams = (typeof this.connectionParams === "function") ? - this.connectionParams(operation) : this.connectionParams - var channel = this.cable.subscriptions.create(Object.assign({},{ - channel: this.channelName, - channelId: channelId - }, connectionParams), { - connected: function() { - this.perform( - actionName, - { - query: operation.query ? print(operation.query) : null, - variables: operation.variables, - // This is added for persisted operation support: - operationId: (operation as {operationId?: string}).operationId, - operationName: operation.operationName - } - ) - }, - received: function(payload) { - if (payload?.result?.data || payload?.result?.errors) { - observer.next(payload.result) - } - - if (!payload.more) { - observer.complete() - } - } - }) - // Make the ActionCable subscription behave like an Apollo subscription - return Object.assign(channel, {closed: false}) - }) - } -} - -export default ActionCableLink diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableSubscriber.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableSubscriber.ts deleted file mode 100644 index 75711734ec6..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/ActionCableSubscriber.ts +++ /dev/null @@ -1,81 +0,0 @@ -import printer from "graphql/language/printer" -import registry from "./registry" -import type { Consumer } from "@rails/actioncable" - -interface ApolloNetworkInterface { - applyMiddlewares: Function - query: (req: object) => Promise - _opts: any -} - -class ActionCableSubscriber { - _cable: Consumer - _networkInterface: ApolloNetworkInterface - _channelName: string - - constructor(cable: Consumer, networkInterface: ApolloNetworkInterface, channelName?: string) { - this._cable = cable - this._networkInterface = networkInterface - this._channelName = channelName || "GraphqlChannel" - } - - /** - * Send `request` over ActionCable (`registry._cable`), - * calling `handler` with any incoming data. - * Return the subscription so that the registry can unsubscribe it later. - * @param {Object} registry - * @param {Object} request - * @param {Function} handler - * @return {ID} An ID for unsubscribing - */ - subscribe(request: any, handler: any) { - var networkInterface = this._networkInterface - // unique-ish - var channelId = Math.round(Date.now() + Math.random() * 100000).toString(16) - var channel = this._cable.subscriptions.create({ - channel: this._channelName, - channelId: channelId, - }, { - // After connecting, send the data over ActionCable - connected: function() { - // applyMiddlewares code is inspired by networkInterface internals - var opts = Object.assign({}, networkInterface._opts) - networkInterface - .applyMiddlewares({request: request, options: opts}) - .then(function() { - var queryString = request.query ? printer.print(request.query) : null - var operationName = request.operationName - var operationId = request.operationId - var variables = JSON.stringify(request.variables) - var channelParams = Object.assign({}, request, { - query: queryString, - variables: variables, - operationId: operationId, - operationName: operationName, - }) - channel.perform("execute", channelParams) - }) - }, - // Payload from ActionCable should have at least two keys: - // - more: true if this channel should stay open - // - result: the GraphQL response for this result - received: function(payload) { - var result = payload.result - if (result) { - handler(result.errors, result.data) - } - if (!payload.more) { - registry.unsubscribe(id) - } - }, - }) - var id = registry.add(channel) - return id - } - - unsubscribe(id: number) { - registry.unsubscribe(id) - } -} - -export default ActionCableSubscriber diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/PusherLink.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/PusherLink.ts deleted file mode 100644 index 049bae1b174..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/PusherLink.ts +++ /dev/null @@ -1,169 +0,0 @@ -// An Apollo Link for using graphql-pro's Pusher subscriptions -// -// @example Adding subscriptions to a HttpLink -// // Load Pusher and create a client -// import Pusher from "pusher-js" -// var pusherClient = new Pusher("your-app-key", { cluster: "us2" }) -// -// // Build a combined link, initialize the client: -// const pusherLink = new PusherLink({pusher: pusherClient}) -// const link = ApolloLink.from([authLink, pusherLink, httpLink]) -// const client = new ApolloClient(link: link, ...) -// -// @example Building a subscription, then subscribing to it -// subscription = client.subscribe({ -// variables: { room: roomName}, -// query: gql` -// subscription MessageAdded($room: String!) { -// messageWasAdded(room: $room) { -// room { -// messages { -// id -// body -// author { -// screenname -// } -// } -// } -// } -// } -// ` -// }) -// -// subscription.subscribe({ next: ({data, errors}) => { -// // Do something with `data` and/or `errors` -// }}) -// -import { ApolloLink, Observable, Observer, Operation, NextLink, FetchResult } from "@apollo/client/core" -import Pusher from "pusher-js" - -type RequestResult = FetchResult<{ [key: string]: any; }, Record, Record> - -type Subscription = { - closed: boolean; - unsubscribe(): void; -} - -class PusherLink extends ApolloLink { - pusher: Pusher - decompress: (result: string) => any - - constructor(options: { pusher: Pusher, decompress?: (result: string) => any}) { - super() - // Retain a handle to the Pusher client - this.pusher = options.pusher - if (options.decompress) { - this.decompress = options.decompress - } else { - this.decompress = function(_result: string) { - throw new Error("Received compressed_result but PusherLink wasn't configured with `decompress: (result: string) => any`. Add this configuration.") - } - } - } - - request(operation: Operation, forward: NextLink): Observable { - const subscribeObservable = new Observable((_observer: any) => { }) - // Capture the super method - const prevSubscribe = subscribeObservable.subscribe.bind(subscribeObservable) - // Override subscribe to return an `unsubscribe` object, see - // https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/client.ts#L182-L212 - subscribeObservable.subscribe = ( - observerOrNext: Observer | ((value: RequestResult) => void), - onError?: (error: any) => void, - onComplete?: () => void - ): Subscription => { - // Call super - if (typeof(observerOrNext) == "function") { - prevSubscribe(observerOrNext, onError, onComplete) - } else { - prevSubscribe(observerOrNext) - } - const observer = getObserver(observerOrNext, onError, onComplete) - var subscriptionChannel: string - // Check the result of the operation - const resultObservable = forward(operation) - // When the operation is done, try to get the subscription ID from the server - resultObservable.subscribe({ next: (data: any) => { - // If the operation has the subscription header, it's a subscription - const response = operation.getContext().response - // Check to see if the response has the header - subscriptionChannel = response.headers.get("X-Subscription-ID") - if (subscriptionChannel) { - // Set up the pusher subscription for updates from the server - const pusherChannel = this.pusher.subscribe(subscriptionChannel) - // Pass along the initial payload: - if (data.data && Object.keys(data.data).length > 0) { - observer.next(data) - } - // Subscribe for more update - pusherChannel.bind("update", (payload: any) => { - this._onUpdate(subscriptionChannel, observer, payload) - }) - } else { - // This isn't a subscription, - // So pass the data along and close the observer. - observer.next(data) - observer.complete() - } - }, - error: observer.error, - // complete: observer.complete Don't pass this because Apollo unsubscribes if you do - }) - - // Return an object that will unsubscribe _if_ the query was a subscription. - return { - closed: false, - unsubscribe: () => { - subscriptionChannel && this.pusher.unsubscribe(subscriptionChannel) - } - } - } - return subscribeObservable - } - - _onUpdate(subscriptionChannel: string, observer: { next: Function, complete: Function }, payload: {more: boolean, compressed_result?: string, result?: object}): void { - let result: any - if (payload.compressed_result) { - result = this.decompress(payload.compressed_result) - } else { - result = payload.result - } - if (result) { - // Send the new response to listeners - observer.next(result) - } - if (!payload.more) { - // This is the end, the server says to unsubscribe - this.pusher.unsubscribe(subscriptionChannel) - observer.complete() - } - } -} - - - -// Turn `subscribe` arguments into an observer-like thing, see getObserver -// https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/client.ts#L347-L361 -function getObserver( - observerOrNext: Function | Observer, - onError?: (e: Error) => void, - onComplete?: () => void, -) { - if (typeof observerOrNext === 'function') { - // Duck-type an observer - return { - next: (v: T) => observerOrNext(v), - error: (e: Error) => onError && onError(e), - complete: () => onComplete && onComplete(), - } - } else { - // Make an object that calls to the given object, with safety checks - return { - next: (v: T) => observerOrNext.next && observerOrNext.next(v), - error: (e: Error) => observerOrNext.error && observerOrNext.error(e), - complete: () => observerOrNext.complete && observerOrNext.complete(), - } - } -} - -export default PusherLink diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/PusherSubscriber.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/PusherSubscriber.ts deleted file mode 100644 index 6be33e97f0f..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/PusherSubscriber.ts +++ /dev/null @@ -1,83 +0,0 @@ -import registry from "./registry" -import Pusher from "pusher-js" - -interface ApolloNetworkInterface { - use: Function - useAfter: Function - query: (req: object) => Promise -} -/** - * Make a new subscriber for `addGraphQLSubscriptions` - * - * @param {Pusher} pusher -*/ - -class PusherSubscriber { - _pusher: Pusher - _networkInterface: ApolloNetworkInterface - _decompress: (compressed: string) => any - - constructor(pusher: Pusher, networkInterface: ApolloNetworkInterface, decompress?: (compressed: string) => any) { - this._pusher = pusher - this._networkInterface = networkInterface - this._decompress = decompress || function(_compressed) { throw new Error("Received compressed_result but this addGraphQLSubscriptions wasn't configured with `decompress: (result: string) => any`. Add this configuration.")} - // This is a bit tricky: - // only the _request_ is passed to the `subscribe` function, s - // so we have to attach the subscription id to the `request`. - // However, the request is _not_ available in the afterware function. - // So: - // - Add the request to `options` so it's available in afterware - // - In the afterware, update the request to hold the header value - // - Finally, in `subscribe`, read the subscription ID off of `request` - networkInterface.use([{ - applyMiddleware: function({request, options}: any, next: Function) { - options.request = request - next() - } - }]) - networkInterface.useAfter([{ - applyAfterware: function({response, options}: any, next: Function) { - options.request.__subscriptionId = response.headers.get("X-Subscription-ID") - next() - } - }]) - } - // Implement the Apollo subscribe API - subscribe(request: {__subscriptionId: string}, handler: any) { - var pusher = this._pusher - var networkInterface = this._networkInterface - var decompress = this._decompress - var subscription = { - _channelName: "", // set after the successful POST - unsubscribe: function() { - if (this._channelName) { - pusher.unsubscribe(this._channelName) - } - } - } - var id = registry.add(subscription) - // Send the subscription as a query - // Get the channel ID from the response headers - networkInterface.query(request).then(function(_executionResult: any){ - var subscriptionChannel = request.__subscriptionId - subscription._channelName = subscriptionChannel - var pusherChannel = pusher.subscribe(subscriptionChannel) - // When you get an update form Pusher, send it to Apollo - pusherChannel.bind("update", function(payload: any) { - var result = payload.compressed_result ? decompress(payload.compressed_result) : payload.result - if (result) { - handler(result.errors, result.data) - } - if (!payload.more) { - registry.unsubscribe(id) - } - }) - }) - return id - } - - unsubscribe(id: number) { - registry.unsubscribe(id) - } -} -export default PusherSubscriber diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/SubscriptionExchange.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/SubscriptionExchange.ts deleted file mode 100644 index cbf0418daa3..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/SubscriptionExchange.ts +++ /dev/null @@ -1,143 +0,0 @@ -import Pusher from "pusher-js" -import Urql from "urql" -import { Consumer, Subscription } from "@rails/actioncable" - -type ForwardCallback = (...args: any[]) => void - -const SubscriptionExchange = { - create(options: { pusher?: Pusher, consumer?: Consumer, channelName?: string }) { - if (options.pusher) { - return createPusherSubscription(options.pusher) - } else if (options.consumer) { - return createUrqlActionCableSubscription(options.consumer, options?.channelName) - } else { - throw new Error("Either `pusher: ...` or `consumer: ...` is required.") - } - } -} - - -function createPusherSubscription(pusher: Pusher) { - return function(operation: Urql.Operation) { - // urql will call `.subscribed` on the returned object: - // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L68-L73 - // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L82-L97 - return { - subscribe: ({next, error, complete}: { next: ForwardCallback, error: ForwardCallback, complete: ForwardCallback}) => { - // Somehow forward the operation to be POSTed to the server, - // I don't see an option for passing this on to the `fetchExchange` - const fetchBody = JSON.stringify({ - query: operation.query, - variables: operation.variables, - }) - var pusherChannelName: string - const subscriptionId = "" + operation.key - var fetchOptions = operation.context.fetchOptions - if (typeof fetchOptions === "function") { - fetchOptions = fetchOptions() - } else if (fetchOptions == null) { - fetchOptions = {} - } - - const headers = { - ...(fetchOptions.headers), - ...{ - 'Content-Type': 'application/json', - 'X-Subscription-ID': subscriptionId - } - } - - const defaultFetchOptions = { method: "POST" } - const mergedFetchOptions = { - ...defaultFetchOptions, - ...fetchOptions, - body: fetchBody, - headers: headers, - } - const fetchFn = operation.context.fetch || fetch - fetchFn(operation.context.url, mergedFetchOptions) - .then((fetchResult) => { - // Get the server-provided subscription ID - pusherChannelName = fetchResult.headers.get("X-Subscription-ID") as string - // Set up a subscription to Pusher, forwarding updates to - // the `next` function provided by urql - const pusherChannel = pusher.subscribe(pusherChannelName) - pusherChannel.bind("update", (payload: {result: object, more: boolean}) => { - // Here's an update to this subscription, - // pass it on: - if (payload.result) { - next(payload.result) - } - // If the server signals that this is the end, - // then unsubscribe the client: - if (!payload.more) { - complete() - } - }) - // Continue processing the initial result for the subscription - return fetchResult.json() - }) - .then((jsonResult) => { - // forward the initial result to urql - next(jsonResult) - }) - .catch(error) - - // urql will call `.unsubscribe()` if it's returned here: - // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L102 - return { - unsubscribe: () => { - // When requested by urql, disconnect from this channel - pusherChannelName && pusher.unsubscribe(pusherChannelName) - } - } - } - } - } -} - - - -function createUrqlActionCableSubscription(consumer: Consumer, channelName: string = "GraphqlChannel") { - return function (operation: Urql.Operation) { - const subscribe = ({ next, error, complete }: { next: ForwardCallback, error: ForwardCallback, complete: ForwardCallback }) => { - let subscribed = false; - - const subscription: Subscription = consumer.subscriptions.create(channelName, { - connected() { - subscription.perform("execute", { query: operation.query, variables: operation.variables }); - subscribed = true; - }, - received(data: any) { - if (data?.result?.errors) { - error(data.errors); - } - if (data?.result?.data) { - next(data.result); - } - if (!data.more && subscribed) { - complete(); - } - } - }); - - // urql will call `.unsubscribe()` if it's returned here: - // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L102 - const unsubscribe = () => { - if (subscribed) { - subscribed = false; - subscription?.unsubscribe(); - } - }; - - return { unsubscribe }; - }; - - // urql will call `.subscribed` on the returned object: - // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L68-L73 - // https://github.com/FormidableLabs/urql/blob/f89cfd06d9f14ae9cb3be10b21bd5cbd12ca275c/packages/core/src/exchanges/subscription.ts#L82-L97 - return { subscribe }; - }; -} - -export default SubscriptionExchange diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/AblyLinkTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/AblyLinkTest.ts deleted file mode 100644 index 636583e85d4..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/AblyLinkTest.ts +++ /dev/null @@ -1,136 +0,0 @@ -import AblyLink from "../AblyLink" -import { Realtime } from "ably" -import { Operation } from "@apollo/client/core" -import { parse } from "graphql" -function createAbly() { - const _channels: {[key: string]: any } = {} - const log: any[] = [] - - const ably = { - _channels: _channels, - log: log, - auth: { - clientId: null, - }, - channels: { - get(channelName: string) { - return _channels[channelName] ||= { - _listeners: [] as [string, Function][], - name: channelName, - presence: { - enterClient(_clientName: string, _status: string) {}, - leaveClient(_clientName: string) {}, - }, - detach(callback: Function) { - callback() - }, - subscribe(eventName: string, callback: Function) { - log.push(["subscribe", channelName, eventName]) - this._listeners.push([eventName, callback]) - }, - unsubscribe(){ - log.push(["unsubscribe", channelName]) - } - } - }, - release(channelName: string) { - delete _channels[channelName] - } - }, - __testTrigger(channelName: string, eventName: string, data: any) { - const channel = this.channels.get(channelName) - const handler = channel._listeners.find((l: any) => l[0] == eventName) - if (handler) { - handler[1](data) - } - } - } - - return (ably as unknown) as Realtime -} - -function createOperation(options: { subscriptionId: string | null }) { - return ({ - query: parse("subscription { foo { bar } }"), - variables: { a: 1 }, - operationId: "operationId", - operationName: "operationName", - getContext: () => { - return { - response: { - headers: { - get: (key: string) => { - if (key == "X-Subscription-ID") { - return options.subscriptionId - } else { - return null - } - } - } - } - } - } - } as unknown) as Operation -} -describe("AblyLink", () => { - test("delegates to Ably", () => { - var mockAbly = createAbly() - var log = (mockAbly as any).log - var operation = createOperation({subscriptionId: "sub-1234"}) - - var nextLink = (operation: any) => { - log.push(["forward", operation.operationName]) - return { - subscribe(info: any) { - info.next() - } - } as any - } - - var observable = new AblyLink({ ably: mockAbly}).request(operation, nextLink) - - observable.subscribe(function(result: any) { - log.push(["received", result]) - }); - - (mockAbly as any).__testTrigger("sub-1234", "update", { data: { result: { data: null }, more: true} }); - (mockAbly as any).__testTrigger("sub-1234", "update", { data: { result: { data: "data 1" }, more: true} }); - (mockAbly as any).__testTrigger("sub-1234", "update", { data: { result: { data: "data 2" }, more: false} }); - - expect(log).toEqual([ - ["forward", "operationName"], - ["subscribe", "sub-1234", "update"], - ["received", { data: null }], - ["received", { data: "data 1" }], - ["received", { data: "data 2" }], - ["unsubscribe", "sub-1234"] - ]) - }) - - test("it doesn't call ably when the subscription header isn't present", () => { - var mockAbly = createAbly() - var log = (mockAbly as any).log - var operation = createOperation({subscriptionId: null}) - - var nextLink = (operation: any) => { - log.push(["forward", operation.operationName]) - return { - subscribe(info: any) { - info.next() - } - } as any - } - - var observable = new AblyLink({ ably: mockAbly}).request(operation, nextLink) - - observable.subscribe(function(result: any) { - log.push(["received", result]) - }); - - (mockAbly as any).__testTrigger("sub-1234", "update", { data: { result: { data: null }, more: true} }); - (mockAbly as any).__testTrigger("sub-1234", "update", { data: { result: { data: "data 1" }, more: true} }); - (mockAbly as any).__testTrigger("sub-1234", "update", { data: { result: { data: "data 2" }, more: false} }); - - expect(log).toEqual([["forward", "operationName"]]) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts deleted file mode 100644 index 4ba63f6846b..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/ActionCableLinkTest.ts +++ /dev/null @@ -1,174 +0,0 @@ -import ActionCableLink from "../ActionCableLink" -import { parse } from "graphql" -import type { Consumer } from "@rails/actioncable" -import { Operation } from "@apollo/client/core" - -describe("ActionCableLink", () => { - var log: any[] - var cable: any - var options: any - var query: any - var operation: Operation - - beforeEach(() => { - log = [] - cable = { - subscriptions: { - create: function(channelName: string | object, options: {connected: Function, received: Function}) { - var channel = channelName - var params = typeof channel === "object" ? channel : { channel } - var alreadyConnected = false - var subscription = Object.assign( - Object.create({ - perform: function(actionName: string, options: object) { - log.push(["perform", { actionName: actionName, options: options }]) - }, - unsubscribe: function() { - log.push(["unsubscribe"]) - } - }), - { params }, - options - ) - - subscription.connected = subscription.connected.bind(subscription) - var received = subscription.received - subscription.received = function(data: any) { - if (!alreadyConnected) { - alreadyConnected = true - subscription.connected() - } - received(data) - } - subscription.__proto__.unsubscribe = subscription.__proto__.unsubscribe.bind(subscription) - return subscription - } - } - } - options = { - cable: (cable as unknown) as Consumer - } - - query = parse("subscription { foo { bar } }") - - operation = ({ - query: query, - variables: { a: 1 }, - operationId: "operationId", - operationName: "operationName" - } as unknown) as Operation - }) - - it("delegates to the cable", () => { - var observable = new ActionCableLink(options).request(operation, null as any) - - // unpack the underlying subscription - var subscription: any = (observable.subscribe(function(result: any) { - log.push(["received", result]) - }) as any)._cleanup - - subscription.received({ - result: { - data: null - }, - more: true - }) - - subscription.received({ - result: { - data: "data 1" - }, - more: true - }) - - subscription.received({ - result: { - data: "data 2" - }, - more: false - }) - - expect(log).toEqual([ - [ - "perform", { - actionName: "execute", - options: { - query: "subscription {\n foo {\n bar\n }\n}", - variables: { a: 1 }, - operationId: "operationId", - operationName: "operationName" - } - } - ], - ["received", { data: "data 1" }], - ["received", { data: "data 2" }], - ["unsubscribe"] - ]) - }) - - it("delegates a manual unsubscribe to the cable", () => { - var observable = new ActionCableLink(options).request(operation, null as any) - - // unpack the underlying subscription - var subscription: any = (observable.subscribe(function(result: any) { - log.push(["received", result]) - }) as any)._cleanup - - subscription.received({ - result: { - data: null - }, - more: true - }) - - subscription.received({ - result: { - data: "data 1" - }, - more: true - }) - - subscription.unsubscribe() - - expect(log).toEqual([ - [ - "perform", { - actionName: "execute", - options: { - query: "subscription {\n foo {\n bar\n }\n}", - variables: { a: 1 }, - operationId: "operationId", - operationName: "operationName" - } - } - ], - ["received", { data: "data 1" }], - ["unsubscribe"] - ]) - }) - - it("forward object connectionParams to subscription creation", () => { - var observable = new ActionCableLink(Object.assign(options, { connectionParams: { test: 1 } })). - request(operation, null as any) - - // unpack the underlying subscription - var subscription: any = (observable.subscribe(() => null) as any)._cleanup - - subscription.unsubscribe() - - expect(subscription.params["test"]).toEqual(1) - }) - - it("calls connectionParams during subscription creation to fetch additional params", () => { - var observable = new ActionCableLink( - Object.assign(options, { connectionParams: () => ({ test: 1 })} ) - ).request(operation, null as any) - - // unpack the underlying subscription - var subscription: any = (observable.subscribe(() => null) as any)._cleanup - - subscription.unsubscribe() - - expect(subscription.params["test"]).toEqual(1) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/PusherLinkTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/PusherLinkTest.ts deleted file mode 100644 index f515370e2a7..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/PusherLinkTest.ts +++ /dev/null @@ -1,293 +0,0 @@ -import PusherLink from "../PusherLink" -import { parse } from "graphql" -import Pusher from "pusher-js" -import { Operation } from "@apollo/client/core" -import pako from 'pako' - -type MockChannel = { - bind: (action: string, handler: Function) => void, -} - -describe("PusherLink", () => { - var channelName = "abcd-efgh" - var log: any[] - var pusher: any - var options: any - var link: any - var query: any - var operation: Operation - - beforeEach(() => { - log = [] - pusher = { - _channels: {}, - trigger: function(channel: string, event: string, data: any) { - var handlers = this._channels[channel] - if (handlers) { - handlers.forEach(function(handler: [string, Function]) { - if (handler[0] == event) { - handler[1](data) - } - }) - } - }, - subscribe: function(channel: string): MockChannel { - log.push(["subscribe", channel]) - var handlers = this._channels[channel] - if (!handlers) { - handlers = this._channels[channel] = [] - } - - return { - bind: (action: string, handler: Function): void => { - handlers.push([action, handler]) - } - } - }, - unsubscribe: (channel: string): void => { - log.push(["unsubscribe", channel]) - }, - } - - options = { - pusher: (pusher as unknown) as Pusher - } - link = new PusherLink(options) - - query = parse("subscription { foo { bar } }") - - operation = ({ - query: query, - variables: { a: 1 }, - operationId: "operationId", - operationName: "operationName", - getContext: () => { - return { - response: { - headers: { - get: (headerName: string) => { - if (headerName == "X-Subscription-ID") { - return channelName - } else { - throw "Unsupported header name: " + headerName - } - } - } - } - } - } - } as unknown) as Operation - }) - - it("forwards errors to error handlers", () => { - let passedErrorHandler: Function = () => {} - - var observable = link.request(operation, function(_operation: Operation): any { - return { - subscribe: (options: { next: Function, error: Function, complete: Function }): void => { - passedErrorHandler = options.error - {} - } - } - }) - - let errorHandlerWasCalled = false - function createdErrorHandler(_err: Error) { - errorHandlerWasCalled = true - } - - observable.subscribe(function(result: any) { - log.push(["received", result]) - }, createdErrorHandler) - - if (passedErrorHandler) { - passedErrorHandler(new Error) - } - - expect(errorHandlerWasCalled).toBe(true) - }) - - it("doesn't call the link request's `complete` handler because otherwise Apollo would clean up subscriptions", () => { - let passedComplete: Function = () => {} - - var observable = link.request(operation, function(_operation: Operation): any { - return { - subscribe: (options: { next: Function, error: Function, complete: Function }): void => { - passedComplete = options.complete - {} - } - } - }) - - observable.subscribe(function(result: any) { - log.push(["received", result]) - }, null, function() { log.push(["completed"])}) - - expect(log).toEqual([]) - expect(passedComplete).toBeUndefined() - }) - - it("delegates to pusher", () => { - var requestFinished: Function = () => {} - - var observable = link.request(operation, function(_operation: Operation): any { - return { - subscribe: (options: { next: Function }): void => { - requestFinished = options.next - } - } - }) - - // unpack the underlying subscription - observable.subscribe(function(result: any) { - log.push(["received", result]) - }) - - // Pretend the HTTP link finished - requestFinished({ data: "initial payload" }) - - pusher.trigger(channelName, "update", { - result: { - data: "data 1" - }, - more: true - }) - - pusher.trigger(channelName, "update", { - result: { - data: "data 2" - }, - more: false - }) - - expect(log).toEqual([ - ["subscribe", "abcd-efgh"], - ["received", { data: "initial payload"}], - ["received", { data: "data 1" }], - ["received", { data: "data 2" }], - ["unsubscribe", "abcd-efgh"] - ]) - }) - - it("delegates a manual unsubscribe to pusher", () => { - var requestFinished: Function = () => {} - - var observable = link.request(operation, function(_operation: Operation): any { - return { - subscribe: (options: { next: Function }): void => { - requestFinished = options.next - } - } - }) - - // unpack the underlying subscription - var subscription = observable.subscribe(function(result: any) { - log.push(["received", result]) - }) - - // Pretend the HTTP link finished - requestFinished({ data: "initial payload" }) - - pusher.trigger(channelName, "update", { - result: { - data: "data 1" - }, - more: true - }) - - subscription.unsubscribe() - - expect(log).toEqual([ - ["subscribe", "abcd-efgh"], - ["received", { data: "initial payload"}], - ["received", { data: "data 1" }], - ["unsubscribe", "abcd-efgh"] - ]) - }) - - it("doesn't send empty initial responses", () => { - var requestFinished: Function = () => {} - - var observable = link.request(operation, function(_operation: Operation): any { - return { - subscribe: (options: { next: Function }): void => { - requestFinished = options.next - } - } - }) - - // unpack the underlying subscription - var subscription = observable.subscribe(function(result: any) { - log.push(["received", result]) - }) - - // Pretend the HTTP link finished - requestFinished({ data: null }) - - pusher.trigger(channelName, "update", { - result: { - data: "data 1" - }, - more: true - }) - - subscription.unsubscribe() - - expect(log).toEqual([ - ["subscribe", "abcd-efgh"], - ["received", { data: "data 1" }], - ["unsubscribe", "abcd-efgh"] - ]) - }) - - - it("throws an error when no `decompress:` is configured", () => { - const link = new PusherLink({ - pusher: new Pusher("123"), - }) - - const observer = { - next: (_result: object) => {}, - complete: () => {}, - } - - const payload = { - more: true, - compressed_result: "abcdef", - } - - expect(() => { - link._onUpdate("abc", observer, payload) - }).toThrow("Received compressed_result but PusherLink wasn't configured with `decompress: (result: string) => any`. Add this configuration.") - }) - - it("decompresses compressed_result", () => { - const link = new PusherLink({ - pusher: new Pusher("123"), - decompress: (compressed) => { - const buff = Buffer.from(compressed, 'base64'); - return JSON.parse(pako.inflate(buff, { to: 'string' })); - }, - }) - - const results: Array = [] - - const observer = { - next: (result: object) => { results.push(result) }, - complete: () => { results.push("complete") }, - } - - const compressedData = pako.deflate(JSON.stringify({ a: 1, b: 2})) - // Browsers have `TextEncoder` for this - const compressedStr = Buffer.from(compressedData).toString("base64") - const payload = { - more: true, - compressed_result: compressedStr, - } - - // Send a dummy payload and then terminate the subscription - link._onUpdate("abc", observer, payload) - link._onUpdate("abc", observer, { more: false }) - expect(results).toEqual([{a: 1, b: 2}, "complete"]) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/SubscriptionExchangeTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/SubscriptionExchangeTest.ts deleted file mode 100644 index b68068c4753..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/SubscriptionExchangeTest.ts +++ /dev/null @@ -1,167 +0,0 @@ -import SubscriptionExchange from "../SubscriptionExchange" -import Pusher from "pusher-js" -import Urql from "urql" -import {parse} from "graphql" -import { nextTick } from "process" -import { Consumer } from "@rails/actioncable" - -type MockChannel = { - bind: (action: string, handler: Function) => void, -} - -describe("SubscriptionExchange with Pusher", () => { - var channelName = "1234" - var log: any[] - var pusher: any - var options: any - var pusherExchange: any - var operation: any - - beforeEach(() => { - log = [] - pusher = { - _channels: {}, - trigger: function(channel: string, event: string, data: any) { - var handlers = this._channels[channel] - if (handlers) { - handlers.forEach(function(handler: [string, Function]) { - if (handler[0] == event) { - handler[1](data) - } - }) - } - }, - subscribe: function(channel: string): MockChannel { - log.push(["subscribe", channel]) - var handlers = this._channels[channel] - if (!handlers) { - handlers = this._channels[channel] = [] - } - - return { - bind: (action: string, handler: Function): void => { - handlers.push([action, handler]) - } - } - }, - unsubscribe: (channel: string): void => { - delete pusher._channels[channel] - log.push(["unsubscribe", channel]) - }, - } - - options = { - pusher: (pusher as unknown) as Pusher - } - pusherExchange = SubscriptionExchange.create(options) - - operation = { - query: parse("{ foo { bar } }"), - variables: {}, - key: Number(channelName), - context: { - url: "/graphql", - requestPolicy: "network-only", - fetch: () => { - var headers = new Headers - headers.append("X-Subscription-ID", channelName) - const jsonData = { data: { foo: "bar" }} - return Promise.resolve(({ - headers: headers, - json: () => { return jsonData } - } as unknown) as Response) - } - }, - kind: "subscription", - } as Urql.Operation - }) - - it("calls through to handlers and can be unsubscribed", () => { - const subscriber = pusherExchange(operation) - const next = (data: any) => { log.push(["next", data]) } - const error = (err: any) => { log.push(["error", err]) } - const complete = (data: any) => { log.push(["complete", data]) } - const subscription = subscriber.subscribe({ next, error, complete }) - return new Promise((resolve, _reject) => { - nextTick(() => { - pusher.trigger(channelName, { result: {}, more: true }) - expect(Object.keys(pusher._channels)).toEqual([channelName]) - subscription.unsubscribe() - expect(Object.keys(pusher._channels)).toEqual([]) - const expectedLog = [ - ["subscribe", "1234"], - ["next", { data: { foo: "bar" } }], - ["unsubscribe", "1234"] - ] - expect(log).toEqual(expectedLog) - resolve(true) - }) - }) - - }) -}) - -describe("SubscriptionExchange with ActionCable", () => { - it("calls through to handlers", () => { - var handlers: any - var log: [string, any][]= [] - - var dummyActionCableConsumer = { - subscriptions: { - create: (channelName: string, newHandlers: any) => { - log.push(["create", channelName]) - handlers = newHandlers - return { - perform: (evt: string, data: any) => { - log.push([evt, data]) - }, - unsubscribe: () => { - log.push(["unsubscribed", null]) - } - } - } - } - } - - var options = { - consumer: (dummyActionCableConsumer as unknown) as Consumer, - channelName: "CustomChannel" - } - - var exchange = SubscriptionExchange.create(options); - var parsedQuery = parse("{ foo { bar } }") - var operation = { - query: parsedQuery, - variables: {}, - context: { - url: "/graphql", - requestPolicy: "network-only", - }, - kind: "subscription", - } as Urql.Operation - - var subscriber = exchange(operation) - const next = (data: any) => { log.push(["next", data]) } - const error = (err: any) => { log.push(["error", err]) } - const complete = (data: any) => { log.push(["complete", data]) } - const subscription = subscriber.subscribe({ next, error, complete }) - - - return new Promise((resolve, _reject) => { - nextTick(() => { - handlers.connected() // trigger the GraphQL send - handlers.received({ result: { data: { a: "1" } }, more: false }) - subscription.unsubscribe() - const expectedLog = [ - ["create", "CustomChannel"], - ["execute", { query: parsedQuery, variables: {} }], - ["next", { data: { a: "1" } }], - ["complete", undefined], - ["unsubscribed", null], - ] - expect(log).toEqual(expectedLog) - resolve(true) - }) - }) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/addGraphQLSubscriptionsTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/addGraphQLSubscriptionsTest.ts deleted file mode 100644 index 8bce5287665..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/addGraphQLSubscriptionsTest.ts +++ /dev/null @@ -1,26 +0,0 @@ -import addGraphQLSubscriptions from "../addGraphQLSubscriptions" - -describe("addGraphQLSubscriptions", () => { - it("delegates to the subscriber", () => { - var state: {[key: string]: string} = {} - var subscriber = { - subscribe: function(req: string, handler: string) { - state[req] = handler - return req + "/" + handler - }, - unsubscribe(id: string) { - var key = id.split("/")[0] - delete state[key] - } - } - - var dummyNetworkInterface = addGraphQLSubscriptions({}, {subscriber: subscriber}) - - var id = dummyNetworkInterface.subscribe("abc", "def") - expect(id).toEqual("abc/def") - expect(Object.keys(state).length).toEqual(1) - expect(state["abc"]).toEqual("def") - dummyNetworkInterface.unsubscribe(id) - expect(Object.keys(state).length).toEqual(0) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyFetcherTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyFetcherTest.ts deleted file mode 100644 index f256ec53a9f..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyFetcherTest.ts +++ /dev/null @@ -1,105 +0,0 @@ -import createAblyFetcher from "../createAblyFetcher" -import { Realtime } from "ably" - -function createAbly() { - const _channels: {[key: string]: any } = {} - - const ably = { - _channels: _channels, - channels: { - get(channelName: string) { - return _channels[channelName] ||= { - _listeners: [] as [string, Function][], - name: channelName, - presence: { - enterClient(_clientName: string, _status: string) {}, - leaveClient(_clientName: string) {}, - }, - detach(callback: Function) { - callback() - }, - subscribe(eventName: string, callback: Function) { - this._listeners.push([eventName, callback]) - }, - unsubscribe(){} - } - }, - release(channelName: string) { - delete _channels[channelName] - } - }, - __testTrigger(channelName: string, eventName: string, data: any) { - const channel = this.channels.get(channelName) - const handler = channel._listeners.find((l: any) => l[0] == eventName) - if (handler) { - handler[1](data) - } - } - } - - return ably -} - - -describe("createAblyFetcher", () => { - it("yields updates for subscriptions", () => { - const ably = createAbly() - - const fetchLog: any[] = [] - const dummyFetch = function(url: string, fetchArgs: any) { - fetchLog.push([url, fetchArgs.customOpt]) - const dummyResponse = { - json: () => { - return { - data: { - hi: "First response" - } - } - }, - headers: { - get() { - return fetchArgs.body.includes("subscription") ? "abcd" : null - } - } - } - return Promise.resolve(dummyResponse) - } - - const fetcher = createAblyFetcher({ - ably: (ably as unknown) as Realtime, - url: "/graphql", - fetch: ((dummyFetch as unknown) as typeof fetch), - fetchOptions: {customOpt: true} - }) - - const result = fetcher({ - variables: {}, - operationName: "hello", - body: "subscription hello { hi }" - }, {}) - - return result.next().then((res) => { - expect(res.value.data.hi).toEqual("First response") - expect(fetchLog).toEqual([["/graphql", true]]) - }).then(() => { - const promise = result.next().then((res2) => { - expect(res2).toEqual({ value: { data: { hi: "Bonjour" } }, done: false }) - }) - - ably.__testTrigger("abcd", "update", { data: { result: { data: { hi: "Bonjour" } } } }) - - return promise.then(() => { - // Test non-subscriptions too: - expect(Object.keys(ably._channels)).toEqual(["abcd"]) - const queryResult = fetcher({ variables: {}, operationName: null, body: "{ __typename }"}, {}) - return queryResult.next().then((res) => { - expect(res.value.data).toEqual({ hi: "First response"}) - return queryResult.next().then((res2) => { - expect(res2.done).toEqual(true) - expect(ably._channels).toEqual({}) - }) - }) - }) - }) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyHandlerTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyHandlerTest.ts deleted file mode 100644 index 946350d4dfc..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createAblyHandlerTest.ts +++ /dev/null @@ -1,499 +0,0 @@ -import { OnErrorData, createAblyHandler } from "../createAblyHandler" -import { Realtime, Types } from "ably" - -const dummyOperation = { text: "", name: "" } - -const channelTemplate = { - presence: { - enter() {}, - enterClient() {}, - leave(callback?: (err?: Types.ErrorInfo) => void) { - if (callback) callback() - } - }, - subscribe: () => {}, - unsubscribe: () => {}, - on: () => {}, - detach: (callback?: (err?: Types.ErrorInfo) => void) => { - if (callback) callback() - } -} - -const createDummyConsumer = ( - channel: any = channelTemplate, - release = (_channelName: string) => {} -): Realtime => - (({ - auth: { clientId: "foo" }, - channels: { - get: () => channel, - release - } - } as unknown) as Realtime) - -const nextTick = () => new Promise(resolve => setTimeout(resolve, 0)) - -describe("createAblyHandler", () => { - it("returns a function producing a disposable subscription", async () => { - const subscriptionId = "dummy-subscription" - var wasUnsubscribed = false - var wasDetached = false - var releasedChannelName - - const producer = createAblyHandler({ - fetchOperation: () => - new Promise(resolve => - resolve({ - headers: new Map([["X-Subscription-ID", subscriptionId]]), - body: { data: { foo: "bar" } } - }) - ), - ably: createDummyConsumer( - { - ...channelTemplate, - unsubscribe: () => { - wasUnsubscribed = true - }, - detach: (callback?: (err?: Types.ErrorInfo) => void) => { - if (callback) callback() - wasDetached = true - }, - name: subscriptionId - }, - (channelName: string) => { - releasedChannelName = channelName - } - ) - }) - - const subscription = producer( - dummyOperation, - {}, - {}, - { onError: () => {}, onNext: () => {}, onCompleted: () => {} } - ) - - await nextTick() - await subscription.dispose() - expect(wasUnsubscribed).toEqual(true) - expect(wasDetached).toEqual(true) - expect(releasedChannelName).toEqual(subscriptionId) - }) - - it("dispatches the immediate response in case of success", async () => { - let errorInvokedWith = undefined - let nextInvokedWith = undefined - - const producer = createAblyHandler({ - fetchOperation: () => - new Promise(resolve => - resolve({ - headers: new Map([["X-Subscription-ID", "foo"]]), - body: { data: { foo: "bar" } } - }) - ), - ably: createDummyConsumer() - }) - - producer( - dummyOperation, - {}, - {}, - { - onError: (errors: any) => { - errorInvokedWith = errors - }, - onNext: (response: any) => { - nextInvokedWith = response - }, - onCompleted: () => {} - } - ) - - await nextTick() - expect(errorInvokedWith).toBeUndefined() - expect(nextInvokedWith).toEqual({ data: { foo: "bar" } }) - }) - - it("dispatches the immediate response in case of error", async () => { - let errorInvokedWith = undefined - let nextInvokedWith = undefined - - const dummyErrors = [{ message: "baz" }] - - const producer = createAblyHandler({ - fetchOperation: () => - new Promise(resolve => - resolve({ - headers: new Map([["X-Subscription-ID", "foo"]]), - body: { errors: dummyErrors } - }) - ), - ably: createDummyConsumer() - }) - - producer( - dummyOperation, - {}, - {}, - { - onError: (errors: any) => { - errorInvokedWith = errors - }, - onNext: () => {}, - onCompleted: () => {} - } - ) - - await nextTick() - expect(errorInvokedWith).toEqual(dummyErrors) - expect(nextInvokedWith).toBeUndefined() - }) - - it("doesn't dispatch anything for an empty response", async () => { - let errorInvokedWith = undefined - let nextInvokedWith = undefined - - const producer = createAblyHandler({ - fetchOperation: () => - new Promise(resolve => - resolve({ - headers: new Map([["X-Subscription-ID", "foo"]]), - body: {} - }) - ), - ably: createDummyConsumer() - }) - - producer( - dummyOperation, - {}, - {}, - { - onError: (errors: any) => { - errorInvokedWith = errors - }, - onNext: (response: any) => { - nextInvokedWith = response - }, - onCompleted: () => {} - } - ) - - await nextTick() - expect(errorInvokedWith).toBeUndefined() - expect(nextInvokedWith).toBeUndefined() - }) - - it("doesn't dispatch anything for an empty data object", async () => { - let errorInvokedWith = undefined - let nextInvokedWith = undefined - - const producer = createAblyHandler({ - fetchOperation: () => - new Promise(resolve => - resolve({ - headers: new Map([["X-Subscription-ID", "foo"]]), - body: { data: {} } - }) - ), - ably: createDummyConsumer() - }) - - producer( - dummyOperation, - {}, - {}, - { - onError: (errors: any) => { - errorInvokedWith = errors - }, - onNext: (response: any) => { - nextInvokedWith = response - }, - onCompleted: () => {} - } - ) - - await nextTick() - expect(errorInvokedWith).toBeUndefined() - expect(nextInvokedWith).toBeUndefined() - }) - - it("dispatches caught errors", async () => { - let errorInvokedWith = undefined - let nextInvokedWith = undefined - - const error = new Error("blam") - - const producer = createAblyHandler({ - fetchOperation: () => new Promise((_resolve, reject) => reject(error)), - ably: createDummyConsumer() - }) - - producer( - dummyOperation, - {}, - {}, - { - onError: (errors: any) => { - errorInvokedWith = errors - }, - onNext: (response: any) => { - nextInvokedWith = response - }, - onCompleted: () => {} - } - ) - - await nextTick() - expect(errorInvokedWith).toBe(error) - expect(nextInvokedWith).toBeUndefined() - }) - - it("detaches the channel when the subscription is disposed during initial response", async () => { - let detached = false - - const ably = createDummyConsumer({ - ...channelTemplate, - detach() { - detached = true - } - }) - const producer = createAblyHandler({ - fetchOperation: () => - new Promise(resolve => - resolve({ - headers: new Map([["X-Subscription-ID", "foo"]]), - body: { errors: {} } - }) - ), - ably - }) - - const { dispose } = producer( - dummyOperation, - {}, - {}, - { - onError: async () => { - dispose() - }, - onNext: async () => {}, - onCompleted: () => {} - } - ) - - await nextTick() - expect(detached).toBe(true) - }) - - describe("integration with Ably", () => { - const key = process.env.ABLY_KEY - const testWithAblyKey = key ? test : test.skip - - test("onError is called when using invalid key", async () => { - const ably = new Realtime({ - key: "integration-test:invalid", - log: { level: 0 } - }) - await new Promise(resolve => { - const fetchOperation = async () => ({ - headers: new Map([["X-Subscription-ID", "foo"]]) - }) - - const ablyHandler = createAblyHandler({ ably, fetchOperation }) - const operation = {} - const variables = {} - const cacheConfig = {} - const onError = (error: any) => { - expect(error.message).toEqual("unable to handle request; no application id found in request") - resolve() - } - const onNext = () => console.log("onNext") - const onCompleted = () => console.log("onCompleted") - const observer = { - onError, - onNext, - onCompleted - } - ablyHandler(operation, variables, cacheConfig, observer) - }) - ably.close() - }) - - // For executing this test you need to provide a valid Ably API key in - // environment variable ABLY_KEY - testWithAblyKey( - "onError is called for too many subscriptions", - async () => { - const ably = new Realtime({ key, log: { level: 0 } }) - await new Promise(resolve => { - let subscriptionCounter = 0 - const fetchOperation = async () => { - subscriptionCounter += 1 - return { - headers: new Map([ - ["X-Subscription-ID", `foo-${subscriptionCounter}`] - ]) - } - } - const ablyHandler = createAblyHandler({ ably, fetchOperation }) - const operation = {} - const variables = {} - const cacheConfig = {} - const onError = (error: any) => { - expect(error.message).toMatch(/Maximum number of channels/) - resolve() - } - const onNext = () => console.log("onNext") - const onCompleted = () => console.log("onCompleted") - const observer = { - onError, - onNext, - onCompleted - } - for (let i = 0; i < 201; ++i) { - ablyHandler(operation, variables, cacheConfig, observer) - } - }) - - ably.close() - } - ) - - // For executing this test you need to provide a valid Ably API key in - // environment variable ABLY_KEY - // - // This test might take longer than the default jest timeout of 5s. - // Consider setting a higher timeout when running in CI. - testWithAblyKey("can make more than 200 subscriptions", async () => { - let caughtError = null - const ably = new Realtime({ key, log: { level: 0 } }) - let subscriptionCounter = 0 - const fetchOperation = async () => { - subscriptionCounter += 1 - return { - headers: new Map([ - ["X-Subscription-ID", `foo-${subscriptionCounter}`] - ]) - } - } - const ablyHandler = createAblyHandler({ ably, fetchOperation }) - const operation = {} - const variables = {} - const cacheConfig = {} - const onError = (error: OnErrorData) => { - caughtError = error - } - const onNext = () => {} - const onCompleted = () => {} - const observer = { - onError, - onNext, - onCompleted - } - - const disposals = [] - for (let i = 0; i < 200; ++i) { - const { dispose } = ablyHandler( - operation, - variables, - cacheConfig, - observer - ) - await new Promise(resolve => setTimeout(resolve, 0)) - - disposals.push(dispose()) - } - await Promise.all(disposals) - - // 201st subscription - should work now that previous 200 subscriptions have been disposed - const { dispose } = ablyHandler( - operation, - variables, - cacheConfig, - observer - ) - await new Promise(resolve => setTimeout(resolve, 0)) - await dispose() - - ably.close() - - if (caughtError) throw caughtError - }) - - // For executing this test you need to provide a valid Ably API key in - // environment variable ABLY_KEY - testWithAblyKey( - "receives message sent before subscribe takes effect", - async () => { - let caughtError = null - const ably = new Realtime({ key, log: { level: 0 } }) - ably.connect() - - const subscriptionId = Math.random().toString(36) - const fetchOperation = async () => ({ - headers: new Map([["X-Subscription-ID", subscriptionId]]), - body: { data: "immediateResult" } - }) - const ablyHandler = createAblyHandler({ ably, fetchOperation }) - const operation = {} - const variables = {} - const cacheConfig = {} - const onError = (error: OnErrorData) => { - caughtError = error - } - const messages: any[] = [] - const onNext = (message: any) => { - messages.push(message.data) - } - const onCompleted = () => {} - const observer = { - onError, - onNext, - onCompleted - } - - // Publish before subscribe - await new Promise((resolve, reject) => { - const ablyPublisher = new Realtime({ key, log: { level: 0 } }) - const publishChannel = ablyPublisher.channels.get(subscriptionId) - publishChannel.publish( - "update", - { - result: { data: "asyncResult" } - }, - err => { - ablyPublisher.close() - if (err) { - reject(err) - } else { - resolve() - } - } - ) - }) - - const { dispose } = ablyHandler( - operation, - variables, - cacheConfig, - observer - ) - - for (let i = 0; i < 20 && messages.length < 2; ++i) { - await new Promise(resolve => setTimeout(resolve, 100)) - } - - await dispose() - - ably.close() - - if (caughtError) throw caughtError - - expect(messages).toEqual(["immediateResult", "asyncResult"]) - } - ) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableFetcherTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableFetcherTest.ts deleted file mode 100644 index 86b6519c687..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableFetcherTest.ts +++ /dev/null @@ -1,66 +0,0 @@ -import createActionCableFetcher from "../createActionCableFetcher" -import type { Consumer } from "@rails/actioncable" -import { parse } from "graphql" - -describe("createActionCableFetcherTest", () => { - it("yields updates for subscriptions", () => { - var handlers: any - var log: [string, any][]= [] - - var dummyActionCableConsumer = { - subscriptions: { - create: (_conn: any, newHandlers: any) => { - handlers = newHandlers - return { - perform: (evt: string, data: any) => { - log.push([evt, data]) - } - } - } - } - } - - const fetchLog: any[] = [] - const dummyFetch = function(url: string, fetchArgs: any) { - fetchLog.push([url, fetchArgs.custom]) - return Promise.resolve({ json: () => { {} } }) - } - - var options = { - consumer: (dummyActionCableConsumer as unknown) as Consumer, - url: "/some_graphql_endpoint", - fetch: dummyFetch as typeof fetch, - fetchOptions: { - custom: true, - } - } - - var fetcher = createActionCableFetcher(options) - - - const queryStr = "subscription listen { update { message } }" - const doc = parse(queryStr) - - const res = fetcher({ operationName: "listen", query: queryStr, variables: {}}, { documentAST: doc }) - const promise = res.next().then((result) => { - - handlers.connected() // trigger the GraphQL send - - expect(result).toEqual({ value: { data: "hello" } , done: false }) - expect(fetchLog).toEqual([]) - expect(log).toEqual([ - ["execute", { operationName: "listen", query: queryStr, variables: {} }], - ]) - }) - - handlers.received({ result: { data: "hello" } }) // simulate an update - - return promise.then(() => { - let res2 = fetcher({ operationName: null, query: "{ __typename } ", variables: {}}, {}) - const promise2 = res2.next().then(() => { - expect(fetchLog).toEqual([["/some_graphql_endpoint", true]]) - }) - return promise2 - }) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableHandlerTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableHandlerTest.ts deleted file mode 100644 index 5d298c089e2..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createActionCableHandlerTest.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { createActionCableHandler } from "../createActionCableHandler" -import type { Consumer } from "@rails/actioncable" - -describe("createActionCableHandler", () => { - it("returns a function producing a disposable subscription", () => { - var wasDisposedCount = 0 - - var subscription = { - unsubscribe: () => (wasDisposedCount += 1) - } - var dummyActionCableConsumer = { - subscriptions: { - create: () => subscription - }, - } - - var options = { - cable: (dummyActionCableConsumer as unknown) as Consumer - } - var producer = createActionCableHandler(options) - var relaySubscription = producer({text: "", name: ""}, {}, {}, { onError: () => {}, onNext: () => {}, onCompleted: () => {} }) - - relaySubscription.dispose() - relaySubscription.dispose() - - expect(wasDisposedCount).toEqual(1) - }) - - it("uses a provided clientName and operation.id", () => { - var handlers: any - var log: [string, any][]= [] - - var dummyActionCableConsumer = { - subscriptions: { - create: (_conn: any, newHandlers: any) => { - handlers = newHandlers - return { - perform: (evt: string, data: any) => { - log.push([evt, data]) - } - } - } - } - } - - var options = { - cable: (dummyActionCableConsumer as unknown) as Consumer, - clientName: "client-1", - } - - var producer = createActionCableHandler(options); - - producer( - {text: "", name: "", id: "abcdef"}, - {}, - {}, - { onError: () => {}, onNext: (result: any) => { log.push(["onNext", result])}, onCompleted: () => { log.push(["onCompleted", null])} } - ) - - handlers.connected() // trigger the GraphQL send - handlers.received({ result: { data: { a: "1" } }, more: false }) - - expect(log).toEqual([ - ["execute", { operationId: "client-1/abcdef", operationName: "", query: "", variables: {} }], - ["onNext", { data: { a: "1" } }], - ["onCompleted", null], - ]) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createPusherFetcherTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createPusherFetcherTest.ts deleted file mode 100644 index 595baac353d..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createPusherFetcherTest.ts +++ /dev/null @@ -1,99 +0,0 @@ -import createPusherFetcher from "../createPusherFetcher" -import type Pusher from "pusher-js" - -type MockChannel = { - bind: (action: string, handler: Function) => void, - unsubscribe: () => void, -} - -describe("createPusherFetcher", () => { - it("yields updates for subscriptions", () => { - const pusher = { - _channels: {} as {[key: string]: [string, Function][]}, - - trigger: function(channel: string, event: string, data: any) { - var handlers = this._channels[channel] - if (handlers) { - handlers.forEach(function(handler: [string, Function]) { - if (handler[0] == event) { - handler[1](data) - } - }) - } - }, - subscribe: function(channel: string): MockChannel { - var handlers = this._channels[channel] - if (!handlers) { - handlers = this._channels[channel] = [] - } - - return { - bind: (action: string, handler: Function): void => { - handlers.push([action, handler]) - }, - unsubscribe: () => { - delete this._channels[channel] - } - } - }, - unsubscribe: (_channel: string): void => { - }, - } - - const fetchLog: any[] = [] - const dummyFetch = function(url: string, fetchArgs: any) { - fetchLog.push([url, fetchArgs.customOpt]) - const dummyResponse = { - json: () => { - return { - data: { - hi: "First response" - } - } - }, - headers: { - get() { - return fetchArgs.body.includes("subscription") ? "abcd" : null - } - } - } - return Promise.resolve(dummyResponse) - } - - const fetcher = createPusherFetcher({ - pusher: (pusher as unknown) as Pusher, - url: "/graphql", - fetch: ((dummyFetch as unknown) as typeof fetch), - fetchOptions: {customOpt: true} - }) - - const result = fetcher({ - variables: {}, - operationName: "hello", - body: "subscription hello { hi }" - }, {}) - - return result.next().then((res) => { - expect(res.value.data.hi).toEqual("First response") - expect(fetchLog).toEqual([["/graphql", true]]) - }).then(() => { - const promise = result.next().then((res2) => { - expect(res2).toEqual({ value: { data: { hi: "Bonjour" } }, done: false }) - }) - pusher.trigger("abcd", "update", { result: { data: { hi: "Bonjour" } } }) - - return promise.then(() => { - // Test non-subscriptions too: - expect(Object.keys(pusher._channels)).toEqual(["abcd"]) - const queryResult = fetcher({ variables: {}, operationName: null, body: "{ __typename }"}, {}) - return queryResult.next().then((res) => { - expect(res.value.data).toEqual({ hi: "First response"}) - return queryResult.next().then((res2) => { - expect(res2.done).toEqual(true) - expect(pusher._channels).toEqual({}) - }) - }) - }) - }) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createRelaySubscriptionHandlerTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createRelaySubscriptionHandlerTest.ts deleted file mode 100644 index af5732ff9bf..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/createRelaySubscriptionHandlerTest.ts +++ /dev/null @@ -1,81 +0,0 @@ -import createRelaySubscriptionHandler from "../createRelaySubscriptionHandler" -import { createLegacyRelaySubscriptionHandler } from "../createRelaySubscriptionHandler" -import type { Consumer } from "@rails/actioncable" -import { Network } from 'relay-runtime' - -describe("createRelaySubscriptionHandler", () => { - it("returns a function producing a observable subscription", () => { - var dummyActionCableConsumer = { - subscriptions: { - create: () => ({ unsubscribe: () => ( true) }) - }, - } - - var options = { - cable: (dummyActionCableConsumer as unknown) as Consumer - } - - var handler = createRelaySubscriptionHandler(options) - var fetchQuery: any - // basically, make sure this doesn't blow up during type-checking or runtime - expect(Network.create(fetchQuery, handler)).toBeTruthy() - }) - - it("doesn't send an empty string when no string is given", () => { - var channel: any; - var performLog: any[] = []; - var dummyActionCableConsumer = { - subscriptions: { - create: (opts1: any, opts2: any) => { - channel = Object.assign( - opts1, - opts2, - { - unsubscribe: () => true, - perform: (event: string, payload: object) => performLog.push([event, payload]), - - } - ) - return channel - } - }, - } - - var options = { - cable: (dummyActionCableConsumer as unknown) as Consumer - } - - var handler = createRelaySubscriptionHandler(options) - var observable = handler({id: "abc", text: null, name: "def", operationKind: "subscription", metadata: {}}, { abc: true}); - observable.subscribe({}) - channel.connected() - var expectedLog = [ - [ - 'execute', - { - variables: { abc: true }, - operationName: 'def', - query: null, - operationId: null - } - ] - ] - expect(performLog).toEqual(expectedLog) - }) -}) - -describe("createLegacyRelaySubscriptionHandler", () => { - it("still works", () => { - var dummyActionCableConsumer = { - subscriptions: { - create: () => ({ unsubscribe: () => ( true) }) - }, - } - - var options = { - cable: (dummyActionCableConsumer as unknown) as Consumer - } - - expect(createLegacyRelaySubscriptionHandler(options)).toBeInstanceOf(Function) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/registryTest.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/registryTest.ts deleted file mode 100644 index b9e9b2f6ca9..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/__tests__/registryTest.ts +++ /dev/null @@ -1,37 +0,0 @@ -import registry from "../registry" - -describe("subscription registry", () => { - it("adds and unsubscribes", () => { - // A subscription is something that responds to `.unsubscribe` - var wasUnsubscribed1 = false - var subscription1 = { - unsubscribe: function() { - wasUnsubscribed1 = true - } - } - var wasUnsubscribed2 = false - var subscription2 = { - unsubscribe: function() { - wasUnsubscribed2 = true - } - } - // Adding a subscription returns an ID for unsubscribing - var id1 = registry.add(subscription1) - var id2 = registry.add(subscription2) - expect(typeof id1).toEqual("number") - expect(typeof id2).toEqual("number") - // Unsubscribing calls the `.unsubscribe `function - registry.unsubscribe(id1) - expect(wasUnsubscribed1).toEqual(true) - expect(wasUnsubscribed2).toEqual(false) - registry.unsubscribe(id2) - expect(wasUnsubscribed1).toEqual(true) - expect(wasUnsubscribed2).toEqual(true) - }) - - it("raises on unknown ids", () => { - expect(() => { - registry.unsubscribe(999) - }).toThrow("No subscription found for id: 999") - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/addGraphQLSubscriptions.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/addGraphQLSubscriptions.ts deleted file mode 100644 index fbce178f31c..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/addGraphQLSubscriptions.ts +++ /dev/null @@ -1,80 +0,0 @@ -import ActionCableSubscriber from "./ActionCableSubscriber" -import PusherSubscriber from "./PusherSubscriber" -import Pusher from "pusher-js" -import type { Consumer } from "@rails/actioncable" - -interface Subscriber { - subscribe: Function - unsubscribe: Function -} -/** - * Modify an Apollo network interface to - * subscribe an unsubscribe using `cable:`. - * Based on `addGraphQLSubscriptions` from `subscriptions-transport-ws`. - * - * This function assigns `.subscribe` and `.unsubscribe` functions - * to the provided networkInterface. - * @example Adding ActionCable subscriptions to a HTTP network interface - * // Load ActionCable and create a consumer - * var ActionCable = require('@rails/actioncable') - * var cable = ActionCable.createConsumer() - * window.cable = cable - * - * // Load ApolloClient and create a network interface - * var apollo = require('apollo-client') - * var RailsNetworkInterface = apollo.createNetworkInterface({ - * uri: '/graphql', - * opts: { - * credentials: 'include', - * }, - * headers: { - * 'X-CSRF-Token': $("meta[name=csrf-token]").attr("content"), - * } - * }); - * - * // Add subscriptions to the network interface - * var addGraphQLSubscriptions = require("graphql-ruby-client/subscriptions/addGraphQLSubscriptions") - * addGraphQLSubscriptions(RailsNetworkInterface, {cable: cable}) - * - * // Optionally, add persisted query support: - * var OperationStoreClient = require("./OperationStoreClient") - * RailsNetworkInterface.use([OperationStoreClient.apolloMiddleware]) - * - * @example Subscriptions with Pusher & graphql-pro - * var pusher = new Pusher(appId, options) - * addGraphQLSubscriptions(RailsNetworkInterface, {pusher: pusher}) - * - * @param {Object} networkInterface - an HTTP NetworkInterface - * @param {ActionCable.Consumer} options.cable - A cable for subscribing with - * @param {Pusher} options.pusher - A pusher client for subscribing with -*/ -function addGraphQLSubscriptions(networkInterface: any, options: { pusher?: Pusher, cable?: Consumer, subscriber?: Subscriber, decompress?: (compressed: string) => any, channelName?: string }) { - if (!options) { - options = {} - } - - var subscriber: Subscriber - if (options.subscriber) { - // Right now this is just for testing - subscriber = options.subscriber - } else if (options.cable) { - subscriber = new ActionCableSubscriber(options.cable, networkInterface, options.channelName) - } else if (options.pusher) { - subscriber = new PusherSubscriber(options.pusher, networkInterface, options.decompress) - } else { - throw new Error("Must provide cable: or pusher: option") - } - - var networkInterfaceWithSubscriptions = Object.assign(networkInterface, { - subscribe: function(request: any, handler: any) { - var id = subscriber.subscribe(request, handler) - return id - }, - unsubscribe(id: number) { - subscriber.unsubscribe(id) - }, - }) - return networkInterfaceWithSubscriptions -} - -export default addGraphQLSubscriptions diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createAblyFetcher.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createAblyFetcher.ts deleted file mode 100644 index dc360f4c081..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createAblyFetcher.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type Types from "ably" - -type AblyFetcherOptions = { - ably: Types.Realtime, - url: String, - fetch?: typeof fetch, - fetchOptions?: any, -} - -type SubscriptionIteratorPayload = { - value: any, - done: Boolean -} - -const clientName = "graphiql-subscriber" - -export default function createAblyFetcher(options: AblyFetcherOptions) { - var currentChannel: Types.Types.RealtimeChannelCallbacks | null = null - - return async function*(graphqlParams: any, _fetcherParams: any) { - var nextPromiseResolve: Function | null = null - var shouldBreak = false - - var iterator = { - [Symbol.asyncIterator]() { - return { - next(): Promise { - return new Promise((resolve, _reject) => { - nextPromiseResolve = resolve - }) - }, - return(): Promise { - if (currentChannel) { - currentChannel.presence.leaveClient(clientName) - currentChannel.unsubscribe() - const channelName = currentChannel.name - currentChannel.detach(() => { - options.ably.channels.release(channelName) - }) - currentChannel = null - nextPromiseResolve = null - } - return Promise.resolve({ value: null, done: true }) - } - } - } - } - - const fetchFn = options.fetch || window.fetch - fetchFn("/graphql", { - method: "POST", - body: JSON.stringify(graphqlParams), - headers: { - 'content-type': 'application/json', - }, - ... options.fetchOptions - }).then((r) => { - const subId = r.headers.get("X-Subscription-ID") - if (subId) { - currentChannel && currentChannel.unsubscribe() - currentChannel = options.ably.channels.get(subId, { modes: ["SUBSCRIBE", "PRESENCE"] }) - currentChannel.presence.enterClient(clientName, "subscribed", (err) => { - if (err) { - console.error(err) - } - }) - currentChannel.subscribe("update", (message: Types.Types.Message) => { - console.log("update", message) - if (nextPromiseResolve) { - nextPromiseResolve({ value: message.data.result, done: false }) - } - }) - - if (nextPromiseResolve) { - nextPromiseResolve({ value: r.json(), done: false }) - } - } else { - shouldBreak = true - if (nextPromiseResolve) { - nextPromiseResolve({ value: r.json(), done: false}) - } - } - }) - - for await (const payload of iterator) { - yield payload - if (shouldBreak) { - break - } - } - } -} diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createAblyHandler.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createAblyHandler.ts deleted file mode 100644 index 1e7a9e44463..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createAblyHandler.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { Realtime, Types } from "ably" -// TODO: -// - end-to-end test -// - extract update code, inject it as a function? - -interface AblyHandlerOptions { - ably: Realtime - fetchOperation: Function -} - -interface GraphQLError { - message: string - path: (string | number)[] - locations: number[][] - extensions?: object -} - -type OnErrorData = AblyError | Error| GraphQLError[] - -interface ApolloObserver { - onError: (err: OnErrorData) => void - onNext: Function - onCompleted: Function -} - -const anonymousClientId = "graphql-subscriber" - -// Current max. number of rewound messages in the initial response to -// subscribe. See -// https://github.com/ably/docs/blob/baa0a4666079abba3a3e19e82eb99ca8b8a735d0/content/realtime/channels/channel-parameters/rewind.textile#additional-information -// Note that using a higher value emits a warning. -const maxNumRewindMessages = 100 - -class AblyError extends Error { - constructor(public reason: Types.ErrorInfo) { - super(reason.message) - } - - get code() { - return this.reason.code - } - - get statusCode() { - return this.reason.statusCode - } -} - -function createAblyHandler(options: AblyHandlerOptions) { - const { ably, fetchOperation } = options - - const isAnonymousClient = () => - !ably.auth.clientId || ably.auth.clientId === "*" - - return ( - operation: object, - variables: object, - cacheConfig: object, - observer: ApolloObserver - ) => { - let channel: Types.RealtimeChannelCallbacks | null = null - - const dispatchResult = (result: { errors?: GraphQLError[]; data: any }) => { - if (result) { - if (result.errors) { - // What kind of error stuff belongs here? - observer.onError(result.errors) - } else if (result.data && Object.keys(result.data).length > 0) { - observer.onNext({ data: result.data }) - } - } - } - - const updateHandler = (message: Types.Message) => { - // TODO Extract this code - // When we get a response, send the update to `observer` - const payload = message.data - - dispatchResult(payload.result) - if (!payload.more) { - // Subscription is finished - observer.onCompleted() - } - } - ;(async () => { - try { - // POST the subscription like a normal query - const response = await fetchOperation(operation, variables, cacheConfig) - - const channelName = response.headers.get("X-Subscription-ID") - if (!channelName) { - throw new Error("Missing X-Subscription-ID header") - } - - const channelKey = response.headers.get("X-Subscription-Key") - - channel = ably.channels.get(channelName, { - params: { rewind: String(maxNumRewindMessages) }, - cipher: channelKey ? { key: channelKey } : undefined, - modes: ["SUBSCRIBE", "PRESENCE"] - }) - - channel.on("failed", function(stateChange: Types.ChannelStateChange) { - observer.onError( - stateChange.reason - ? new AblyError(stateChange.reason) - : new Error("Ably channel changed to failed state") - ) - }) - channel.on("suspended", function( - stateChange: Types.ChannelStateChange - ) { - // Note: suspension can be a temporary condition and isn't necessarily - // an error, however we handle the case where the channel gets - // suspended before it is attached because that's the only way to - // propagate error 90010 (see https://help.ably.io/error/90010) - if ( - stateChange.previous === "attaching" && - stateChange.current === "suspended" - ) { - observer.onError( - stateChange.reason - ? new AblyError(stateChange.reason) - : new Error("Ably channel suspended before being attached") - ) - } - }) - - // Register presence, so that we can detect empty channels and clean them up server-side - const enterCallback = (errorInfo: Types.ErrorInfo | null | undefined) => { - if (errorInfo && channel) { - observer.onError(new AblyError(errorInfo)) - } - } - if (isAnonymousClient()) { - channel.presence.enterClient( - anonymousClientId, - "subscribed", - enterCallback - ) - } else { - channel.presence.enter("subscribed", enterCallback) - } - - // When you get an update from ably, give it to Relay - channel.subscribe("update", updateHandler) - - // Dispatch the result _after_ setting up the channel, - // because Relay might immediately dispose of the subscription. - // (In that case, we want to make sure the channel is cleaned up properly.) - dispatchResult(response.body) - } catch (error) { - observer.onError(error as Error) - } - })() - - return { - dispose: async () => { - try { - if (channel) { - const disposedChannel = channel - channel = null - disposedChannel.unsubscribe() - - // Ensure channel is no longer attaching, as otherwise detach does - // nothing - if (disposedChannel.state === "attaching") { - await new Promise((resolve, _reject) => { - const onStateChange = ( - stateChange: Types.ChannelStateChange - ) => { - if (stateChange.current !== "attaching") { - disposedChannel.off(onStateChange) - resolve() - } - } - disposedChannel.on(onStateChange) - }) - } - - await new Promise((resolve, reject) => { - disposedChannel.detach(err => { - if (err) { - reject(new AblyError(err)) - } else { - resolve() - } - }) - }) - - ably.channels.release(disposedChannel.name) - } - } catch (error) { - observer.onError(error as Error) - } - } - } - } -} - -export { createAblyHandler, AblyHandlerOptions, OnErrorData } diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableFetcher.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableFetcher.ts deleted file mode 100644 index da7063f08b7..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableFetcher.ts +++ /dev/null @@ -1,98 +0,0 @@ - -import { visit } from "graphql"; -import type { Consumer, Subscription } from "@rails/actioncable" - -type ActionCableFetcherOptions = { - consumer: Consumer, - url: string, - channelName?: string, - fetch?: typeof fetch, - fetchOptions?: any, -} - -type SubscriptionIteratorPayload = { - value: any, - done: Boolean -} - -export default function createActionCableFetcher(options: ActionCableFetcherOptions) { - let currentChannel: Subscription | null = null - const consumer = options.consumer - const url = options.url || "/graphql" - const channelName = options.channelName || "GraphqlChannel" - - const subscriptionFetcher = async function*(graphqlParams: any, fetcherOpts: any) { - let isSubscription = false; - let nextPromiseResolve: Function | null = null; - - fetcherOpts.documentAST && visit(fetcherOpts.documentAST, { - OperationDefinition(node) { - if (graphqlParams.operationName === node.name?.value && node.operation === 'subscription') { - isSubscription = true; - } - }, - }); - - if (isSubscription) { - currentChannel?.unsubscribe() - currentChannel = consumer.subscriptions.create(channelName, - { - connected: function() { - currentChannel?.perform("execute", { - query: graphqlParams.query, - operationName: graphqlParams.operationName, - variables: graphqlParams.variables, - }) - }, - - received: function(data: any) { - if (nextPromiseResolve) { - nextPromiseResolve({ value: data.result, done: false }) - } - } - } as any - ) - - var iterator = { - [Symbol.asyncIterator]() { - return { - next(): Promise { - return new Promise((resolve, _reject) => { - nextPromiseResolve = resolve - }) - }, - return(): Promise { - if (currentChannel) { - currentChannel.unsubscribe() - currentChannel = null - } - return Promise.resolve({ value: null, done: true }) - } - } - } - } - - for await (const payload of iterator) { - yield payload - } - } else { - const fetchFn = options.fetch || window.fetch - // Not a subscription fetcher, post to the given URL - yield fetchFn(url, { - method: "POST", - body: JSON.stringify({ - query: graphqlParams.query, - operationName: graphqlParams.operationName, - variables: graphqlParams.variables, - }), - headers: { - 'content-type': 'application/json', - }, - ... options.fetchOptions - }).then((r) => r.json()) - return - } - } - - return subscriptionFetcher -} diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableHandler.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableHandler.ts deleted file mode 100644 index 4f2668473ae..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createActionCableHandler.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { Consumer } from "@rails/actioncable" - -/** - * Create a Relay Modern-compatible subscription handler. - * - * @param {ActionCable.Consumer} cable - An ActionCable consumer from `.createConsumer` - * @param {OperationStoreClient} operations - A generated OperationStoreClient for graphql-pro's OperationStore - * @return {Function} -*/ -interface ActionCableHandlerOptions { - cable: Consumer - operations?: { getOperationId: Function} - channelName?: string - clientName?: string -} - -function createActionCableHandler(options: ActionCableHandlerOptions) { - return function (operation: { text: string, name: string, id?: string }, variables: object, _cacheConfig: object, observer: {onError: Function, onNext: Function, onCompleted: Function}) { - // unique-ish - var channelId = Math.round(Date.now() + Math.random() * 100000).toString(16) - var cable = options.cable - var operations = options.operations - var subscribed = true - - // Register the subscription by subscribing to the channel - const channel = cable.subscriptions.create({ - channel: options.channelName || "GraphqlChannel", - channelId: channelId, - }, { - connected: function() { - var channelParams: object - // Once connected, send the GraphQL data over the channel - // Use the stored operation alias if possible - if (operations) { - channelParams = { - variables: variables, - operationName: operation.name, - operationId: operations.getOperationId(operation.name) - } - } else { - channelParams = { - variables: variables, - operationName: operation.name, - query: operation.text, - operationId: (operation.id && options.clientName ? (options.clientName + "/" + operation.id) : null), - } - } - channel.perform("execute", channelParams) - }, - // This result is sent back from ActionCable. - received: function(payload: { result: { errors: any[], data: object }, more: boolean}) { - // When we get a response, send the update to `observer` - const result = payload.result - if (result && result.errors) { - // What kind of error stuff belongs here? - observer.onError(result.errors) - } else if (result) { - observer.onNext({data: result.data}) - } - if (!payload.more && subscribed) { - // Subscription is finished - observer.onCompleted() - } - } - }) - - // Return an object for Relay to unsubscribe with - return { - dispose: function() { - if (subscribed) { - subscribed = false - channel.unsubscribe() - } - } - } - } -} - -export { - createActionCableHandler, - ActionCableHandlerOptions -} diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createPusherFetcher.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createPusherFetcher.ts deleted file mode 100644 index f400ba6ab63..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createPusherFetcher.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type Pusher from "pusher-js" -import type { Channel } from "pusher-js" - -type PusherFetcherOptions = { - pusher: Pusher, - url: String, - fetch?: typeof fetch, - fetchOptions: any, -} - -type SubscriptionIteratorPayload = { - value: any, - done: Boolean -} - -export default function createPusherFetcher(options: PusherFetcherOptions) { - var currentChannel: Channel | null = null - - return async function*(graphqlParams: any, _fetcherParams: any) { - var nextPromiseResolve: Function | null = null - var shouldBreak = false - - var iterator = { - [Symbol.asyncIterator]() { - return { - next(): Promise { - return new Promise((resolve, _reject) => { - nextPromiseResolve = resolve - }) - }, - return(): Promise { - if (currentChannel) { - currentChannel.unsubscribe() - currentChannel = null - } - return Promise.resolve({ value: null, done: true }) - } - } - } - } - - const fetchFn = options.fetch || window.fetch - fetchFn("/graphql", { - method: "POST", - body: JSON.stringify(graphqlParams), - headers: { - 'content-type': 'application/json', - }, - ...options.fetchOptions - }).then((r) => { - const subId = r.headers.get("X-Subscription-ID") - if (subId) { - currentChannel && currentChannel.unsubscribe() - currentChannel = options.pusher.subscribe(subId) - currentChannel.bind("update", (payload: any) => { - if (nextPromiseResolve) { - nextPromiseResolve({ value: payload.result, done: false }) - } - }) - - if (nextPromiseResolve) { - nextPromiseResolve({ value: r.json(), done: false }) - } - } else { - shouldBreak = true - if (nextPromiseResolve) { - nextPromiseResolve({ value: r.json(), done: false}) - } - } - }) - - for await (const payload of iterator) { - yield payload - if (shouldBreak) { - break - } - } - } -} diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createPusherHandler.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createPusherHandler.ts deleted file mode 100644 index 7615616952d..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createPusherHandler.ts +++ /dev/null @@ -1,56 +0,0 @@ -import Pusher from "pusher-js" -// TODO: -// - end-to-end test -// - extract update code, inject it as a function? -interface PusherHandlerOptions { - pusher: Pusher, - fetchOperation: Function, - decompress?: Function, -} - -function createPusherHandler(options: PusherHandlerOptions) { - var pusher = options.pusher - var fetchOperation = options.fetchOperation - var decompress = options.decompress || function(_compressed: string) { - throw new Error("Received compressed_result but createPusherHandler wasn't configured with `decompress: (result: string) => any`. Add this configuration.") - } - return function (operation: object, variables: object, cacheConfig: object, observer: { onNext: Function, onError: Function, onCompleted: Function}) { - var channelName: string - // POST the subscription like a normal query - fetchOperation(operation, variables, cacheConfig).then(function(response: { headers: { get: Function } }) { - channelName = response.headers.get("X-Subscription-ID") - var channel = pusher.subscribe(channelName) - // When you get an update from pusher, give it to Relay - channel.bind("update", function(payload: {more: boolean, result?: any, compressed_result?: string}) { - // TODO Extract this code - // When we get a response, send the update to `observer` - let result: any = null - if (payload.compressed_result) { - result = decompress(payload.compressed_result) - } else { - result = payload.result - } - if (result && result.errors) { - // What kind of error stuff belongs here? - observer.onError(result.errors) - } else if (result) { - observer.onNext({data: result.data}) - } - if (!payload.more) { - // Subscription is finished - observer.onCompleted() - } - }) - }) - return { - dispose: function() { - pusher.unsubscribe(channelName) - } - } - } -} - -export { - createPusherHandler, - PusherHandlerOptions -} diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/createRelaySubscriptionHandler.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/createRelaySubscriptionHandler.ts deleted file mode 100644 index 6036e254281..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/createRelaySubscriptionHandler.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { createActionCableHandler, ActionCableHandlerOptions } from "./createActionCableHandler" -import { createPusherHandler, PusherHandlerOptions } from "./createPusherHandler" -import { createAblyHandler, AblyHandlerOptions } from "./createAblyHandler" -import { RequestParameters, Variables, Observable } from "relay-runtime" - -function createLegacyRelaySubscriptionHandler(options: ActionCableHandlerOptions | PusherHandlerOptions | AblyHandlerOptions) { - var handler: any - if ((options as ActionCableHandlerOptions).cable) { - handler = createActionCableHandler(options as ActionCableHandlerOptions) - } else if ((options as PusherHandlerOptions).pusher) { - handler = createPusherHandler(options as PusherHandlerOptions) - } else if ((options as AblyHandlerOptions).ably) { - handler = createAblyHandler(options as AblyHandlerOptions) - } else { - throw new Error("Missing options for subscription handler") - } - return handler -} - -/** - * Transport-agnostic wrapper for Relay Modern subscription handlers. - * @example Add ActionCable subscriptions - * var subscriptionHandler = createHandler({ - * cable: cable, - * operations: OperationStoreClient, - * }) - * var network = Network.create(fetchQuery, subscriptionHandler) - * @param {ActionCable.Consumer} options.cable - A consumer from `.createConsumer` - * @param {Pusher} options.pusher - A Pusher client - * @param {Ably.Realtime} options.ably - An Ably client - * @param {OperationStoreClient} options.operations - A generated `OperationStoreClient` for graphql-pro's OperationStore - * @return {Function} A handler for a Relay Modern network -*/ - -function createRelaySubscriptionHandler(options: ActionCableHandlerOptions | PusherHandlerOptions | AblyHandlerOptions) { - const handler = createLegacyRelaySubscriptionHandler(options) - - // Turn the handler into a relay-ready subscribe function - return (request: RequestParameters, variables: Variables): any => { - return Observable.from({ - subscribe: (observer: { - next: any | ((v: any) => void); - complete: () => void; - error: (error: Error) => void; - }) => { - const client = handler( - { - text: request.text, - name: request.name, - id: request.id, - }, - variables, - {}, - { - onError: (_error: Error) => { - observer.error; - }, - onNext: (res: any) => { - if (!res || !res.data) { - return; - } - observer.next(res); - }, - onCompleted: observer.complete, - } - ); - - return { - unsubscribe: () => { - client.dispose(); - }, - }; - }, - }); - }; -} - -export { createLegacyRelaySubscriptionHandler } -export default createRelaySubscriptionHandler diff --git a/vendor/gems/graphql/javascript_client/src/subscriptions/registry.ts b/vendor/gems/graphql/javascript_client/src/subscriptions/registry.ts deleted file mode 100644 index 20f83dce5b9..00000000000 --- a/vendor/gems/graphql/javascript_client/src/subscriptions/registry.ts +++ /dev/null @@ -1,38 +0,0 @@ -interface ApolloSubscription { - unsubscribe: Function -} - -// State management for subscriptions. -// Used to add subscriptions to an Apollo network interface. -class ApolloSubscriptionRegistry { - // Apollo expects unique ids to reference each subscription, - // here's a simple incrementing ID generator which starts at 1 - // (so it's always truthy) - _id: number - - // for unsubscribing when Apollo asks us to - _subscriptions: {[key: number]: ApolloSubscription} - - constructor() { - this._id = 1 - this._subscriptions = {} - } - - add(subscription: ApolloSubscription): number { - var id = this._id++ - this._subscriptions[id] = subscription - return id - } - - unsubscribe(id: number): void { - var subscription = this._subscriptions[id] - if (!subscription) { - throw new Error("No subscription found for id: " + id) - } - subscription.unsubscribe() - delete this._subscriptions[id] - } -} - -export default new ApolloSubscriptionRegistry - diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/dumpPayloadTest.ts.snap b/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/dumpPayloadTest.ts.snap deleted file mode 100644 index c830f85a4b1..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/dumpPayloadTest.ts.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`printing out the HTTP Post payload prints the result to stdout 1`] = ` -[ - [ - "{ - "ok": { - "1": true - } -} -", - ], -] -`; diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateClientTest.ts.snap b/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateClientTest.ts.snap deleted file mode 100644 index 98e9bb1d6fd..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateClientTest.ts.snap +++ /dev/null @@ -1,101 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`returns generated code 1`] = ` -" - /** - * Generated by graphql-ruby-client - * - */ - - /** - * Map local operation names to persisted keys on the server - * @return {Object} - * @private - */ - var _aliases = { - "GetStuff": "b8086942c2fbb6ac69b97cbade848033" -} - - /** - * The client who synced these operations with the server - * @return {String} - * @private - */ - var _client = "test-client" - - var OperationStoreClient = { - /** - * Build a string for \`params[:operationId]\` - * @param {String} operationName - * @return {String} stored operation ID - */ - getOperationId: function(operationName) { - return _client + "/" + OperationStoreClient.getPersistedQueryAlias(operationName) - }, - - /** - * Fetch a persisted alias from a local operation name - * @param {String} operationName - * @return {String} persisted alias - */ - getPersistedQueryAlias: function(operationName) { - var persistedAlias = _aliases[operationName] - if (!persistedAlias) { - throw new Error("Failed to find persisted alias for operation name: " + operationName) - } else { - return persistedAlias - } - }, - - /** - * Satisfy the Apollo Link API. - * This link checks for an operation name, and if it's present, - * sets the HTTP context to _not_ include the query, - * and instead, include \`extensions.operationId\`. - * (This is inspired by apollo-link-persisted-queries.) - */ - apolloLink: function(operation, forward) { - if (operation.operationName) { - const operationId = OperationStoreClient.getOperationId(operation.operationName) - operation.setContext({ - http: { - includeQuery: false, - includeExtensions: true, - } - }) - operation.extensions.operationId = operationId - } - return forward(operation) - }, - /** - * Satisfy the Apollo middleware API. - * Replace the query with an operationId - */ - apolloMiddleware: { - applyBatchMiddleware: function(options, next) { - options.requests.forEach(function(req) { - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - return req - }) - // Continue the request - next() - }, - - applyMiddleware: function(options, next) { - var req = options.request - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - // Continue the request - next() - } - } - } - - module.exports = OperationStoreClient - " -`; diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateJsonClientTest.ts.snap b/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateJsonClientTest.ts.snap deleted file mode 100644 index d6081acfe9e..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/generateJsonClientTest.ts.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`generates a valid json object string that maps names to operations 1`] = ` -"{ - "a": "b", - "c-d": "e-f" -}" -`; - -exports[`generates a valid json object string that maps names to operations 2`] = ` -{ - "a": "b", - "c-d": "e-f", -} -`; diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareIsolatedFilesTest.ts.snap b/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareIsolatedFilesTest.ts.snap deleted file mode 100644 index 79aaa4e7241..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareIsolatedFilesTest.ts.snap +++ /dev/null @@ -1,64 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`builds out single operations 1`] = ` -[ - { - "alias": "", - "body": "query GetStuffIsolated { - ...FragIsolated - things { - existHere - } -} - -fragment FragIsolated on Query { - evenMoreStuff { - stuffInside - } -}", - "name": "GetStuffIsolated", - }, - { - "alias": "", - "body": "query GetStuffIsolated2 { - things { - existHere - } -}", - "name": "GetStuffIsolated2", - }, -] -`; - -exports[`with --add-typename builds out single operations with __typename fields 1`] = ` -[ - { - "alias": "", - "body": "query GetStuffIsolated { - ...FragIsolated - things { - existHere - __typename - } -} - -fragment FragIsolated on Query { - evenMoreStuff { - stuffInside - __typename - } -}", - "name": "GetStuffIsolated", - }, - { - "alias": "", - "body": "query GetStuffIsolated2 { - things { - existHere - __typename - } -}", - "name": "GetStuffIsolated2", - }, -] -`; diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/preparePersistedQueryListTest.ts.snap b/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/preparePersistedQueryListTest.ts.snap deleted file mode 100644 index e4abbe39de5..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/preparePersistedQueryListTest.ts.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`reads generate-persisted-query-manifest output 1`] = ` -[ - { - "alias": "4a29162b05ee4d82ad02e8f50af4bf112f47181ec558a7100a", - "body": "query TestQuery1 { - testing { - id - label - description - __typename -} }", - "name": "TestQuery1", - }, - { - "alias": "xyz-123", - "body": "query TestQuery2 { - testing2 { - id2 - label - description2 - __typename -} }", - "name": "TestQuery2", - }, -] -`; diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareProjectTest.ts.snap b/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareProjectTest.ts.snap deleted file mode 100644 index e3155d78897..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/__snapshots__/prepareProjectTest.ts.snap +++ /dev/null @@ -1,70 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`merging a project builds out separate operations 1`] = ` -[ - { - "alias": "", - "body": "query GetStuff2 { - stuff - ...Frag1 - ...Frag2 -} - -fragment Frag1 on Query { - moreStuff -} - -fragment Frag2 on Query { - ...Frag3 -} - -fragment Frag3 on Query { - evenMoreStuff -}", - "name": "GetStuff2", - }, - { - "alias": "", - "body": "query GetStuff { - ...Frag1 -} - -fragment Frag1 on Query { - moreStuff -}", - "name": "GetStuff", - }, -] -`; - -exports[`merging a project with --add-typename builds out operation with __typename fields 1`] = ` -[ - { - "alias": "", - "body": "query GetStuff3 { - stuff { - withStuffInside - __typename - } - ...Frag2 - ...Frag4 -} - -fragment Frag2 on Query { - ...Frag3 -} - -fragment Frag3 on Query { - evenMoreStuff -} - -fragment Frag4 on Query { - evenMoreStuff { - stuffInside - __typename - } -}", - "name": "GetStuff3", - }, -] -`; diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/addTypenameToSelectionSetTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/addTypenameToSelectionSetTest.ts deleted file mode 100644 index a209d391285..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/addTypenameToSelectionSetTest.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {addTypenameToSelectionSet} from "../addTypenameToSelectionSet" -import { parse, print } from "graphql" - -describe("adding typename", () => { - it("adds to fields and inline fragments", () => { - var doc = parse("{ a { b ... { c } } }") - var newDoc = addTypenameToSelectionSet(doc) - var newString = print(newDoc).replace(/\s+/g, " ").trim() - expect(newString).toEqual("{ a { b ... { c __typename } __typename } }") - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/dumpPayloadTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/dumpPayloadTest.ts deleted file mode 100644 index 22867a6cab3..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/dumpPayloadTest.ts +++ /dev/null @@ -1,33 +0,0 @@ -import dumpPayload from "../dumpPayload" -import fs from 'fs' -interface MockedObject { - mock: { calls: object } -} - -describe("printing out the HTTP Post payload", () => { - beforeEach(() => { - process.stdout.write = jest.fn() - }) - - afterEach(() => { - jest.clearAllMocks(); - }) - - - it("prints the result to stdout", () => { - var spy = (process.stdout.write as unknown) as MockedObject - dumpPayload({"ok": { "1": true}}, { dumpPayload: true }) - expect(spy.mock.calls).toMatchSnapshot() - }) - - it("writes the result to a file", () => { - dumpPayload({"ok": { "1": true}}, {dumpPayload: "./DumpPayloadExample.json"}) - let writtenContents = fs.readFileSync("./DumpPayloadExample.json", 'utf8') - expect(writtenContents).toEqual(`{ - "ok": { - "1": true - } -} -`) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generate-persisted-query-manifest.json b/vendor/gems/graphql/javascript_client/src/sync/__tests__/generate-persisted-query-manifest.json deleted file mode 100644 index b469ab6783f..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generate-persisted-query-manifest.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "format": "apollo-persisted-query-manifest", - "version": 1, - "operations": [ - { - "id": "4a29162b05ee4d82ad02e8f50af4bf112f47181ec558a7100a", - "name": "TestQuery1", - "type": "query", - "body": "query TestQuery1 {\n testing {\n id\n label\n description\n __typename\n} }" - }, - { - "id": "xyz-123", - "name": "TestQuery2", - "type": "mutation", - "body": "query TestQuery2 {\n testing2 {\n id2\n label\n description2\n __typename\n} }" - } - ] -} diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateClientTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateClientTest.ts deleted file mode 100644 index 768a5d69a93..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateClientTest.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { generateClient } from "../generateClient" - -it("returns generated code", function() { - var code = generateClient({ - path: "./src/__tests__/documents/*.graphql", - client: "test-client", - }) - expect(code).toMatchSnapshot() -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsClientTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsClientTest.ts deleted file mode 100644 index ef41c05cb4c..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsClientTest.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { generateClientCode, JS_TYPE, OperationStoreClient } from "../generateClient" -import fs from "fs" - -function withExampleClient(mapName: string, callback: (client: OperationStoreClient) => void) { - // Generate some code and write it to a file - var exampleOperations = [ - {name: "a", alias: "b", body: ""}, - {name: "c-d", alias: "e-f", body: ""} - ] - var jsCode = generateClientCode("example-client", exampleOperations, JS_TYPE) - var filename = "./src/sync/__tests__/" + mapName + ".js" - fs.writeFileSync(filename, jsCode) - - // Load the module and use it - var exampleModule = require("./" + mapName) - callback(exampleModule) - - // Clean up the generated file - fs.unlinkSync(filename) -} - -it("generates a valid JavaScript module that maps names to operations", () => { - withExampleClient("map1", (exampleClient) => { - // It does the mapping - expect(exampleClient.getPersistedQueryAlias("a")).toEqual("b") - expect(exampleClient.getPersistedQueryAlias("c-d")).toEqual("e-f") - // It returns a param - expect(exampleClient.getOperationId("a")).toEqual("example-client/b") - }) -}) - -it("generates an Apollo middleware", () => { - withExampleClient("map2", (exampleClient) => { - var nextWasCalled = false - var next = () => { - nextWasCalled = true - } - var req = { - operationName: "a", - query: "x", - operationId: "", - } - - exampleClient.apolloMiddleware.applyMiddleware({request: req}, next) - - expect(nextWasCalled).toEqual(true) - expect(req.query).toBeUndefined() - expect(req.operationId).toEqual("example-client/b") - }) -}) - -it("generates an Apollo Link", () => { - var fakeOperation = { - operationName: "a", - context: { http: {} }, - setContext: function(c: { http: object }) { - this.context = c - }, - extensions: { - operationId: "", - }, - } - - var forwardedOperation: object - var fakeForward = function(operation: object) { - forwardedOperation = operation - } - - withExampleClient("map3", (exampleClient) => { - exampleClient.apolloLink(fakeOperation, fakeForward) - - expect(fakeOperation.extensions.operationId).toEqual("example-client/b") - expect(fakeOperation.context.http).toEqual({includeQuery: false, includeExtensions: true}) - expect(forwardedOperation).toEqual(fakeOperation) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsonClientTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsonClientTest.ts deleted file mode 100644 index 56b916a7f7c..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/generateJsonClientTest.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { generateClientCode, JSON_TYPE } from "../generateClient" - -function withExampleClient(callback: (client: string) => void) { - // Generate some code and write it to a file - var exampleOperations = [ - {name: "a", alias: "b", body: ""}, - {name: "c-d", alias: "e-f", body: ""} - ] - - var json = generateClientCode("example-client", exampleOperations, JSON_TYPE) - - // Run callback with generated client - callback(json) -} - -it("generates a valid json object string that maps names to operations", () => { - withExampleClient((json) => { - expect(json).toMatchSnapshot() // String version - expect(JSON.parse(json)).toMatchSnapshot() // Object version (i.e., valid JSON) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareIsolatedFilesTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareIsolatedFilesTest.ts deleted file mode 100644 index 1388cce4ed7..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareIsolatedFilesTest.ts +++ /dev/null @@ -1,21 +0,0 @@ -import prepareIsolatedFiles from "../prepareIsolatedFiles" - -it("builds out single operations", () => { - var filenames = [ - "./src/__tests__/project/op_isolated_1.graphql", - "./src/__tests__/project/op_isolated_2.graphql", - ] - var ops = prepareIsolatedFiles(filenames, false) - expect(ops).toMatchSnapshot() -}) - -describe("with --add-typename", () => { - it("builds out single operations with __typename fields", () => { - var filenames = [ - "./src/__tests__/project/op_isolated_1.graphql", - "./src/__tests__/project/op_isolated_2.graphql", - ] - var ops = prepareIsolatedFiles(filenames, true) - expect(ops).toMatchSnapshot() - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/preparePersistedQueryListTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/preparePersistedQueryListTest.ts deleted file mode 100644 index 306a80c348c..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/preparePersistedQueryListTest.ts +++ /dev/null @@ -1,7 +0,0 @@ -import preparePersistedQueryList from "../preparePersistedQueryList" - -it("reads generate-persisted-query-manifest output", () => { - const manifestPath = "./src/sync/__tests__/generate-persisted-query-manifest.json" - var ops = preparePersistedQueryList(manifestPath) - expect(ops).toMatchSnapshot() -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareProjectTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareProjectTest.ts deleted file mode 100644 index 0ce0a8513c1..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/prepareProjectTest.ts +++ /dev/null @@ -1,40 +0,0 @@ -import prepareProject from "../prepareProject" - -describe("merging a project", () => { - it("builds out separate operations", () => { - var filenames = [ - "./src/__tests__/project/op_2.graphql", - "./src/__tests__/project/op_1.graphql", - "./src/__tests__/project/frag_1.graphql", - "./src/__tests__/project/frag_2.graphql", - "./src/__tests__/project/frag_3.graphql", - ] - var ops = prepareProject(filenames, false) - expect(ops).toMatchSnapshot() - }) - - describe("with --add-typename", () => { - it("builds out operation with __typename fields", () => { - var filenames = [ - "./src/__tests__/project/op_3.graphql", - "./src/__tests__/project/frag_2.graphql", - "./src/__tests__/project/frag_3.graphql", - "./src/__tests__/project/frag_4.graphql", - ] - var ops = prepareProject(filenames, true) - expect(ops).toMatchSnapshot() - }) - }) - - it("blows up on duplicate names", () => { - var filenames = [ - "./src/__tests__/documents/doc1.graphql", - "./src/__tests__/project/op_2.graphql", - "./src/__tests__/project/op_1.graphql", - "./src/__tests__/project/frag_1.graphql", - ] - expect(() => { - prepareProject(filenames, false) - }).toThrow("Found duplicate definition name: GetStuff, fragment & operation names must be unique to sync") - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/removeClientFieldsTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/removeClientFieldsTest.ts deleted file mode 100644 index 369b05b3b53..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/removeClientFieldsTest.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { removeClientFieldsFromString } from "../removeClientFields" - - -describe("removing @client fields", () => { - function normalizeString(str: string) { - return str.replace(/\s+/g, " ").trim() - } - - it("returns a string without any fields with @client", () => { - var newString = removeClientFieldsFromString("{ f1 f2 @client { a b } f3 { a b @client } }") - var expectedString = "{ f1 f3 { a } }" - expect(normalizeString(newString)).toEqual(expectedString) - }) - - it("leaves other strings unchanged", () => { - var originalString = "{ f1 f2 @other { a b } f3 { a b @notClient } }" - var newString = removeClientFieldsFromString(originalString) - expect(normalizeString(newString)).toEqual(originalString) - }) - - it("removes references to fragments that contain all client fields", () => { - var originalString = ` - { - f1 - ...Fragment1 - ... on Query { - f3 - ...Fragment2 - } - ...Fragment3 - } - - fragment Fragment1 on Query { - f2 @client - f3 - ...Fragment2 - } - - fragment Fragment2 on Query { - f4 @client - f5 @client - f6 @client { - f7 - f8 - } - } - - fragment Fragment3 on Query { - ...Fragment2 - } - ` - - var expectedString = ` - { - f1 - ...Fragment1 - ... on Query { - f3 - } - } - - fragment Fragment1 on Query { - f3 - } - ` - - var newString = removeClientFieldsFromString(originalString) - expect(normalizeString(newString)).toEqual(normalizeString(expectedString)) - }) - - it("removes now-unused variables", () => { - var newString = removeClientFieldsFromString("query($thing: ID!){ f1 f2(thing: $thing) @client }") - var expectedString = "{ f1 }" - expect(normalizeString(newString)).toEqual(expectedString) - }) - - it("removes fragments that are spread inside client fields", () => { - // from https://github.com/apollographql/apollo-client/pull/6892/ - var originalString = ` - query Simple { - networkField - field @client { - ...ClientFragment - } - } - fragment ClientFragment on Thing { - ...NestedFragment - } - fragment NestedFragment on Thing { - otherField - bar - }` - var expectedString = ` - query Simple { - networkField - } - ` - - var newString = removeClientFieldsFromString(originalString) - expect(normalizeString(newString)).toEqual(normalizeString(expectedString)) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/__tests__/sendPayloadTest.ts b/vendor/gems/graphql/javascript_client/src/sync/__tests__/sendPayloadTest.ts deleted file mode 100644 index 7892406973c..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/__tests__/sendPayloadTest.ts +++ /dev/null @@ -1,92 +0,0 @@ -jest.dontMock('nock'); -import nock from "nock" -import Logger from "../logger"; -import sendPayload from "../sendPayload" - -var fakeLogger = { - log: function() {}, - bright: function(str: string) { return str }, - colorize: function(str: string) { return str }, - red: function(str: string) { return str }, - green: function(str: string) { return str }, - error: function() {}, - isQuiet: true, -} as Logger - -describe("Posting GraphQL to OperationStore Endpoint", () => { - it("Posts to the specified URL", () => { - var mock = nock("http://example.com") - .post("/stored_operations/sync") - .reply(200, { "ok" : "ok" }) - - return sendPayload("payload", { url: "http://example.com/stored_operations/sync", logger: fakeLogger }).then(function() { - expect(mock.isDone()).toEqual(true) - }) - }) - - it("Uses HTTPS when provided", () => { - var mock = nock("https://example2.com") - .post("/stored_operations/sync") - .reply(200, { "ok" : "ok" }) - - return sendPayload("payload", { url: "https://example2.com/stored_operations/sync", logger: fakeLogger }).then(function() { - expect(mock.isDone()).toEqual(true) - }) - }) - - it("Uses auth, port, and query", () => { - var mock = nock("https://example2.com:229") - .post("/stored_operations/sync?q=1") - .basicAuth({ user: "username", pass: "pass" }) - .reply(200, { "ok" : "ok" }) - - return sendPayload("payload", { url: "https://username:pass@example2.com:229/stored_operations/sync?q=1", logger: fakeLogger }).then(function() { - expect(mock.isDone()).toEqual(true) - }) - }) - - it("Returns the response JSON to the promise", () => { - nock("http://example.com") - .post("/stored_operations/sync") - .reply(200, { result: "ok" }) - - return sendPayload("payload", { url: "http://example.com/stored_operations/sync", logger: fakeLogger }).then(function(response) { - expect(response).toEqual('{"result":"ok"}') - }) - }) - - it("Sends headers and changeset version", () => { - var mock = nock("http://example.com", { - reqheaders: { - thing: "Stuff", - "Changeset-Version": "2023-01-01", - } - }) - .post("/stored_operations/sync") - .reply(200, { result: "ok" }) - - return sendPayload("payload", { url: "http://example.com/stored_operations/sync", logger: fakeLogger, headers: { thing: "Stuff" }, changesetVersion: "2023-01-01" }).then(function(_response) { - expect(mock.isDone()).toEqual(true) - }) - }) - - it("Adds an hmac-sha256 header if key is present", () => { - var payload = { "payload": [1,2,3] } - var key = "2f26b770ded2a04279bc4bf824ca54ac" - // ruby -ropenssl -e 'puts OpenSSL::HMAC.hexdigest("SHA256", "2f26b770ded2a04279bc4bf824ca54ac", "{\"payload\":[1,2,3]}")' - // f6eab31abc2fa446dbfd2e9c10a778aaffd4d0c1d62dd9513d6f7ea60557987c - var signature = "f6eab31abc2fa446dbfd2e9c10a778aaffd4d0c1d62dd9513d6f7ea60557987c" - var mock = nock("http://example.com", { - reqheaders: { - 'authorization': 'GraphQL::Pro Abc ' + signature - }}) - .post("/stored_operations/sync") - .reply(200, { result: "ok" }) - - var opts = {secret: key, client: "Abc", url: "http://example.com/stored_operations/sync", logger: fakeLogger } - return sendPayload(payload, opts).then(function(response) { - expect(response).toEqual('{"result":"ok"}') - expect(mock.isDone()).toEqual(true) - }) - }) -}) diff --git a/vendor/gems/graphql/javascript_client/src/sync/addTypenameToSelectionSet.ts b/vendor/gems/graphql/javascript_client/src/sync/addTypenameToSelectionSet.ts deleted file mode 100644 index cd843be8f64..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/addTypenameToSelectionSet.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { visit, ASTNode, FieldNode, InlineFragmentNode, Kind } from "graphql" - -const TYPENAME_FIELD: FieldNode = { - kind: Kind.FIELD, - name: { - kind: Kind.NAME, - value: "__typename", - }, - selectionSet: { - kind: Kind.SELECTION_SET, - selections: [] - } -} - -function addTypenameIfAbsent(node: FieldNode | InlineFragmentNode): undefined | FieldNode | InlineFragmentNode { - if (node.selectionSet) { - const alreadyHasThisField = node.selectionSet.selections.some(function(selection) { - return ( - selection.kind === "Field" && selection.name.value === "__typename" - ) - }) - - if (!alreadyHasThisField) { - return { - ...node, - selectionSet: { - ...node.selectionSet, - selections: [...node.selectionSet.selections, TYPENAME_FIELD] - } - } - } else { - return undefined - } - } else { - return undefined - } -} - -function addTypenameToSelectionSet(node: ASTNode) { - var visitor = { - Field: { - leave: addTypenameIfAbsent, - }, - InlineFragment: { - leave: addTypenameIfAbsent, - } - } - var newNode = visit(node, visitor) - return newNode -} - -export { - addTypenameToSelectionSet, - addTypenameIfAbsent -} diff --git a/vendor/gems/graphql/javascript_client/src/sync/dumpPayload.ts b/vendor/gems/graphql/javascript_client/src/sync/dumpPayload.ts deleted file mode 100644 index c49714ae55e..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/dumpPayload.ts +++ /dev/null @@ -1,14 +0,0 @@ -import fs from 'fs'; - -interface DumpPayloadOptions { - dumpPayload: string | true, -} - -export default function dumpPayload(payload: Object, options: DumpPayloadOptions) { - let payloadStr = JSON.stringify(payload, null, 2) + "\n" - if (options.dumpPayload == true) { - process.stdout.write(payloadStr) - } else { - fs.writeFileSync(options.dumpPayload, payloadStr, 'utf8') - } -} diff --git a/vendor/gems/graphql/javascript_client/src/sync/generateClient.ts b/vendor/gems/graphql/javascript_client/src/sync/generateClient.ts deleted file mode 100644 index bcd370e664a..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/generateClient.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { globSync } from "glob" -import prepareRelay from "./prepareRelay" -import prepareIsolatedFiles from './prepareIsolatedFiles' -import prepareProject from "./prepareProject" -import md5 from "./md5" - -import generateJs from "./outfileGenerators/js" -import generateJson from "./outfileGenerators/json" - -var JS_TYPE = "js"; -var JSON_TYPE = "json"; - -var generators = { - [JS_TYPE]: generateJs, - [JSON_TYPE]: generateJson, -}; - -interface GenerateClientCodeOptions { - path?: string // A glob to recursively search for `.graphql` files (Default is `./`) - mode?: string // If `"file"`, treat each file separately. If `"project"`, concatenate all files and extract each operation. If `"relay"`, treat it as relay-compiler output - addTypename?: boolean // Indicates if the "__typename" field are automatically added to your queries - clientType?: string // The type of the generated code (i.e., json, js) - client: string // the Client ID that these operations belong to - hash?: Function // A custom hash function for query strings with the signature `options.hash(string) => digest` (Default is `md5(string) => digest`) - verbose?: boolean // If true, print debug output -} - -interface OperationStoreClient { - getOperationId: (operationName: string) => string - getPersistedQueryAlias: (operationName: string) => string - apolloMiddleware: { applyMiddleware: (req: any, next: any) => any } - apolloLink: (operation: any, forward: any) => any -} - -/** - * Generate a JavaScript client module based on local `.graphql` files. - * - * See {gatherOperations} and {generateClientCode} for options. - * @return {String} The generated JavaScript code -*/ -function generateClient(options: GenerateClientCodeOptions): string { - var payload = gatherOperations(options) - var generatedCode = generateClientCode(options.client, payload.operations, options.clientType) - return generatedCode -} - -interface ClientOperation { - alias: string, - name?: string, - body: string, -} -/** - * Parse files in the specified path and generate an alias for each operation. -*/ -function gatherOperations(options: GenerateClientCodeOptions) { - var graphqlGlob = options.path || "./" - // Check for file ext already, add it if missing - var containsFileExt = graphqlGlob.indexOf(".graphql") > -1 || graphqlGlob.indexOf(".gql") > -1 - if (!containsFileExt) { - graphqlGlob = graphqlGlob + "**/*.graphql*" - } - var hashFunc = options.hash || md5 - var filesMode = options.mode || (graphqlGlob.indexOf("__generated__") > -1 ? "relay" : "project") - var addTypename = !!options.addTypename - var verbose = !!options.verbose - - var operations: ClientOperation[] = [] - - var filenames: string[] = globSync(graphqlGlob, {}).sort() - if (verbose) { - console.log("[Sync] glob: ", graphqlGlob) - console.log("[Sync] " + filenames.length + " files:") - console.log(filenames.map(function(f) { return "[Sync] - " + f }).join("\n")) - } - if (filesMode == "relay") { - operations = prepareRelay(filenames) - } else { - if (filesMode === "file") { - operations = prepareIsolatedFiles(filenames, addTypename) - } else if (filesMode === "project") { - operations = prepareProject(filenames, addTypename) - } else { - throw new Error("Unexpected mode: " + filesMode) - } - // Update the operations with the hash of the body - operations.forEach(function(op) { - op.alias = hashFunc(op.body) - // console.log("operation", op.alias, op.body) - }) - } - return { operations: operations } -} - -/** - * Given a map of { name => alias } pairs, generate outfile based on type. - * @param {String} clientName - the client ID that this map belongs to - * @param {Object} nameToAlias - `name => alias` pairs - * @param {String} type - the outfile's type - * @return {String} generated outfile code -*/ -function generateClientCode(clientName: string, operations: ClientOperation[], type?: string): string { - if (!clientName) { - throw new Error("Client name is required to generate a persisted alias lookup map"); - } - - var nameToAlias: {[key: string] : string | null} = {} - operations.forEach(function(op) { - // This can be blank from relay-perisisted-output, - // but typescript doesn't know that we don't use this function in that case - // (Er, I should make _two_ interfaces, but I haven't yet.) - if (op.name) { - nameToAlias[op.name] = op.alias - } - }) - - // Build up the map - var keyValuePairs = "{" - keyValuePairs += Object.keys(nameToAlias).map(function(operationName) { - var persistedAlias = nameToAlias[operationName] - return "\n \"" + operationName + "\": \"" + persistedAlias + "\"" - }).join(",") - keyValuePairs += "\n}" - - var outfileType = type || JS_TYPE - var generateOutfile = generators[outfileType]; - - if (!generateOutfile) { - throw new Error("Unknown generator type " + outfileType + " encountered for generating the outFile"); - } - - return generateOutfile(outfileType, clientName, keyValuePairs); -} - -export { - generateClient, - generateClientCode, - gatherOperations, - JS_TYPE, - JSON_TYPE, - ClientOperation, - OperationStoreClient, -} diff --git a/vendor/gems/graphql/javascript_client/src/sync/index.ts b/vendor/gems/graphql/javascript_client/src/sync/index.ts deleted file mode 100644 index ee5a8a8e2dd..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/index.ts +++ /dev/null @@ -1,262 +0,0 @@ -import sendPayload from "./sendPayload" -import dumpPayload from "./dumpPayload" -import { generateClientCode, gatherOperations, ClientOperation } from "./generateClient" -import Logger from "./logger" -import fs from "fs" -import { removeClientFieldsFromString } from "./removeClientFields" -import preparePersistedQueryList from "./preparePersistedQueryList" - -export interface SyncOptions { - path?: string, - relayPersistedOutput?: string, - apolloAndroidOperationOutput?: string, - apolloCodegenJsonOutput?: string, - apolloPersistedQueryManifest?: string, - secret?: string - url?: string, - mode?: string, - dumpPayload?: string | true, - outfile?: string, - outfileType?: string, - client: string, - send?: Function, - hash?: Function, - verbose?: boolean, - quiet?: boolean, - addTypename?: boolean, - changesetVersion?: string, - headers?: {[key: string]: string}, -} -/** - * Find `.graphql` files in `path`, - * then prepare them & send them to the configured endpoint. - * - * @param {Object} options - * @param {String} options.path - A glob to recursively search for `.graphql` files (Default is `./`) - * @param {String} options.relayPersistedOutput - A path to a `.json` file from `relay-compiler`'s `--persist-output` option - * @param {String} options.apolloCodegenJsonOutput - A path to a `.json` file from `apollo client:codegen ... --type json` - * @param {String} options.apolloPersistedQueryManifest - A path to a `.json` file from `generate-persisted-query-manifest` - * @param {String} options.secret - HMAC-SHA256 key which must match the server secret (default is no encryption) - * @param {String} options.url - Target URL for sending prepared queries. If omitted, then an outfile is generated without sending operations to the server. - * @param {String} options.mode - If `"file"`, treat each file separately. If `"project"`, concatenate all files and extract each operation. If `"relay"`, treat it as relay-compiler output - * @param {Boolean} options.addTypename - Indicates if the "__typename" field are automatically added to your queries - * @param {String} options.outfile - Where the generated code should be written - * @param {String} options.outfileType - The type of the generated code (i.e., json, js) - * @param {String} options.client - the Client ID that these operations belong to - * @param {Function} options.send - A function for sending the payload to the server, with the signature `options.send(payload)`. (Default is an HTTP `POST` request) - * @param {Function} options.hash - A custom hash function for query strings with the signature `options.hash(string) => digest` (Default is `md5(string) => digest`) - * @param {Boolean} options.verbose - If true, log debug output - * @param {Object} options.headers - If present, extra headers to add to the HTTP request - * @param {String|true} options.dumpPayload - If a filename is given, write the HTTP Post data to that file. If present without a filename, print it to stdout. - * @param {String} options.changesetVersion - If present, sent to populate `context[:changeset_version]` on the server - * @return {Promise} Rejects with an Error or String if something goes wrong. Resolves with the operation payload if successful. -*/ -function sync(options: SyncOptions) { - var logger = new Logger(!!options.quiet) - var verbose = !!options.verbose - var url = options.url - var dumpingPayload = "dumpPayload" in options - var dumpingToStdout = options.dumpPayload == true - if (!url && !dumpingPayload) { - logger.log("No URL; Generating artifacts without syncing them") - } - var clientName = options.client - if (!clientName) { - throw new Error("Client name must be provided for sync") - } - var encryptionKey = options.secret - if (encryptionKey && options.dumpPayload != null) { - logger.log("Authenticating with HMAC") - } - - var graphqlGlob = options.path - var hashFunc = options.hash - var sendFunc = options.send || (dumpingPayload ? dumpPayload : sendPayload) - var gatherMode = options.mode - var clientType = options.outfileType - if (options.relayPersistedOutput) { - // relay-compiler has already generated an artifact for us - var payload: { operations: ClientOperation[] } = { operations: [] } - var relayOutputText = fs.readFileSync(options.relayPersistedOutput, "utf8") - var relayOutput = JSON.parse(relayOutputText) - var operationBody - for (var hash in relayOutput) { - operationBody = relayOutput[hash] - payload.operations.push({ - body: operationBody, - alias: hash, - }) - } - } else if (options.apolloAndroidOperationOutput) { - // Apollo Android has already generated an artifact (https://www.apollographql.com/docs/android/advanced/persisted-queries/#operationoutputjson) - var payload: { operations: ClientOperation[] } = { operations: [] } - var apolloAndroidOutputText = fs.readFileSync(options.apolloAndroidOperationOutput, "utf8") - var apolloAndroidOutput = JSON.parse(apolloAndroidOutputText) - var operationData - // Structure is { operationId => { "name" => "...", "source" => "query { ... } " } } - for (var operationId in apolloAndroidOutput) { - operationData = apolloAndroidOutput[operationId] - let bodyWithoutClientFields = removeClientFieldsFromString(operationData.source) - payload.operations.push({ - body: bodyWithoutClientFields, - alias: operationId, - }) - } - } else if (options.apolloCodegenJsonOutput) { - var payload: { operations: ClientOperation[] } = { operations: [] } - const jsonText = fs.readFileSync(options.apolloCodegenJsonOutput).toString() - const jsonData = JSON.parse(jsonText) - jsonData.operations.map(function(operation: {operationId: string, operationName: string, sourceWithFragments: string}) { - const bodyWithoutClientFields = removeClientFieldsFromString(operation.sourceWithFragments) - payload.operations.push({ - alias: operation.operationId, - name: operation.operationName, - body: bodyWithoutClientFields, - }) - }) - } else if (options.apolloPersistedQueryManifest) { - var payload: { operations: ClientOperation[] } = { - operations: preparePersistedQueryList(options.apolloPersistedQueryManifest) - } - } else { - var payload = gatherOperations({ - path: graphqlGlob, - hash: hashFunc, - mode: gatherMode, - addTypename: !!options.addTypename, - clientType: clientType, - client: clientName, - verbose: verbose, - }) - } - - var outfile: string | null - if (options.outfile) { - outfile = options.outfile - } else if (options.relayPersistedOutput || options.apolloAndroidOperationOutput || options.apolloCodegenJsonOutput || options.apolloPersistedQueryManifest) { - // These artifacts have embedded IDs in its generated files, - // no need to generate an outfile. - outfile = null - } else if (fs.existsSync("src")) { - outfile = "src/OperationStoreClient.js" - } else { - outfile = "OperationStoreClient.js" - } - - var syncPromise = new Promise(function(resolve, reject) { - if (payload.operations.length === 0) { - logger.log("No operations found in " + options.path + ", not syncing anything") - resolve(null) - return - } else if (url) { - logger.log("Syncing " + payload.operations.length + " operations to " + logger.bright(url) + "...") - var sendOpts = { - url: url, - client: clientName, - secret: encryptionKey, - headers: options.headers, - changesetVersion: options.changesetVersion, - logger: logger, - } - var sendPromise = Promise.resolve(sendFunc(payload, sendOpts)) - return sendPromise.then(function(response) { - var responseData - if (response) { - try { - responseData = JSON.parse(response) - var aliasToNameMap: {[key: string] : string | undefined} = {} - - payload.operations.forEach(function(op) { - aliasToNameMap[op.alias] = op.name - }) - - var failed = responseData.failed.length - // These might get overridden for status output - var notModified = responseData.not_modified.length - var added = responseData.added.length - if (failed) { - // Override these to reflect reality - notModified = 0 - added = 0 - } - - var addedColor = added ? "green" : "dim" - logger.log(" " + logger.colorize(addedColor, added + " added")) - var notModifiedColor = notModified ? "reset" : "dim" - - logger.log(" " + logger.colorize(notModifiedColor, notModified + " not modified")) - var failedColor = failed ? "red" : "dim" - logger.log(" " + logger.colorize(failedColor, failed + " failed")) - - if (failed) { - logger.error("Sync failed, errors:") - var failedOperationAlias: string - var failedOperationName: string - var errors - var allErrors: string[] = [] - for (failedOperationAlias in responseData.errors) { - failedOperationName = aliasToNameMap[failedOperationAlias] || failedOperationAlias - logger.error(" " + failedOperationName + ":") - errors = responseData.errors[failedOperationAlias] - errors.forEach(function(errMessage: string) { - allErrors.push(failedOperationName + ": " + errMessage) - logger.error(" " + logger.red("✘") + " " + errMessage) - }) - } - reject("Sync failed: " + allErrors.join(", ")) - return - } - } catch (err) { - logger.log("Failed to print sync result:", err as string) - reject(err) - return - } - } - resolve(payload) - return - }).catch(function(err) { - logger.error(logger.red("Sync failed:")) - logger.error(err) - reject(err) - return - }) - } else if (dumpingPayload) { - sendFunc(payload, { dumpPayload: options.dumpPayload }) - resolve(payload) - return - } else { - // This is a local-only run to generate an artifact - resolve(payload) - return - } - }) - - return syncPromise.then(function(_payload) { - // The payload is yielded when sync was successful, but typescript had - // trouble using it from ^^ here. So instead, just use its presence as a signal to continue. - - // Don't generate a new file when we're using relay-compiler's --persist-output - if (_payload && outfile) { - var generatedCode = generateClientCode(clientName, payload.operations, clientType) - var finishedPayload = { - operations: payload.operations, - generatedCode, - } - if (!dumpingToStdout) { - logger.log("Generating client module in " + logger.colorize("bright", outfile) + "...") - } - fs.writeFileSync(outfile, generatedCode, "utf8") - if (!dumpingToStdout) { - logger.log(logger.green("✓ Done!")) - } - return finishedPayload - } else { - if (!dumpingToStdout) { - logger.log(logger.green("✓ Done!")) - } - return payload - } - }) -} - -export default sync diff --git a/vendor/gems/graphql/javascript_client/src/sync/logger.ts b/vendor/gems/graphql/javascript_client/src/sync/logger.ts deleted file mode 100644 index af763b5efc9..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/logger.ts +++ /dev/null @@ -1,52 +0,0 @@ -class Logger { - isQuiet: boolean - - constructor(isQuiet: boolean) { - this.isQuiet = isQuiet - } - - log(...args: string[]) { - this.isQuiet ? null : console.log(...args) - } - - error(...args: string[]) { - this.isQuiet ? null : console.error(...args) - } - - colorize(color: string, text: string) { - var prefix = colors[color] - if (!prefix) { - throw new Error("No color named: " + color) - } - return prefix + text + colors.reset - } - - // Shortcuts to `.colorize`, add more as-needed. - - red(text: string) { - return this.colorize("red", text) - } - - green(text: string) { - return this.colorize("green", text) - } - - bright(text: string) { - return this.colorize("bright", text) - } -} - - -const colors: {[key: string]: string} = { - yellow: "\x1b[33m", - red: "\x1b[31m", - green: "\x1b[32m", - blue: "\x1b[34m", - magenta: "\x1b[35m", - cyan: "\x1b[36m", - reset: "\x1b[0m", - bright: "\x1b[1m", - dim: "\x1b[2m", -} - -export default Logger diff --git a/vendor/gems/graphql/javascript_client/src/sync/md5.ts b/vendor/gems/graphql/javascript_client/src/sync/md5.ts deleted file mode 100644 index 4b4b852d202..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/md5.ts +++ /dev/null @@ -1,10 +0,0 @@ -import crypto from 'crypto' - -// Return the hex-encoded md5 hash of `inputString` -function md5(inputString: string): string { - return crypto.createHash("md5") - .update(inputString) - .digest("hex") -} - -export default md5 diff --git a/vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/js.ts b/vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/js.ts deleted file mode 100644 index c83a9e07f13..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/js.ts +++ /dev/null @@ -1,99 +0,0 @@ -function generateOutfile(_type: string, clientName: string, keyValuePairs: string) { - return ` - /** - * Generated by graphql-ruby-client - * - */ - - /** - * Map local operation names to persisted keys on the server - * @return {Object} - * @private - */ - var _aliases = ${keyValuePairs} - - /** - * The client who synced these operations with the server - * @return {String} - * @private - */ - var _client = "${clientName}" - - var OperationStoreClient = { - /** - * Build a string for \`params[:operationId]\` - * @param {String} operationName - * @return {String} stored operation ID - */ - getOperationId: function(operationName) { - return _client + "/" + OperationStoreClient.getPersistedQueryAlias(operationName) - }, - - /** - * Fetch a persisted alias from a local operation name - * @param {String} operationName - * @return {String} persisted alias - */ - getPersistedQueryAlias: function(operationName) { - var persistedAlias = _aliases[operationName] - if (!persistedAlias) { - throw new Error("Failed to find persisted alias for operation name: " + operationName) - } else { - return persistedAlias - } - }, - - /** - * Satisfy the Apollo Link API. - * This link checks for an operation name, and if it's present, - * sets the HTTP context to _not_ include the query, - * and instead, include \`extensions.operationId\`. - * (This is inspired by apollo-link-persisted-queries.) - */ - apolloLink: function(operation, forward) { - if (operation.operationName) { - const operationId = OperationStoreClient.getOperationId(operation.operationName) - operation.setContext({ - http: { - includeQuery: false, - includeExtensions: true, - } - }) - operation.extensions.operationId = operationId - } - return forward(operation) - }, - /** - * Satisfy the Apollo middleware API. - * Replace the query with an operationId - */ - apolloMiddleware: { - applyBatchMiddleware: function(options, next) { - options.requests.forEach(function(req) { - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - return req - }) - // Continue the request - next() - }, - - applyMiddleware: function(options, next) { - var req = options.request - // Fetch the persisted alias for this operation - req.operationId = OperationStoreClient.getOperationId(req.operationName) - // Remove the now-unused query string - delete req.query - // Continue the request - next() - } - } - } - - module.exports = OperationStoreClient - ` -} - -export default generateOutfile; diff --git a/vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/json.ts b/vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/json.ts deleted file mode 100644 index d7c5b01ff2e..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/outfileGenerators/json.ts +++ /dev/null @@ -1,5 +0,0 @@ -function generateOutfile(_type: string, _clientName: string, keyValuePairs: string) { - return `${keyValuePairs}` -} - -export default generateOutfile; diff --git a/vendor/gems/graphql/javascript_client/src/sync/prepareIsolatedFiles.ts b/vendor/gems/graphql/javascript_client/src/sync/prepareIsolatedFiles.ts deleted file mode 100644 index 7d580da0a4e..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/prepareIsolatedFiles.ts +++ /dev/null @@ -1,47 +0,0 @@ -import fs from "fs" -import {parse, visit, print, OperationDefinitionNode} from "graphql" -import {addTypenameIfAbsent} from "./addTypenameToSelectionSet" -import { removeClientFields } from "./removeClientFields" - -/** - * Read a bunch of GraphQL files and treat them as islands. - * Don't join any fragments from other files. - * Don't make assertions about name uniqueness. - * - */ -function prepareIsolatedFiles(filenames: string[], addTypename: boolean) { - return filenames.map(function(filename) { - var fileOperationBody = fs.readFileSync(filename, "utf8") - var fileOperationName = "" - - var ast = parse(fileOperationBody) - var visitor = { - OperationDefinition: { - enter: function(node: OperationDefinitionNode) { - if (fileOperationName.length > 0) { - throw new Error("Found multiple operations in " + filename + ": " + fileOperationName + ", " + node.name + ". Files must contain only one operation") - } else if (node.name && node.name.value) { - fileOperationName = node.name.value - } - }, - }, - InlineFragment: { - leave: addTypename ? addTypenameIfAbsent : () => {} - }, - Field: { - leave: addTypename ? addTypenameIfAbsent : () => {} - } - } - ast = visit(ast, visitor) - ast = removeClientFields(ast) - - return { - // populate alias later, when hashFunc is available - alias: "", - name: fileOperationName, - body: print(ast), - } - }) -} - -export default prepareIsolatedFiles diff --git a/vendor/gems/graphql/javascript_client/src/sync/preparePersistedQueryList.ts b/vendor/gems/graphql/javascript_client/src/sync/preparePersistedQueryList.ts deleted file mode 100644 index 68d53ff7b04..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/preparePersistedQueryList.ts +++ /dev/null @@ -1,15 +0,0 @@ -import fs from "fs" - -// Transform the output from generate-persisted-query-manifest -// to something that OperationStore `sync` can use. -export default function preparePersistedQueryList(pqlPath: string) { - const pqlString = fs.readFileSync(pqlPath, "utf8") - const pqlJson = JSON.parse(pqlString) - return pqlJson.operations.map(function(persistedQueryConfig: { body: string, id: string, name: string, type: string }) { - return { - body: persistedQueryConfig.body, - alias: persistedQueryConfig.id, - name: persistedQueryConfig.name - } - }) -} diff --git a/vendor/gems/graphql/javascript_client/src/sync/prepareProject.ts b/vendor/gems/graphql/javascript_client/src/sync/prepareProject.ts deleted file mode 100644 index 567312610ff..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/prepareProject.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { addTypenameIfAbsent } from "./addTypenameToSelectionSet"; -import fs from "fs" -import {parse, visit, print, OperationDefinitionNode, FragmentDefinitionNode, FragmentSpreadNode, DocumentNode} from "graphql" -import { removeClientFields } from "./removeClientFields"; - -/** - * Take a whole bunch of GraphQL in one big string - * and validate it, especially: - * - * - operation names are unique - * - fragment names are unique - * - * Then, split each operation into a free-standing document, - * so it has all the fragments it needs. - */ - -function prepareProject(filenames: string[], addTypename: boolean) { - if(!filenames.length) { return []; } - var allGraphQL = "" - filenames.forEach(function(filename) { - allGraphQL += fs.readFileSync(filename) - }) - - var ast = parse(allGraphQL) - - // This will contain { name: [name, name] } pairs - var definitionDependencyNames: {[key: string] : string[] } = {} - var allOperationNames: string[] = [] - var currentDependencyNames = null - - // When entering a fragment or operation, - // start recording its dependencies - var enterDefinition = function(node: FragmentDefinitionNode | OperationDefinitionNode) { - // Technically, it could be an anonymous definition - if (node.name) { - var definitionName = node.name.value - if (definitionDependencyNames[definitionName]) { - throw new Error("Found duplicate definition name: " + definitionName + ", fragment & operation names must be unique to sync") - } else { - currentDependencyNames = definitionDependencyNames[definitionName] = [] - } - } - } - - var visitor = { - OperationDefinition: { - enter: function(node: OperationDefinitionNode) { - enterDefinition(node) - node.name && allOperationNames.push(node.name.value) - }, - }, - FragmentDefinition: { - enter: enterDefinition, - }, - // When entering a fragment spread, register it as a - // dependency of its context - FragmentSpread: { - enter: function(node: FragmentSpreadNode) { - currentDependencyNames.push(node.name.value) - } - }, - Field: { - leave: addTypename ? addTypenameIfAbsent : () => {} - }, - InlineFragment: { - leave: addTypename ? addTypenameIfAbsent : () => {} - } - } - - // Find the dependencies, build the accumulator - ast = visit(ast, visitor) - ast = removeClientFields(ast) - // For each operation, build a separate document of that operation and its deps - // then print the new document to a string - var operations = allOperationNames.map(function(operationName) { - var visitedDepNames: string[] = [] - var depNamesToVisit = [operationName] - - var depName - while (depNamesToVisit.length > 0) { - depName = depNamesToVisit.shift() - if (depName) { - visitedDepNames.push(depName) - definitionDependencyNames[depName].forEach(function(nextDepName) { - if (visitedDepNames.indexOf(nextDepName) === -1) { - depNamesToVisit.push(nextDepName) - } - }) - } - } - var newAST = extractDefinitions(ast, visitedDepNames) - return { - name: operationName, - body: print(newAST), - alias: "", // will be filled in later, when hashFunc is available - } - }) - - return operations -} - - -// Return a new AST which contains only `definitionNames` -function extractDefinitions(ast: DocumentNode, definitionNames: string[]) { - var removeDefinitionNode = function(node: FragmentDefinitionNode | OperationDefinitionNode) { - if (node.name && definitionNames.indexOf(node.name.value) === -1) { - return null - } else { - return undefined - } - } - var visitor = { - OperationDefinition: removeDefinitionNode, - FragmentDefinition: removeDefinitionNode, - } - - var newAST = visit(ast, visitor) - return newAST -} - -export default prepareProject diff --git a/vendor/gems/graphql/javascript_client/src/sync/prepareRelay.ts b/vendor/gems/graphql/javascript_client/src/sync/prepareRelay.ts deleted file mode 100644 index 5481c269f4b..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/prepareRelay.ts +++ /dev/null @@ -1,61 +0,0 @@ -import path from "path" -import fs from "fs" - -interface RelayCompilerOperation { - params?: RelayCompilerOperation - text: string - name: string -} -/** - * Read relay-compiler output - * and extract info for persisting them & writing a map: - * - * - alias: get the relayHash from the header - * - name: get the name from the JavaScript object - * - body: get the text from the JavaScript object - * - * @param {Array} filenames - Filenames to read - * @return {Array} List of operations to persist & write to a map - */ -function prepareRelay(filenames: string[]) { - var currentDirectory = process.cwd() - var operations = filenames.map(function(filename) { - // Search the file for the relayHash - var textContent = fs.readFileSync(filename, "utf8") - var operationAlias = textContent.match(/@relayHash ([a-z0-9]+)/) - // Only operations get `relayHash`, so - // skip over generated fragments - if (operationAlias) { - // Require the file to get values from the JavaScript code - var absoluteFilename = path.resolve(currentDirectory, filename) - var operation: RelayCompilerOperation = require(absoluteFilename) - var operationBody, operationName - // Support Relay version ^2.0.0 - if (operation.params) { - operationBody = operation.params.text - operationName = operation.params.name - } else { - // Support Relay versions < 2.0.0 - operationBody = operation.text - operationName = operation.name - } - - return { - alias: operationAlias[1], - name: operationName, - body: operationBody, - } - } else { - return { - alias: "", - name: "not-found", - body: "not-found", - } - } - }) - // Remove the nulls - var operationsWithoutNulls = operations.filter(function(o) { return o.alias.length }) - return operationsWithoutNulls -} - -export default prepareRelay diff --git a/vendor/gems/graphql/javascript_client/src/sync/removeClientFields.ts b/vendor/gems/graphql/javascript_client/src/sync/removeClientFields.ts deleted file mode 100644 index bad65bc518a..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/removeClientFields.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { parse, DocumentNode, VariableDefinitionNode, print, visit } from "graphql" - -function removeClientFields(node: DocumentNode) { - // Deleting fields can create invalid documents: - // - If variables were used by those fields (or their subfields), then their definitions are invalid - // - If a fragment contained only deleted fields, it is now empty and therefore invalid and should be deleted - // - If a fragment spread names a deleted fragment, it is now invalid - // - If a client field contained a fragment spread and it's deleted, then a fragment may be left unspread - - let anythingWasRemoved = false - const usedVariables: string[] = [] - let definedFragments: string[] = [] - let spreadFragments: string[] = [] - - // First pass: remove as much as possible, even if the document is left invalid. - // - remove fields that have @client - // - remove fragment definitions that become empty - let newDoc = visit(node, { - Field: { - enter: (node) => { - if (node.directives && node.directives.some((d) => { return d.name.value === "client" })) { - anythingWasRemoved = true - // Delete this node - return null - } else { - return undefined - } - } - }, - // FragmentSpread: ... Don't do this now, because we might find some in Fragment Definitions that are deleted later. - FragmentDefinition: { - leave: (node) => { - if (node.selectionSet.selections.length == 0) { - // All the fields in this fragment were removed - return null - } else { - definedFragments.push(node.name.value) - return undefined - } - } - }, - FragmentSpread: { - enter: (node) => { - spreadFragments.push(node.name.value) - } - }, - Variable: { - enter: (node, _key, parent) => { - if ((parent as VariableDefinitionNode).kind !== 'VariableDefinition') { - // This will only find variables that are used _after_ `@client` fields are deleted. - // (If `@client` fields are deleted, then their arguments aren't visited) - usedVariables.push(node.name.value) - } - }, - }, - }) - - if (anythingWasRemoved) { - // At this point, we can remove variables that aren't used. - newDoc = visit(newDoc, { - VariableDefinition: { - enter: (node) => { - if (!usedVariables.includes(node.variable.name.value)) { - return null - } else { - return undefined - } - } - }, - }) - - // Then, remove spreads of empty fragment definitions as long as we keep finding them - // Also remove definitions of fragments that aren't spread anymore - while (anythingWasRemoved) { - let previouslyDefinedFragments = definedFragments - let previouslySpreadFragments = spreadFragments - definedFragments = [] - spreadFragments = [] - anythingWasRemoved = false - newDoc = visit(newDoc, { - FragmentSpread: { - enter: (node) => { - if (!previouslyDefinedFragments.includes(node.name.value)) { - anythingWasRemoved = true - return null - } else { - spreadFragments.push(node.name.value) - return undefined - } - } - }, - FragmentDefinition: { - enter: (node) => { - if (node.selectionSet.selections.length == 0 || !previouslySpreadFragments.includes(node.name.value)) { - anythingWasRemoved = true - return null - } else { - definedFragments.push(node.name.value) - return undefined - } - } - } - }) - } - } - - return newDoc -} - -function removeClientFieldsFromString(body: string): string { - if (body.includes("@client")) { - const ast = parse(body) - const newAst = removeClientFields(ast) - return print(newAst) - } else { - return body - } -} - -export { - removeClientFields, - removeClientFieldsFromString -} diff --git a/vendor/gems/graphql/javascript_client/src/sync/sendPayload.ts b/vendor/gems/graphql/javascript_client/src/sync/sendPayload.ts deleted file mode 100644 index 6c082f6406a..00000000000 --- a/vendor/gems/graphql/javascript_client/src/sync/sendPayload.ts +++ /dev/null @@ -1,119 +0,0 @@ -import http from "http" -import https from "https" -import url from "url" -import crypto from 'crypto' -import Logger from './logger' - -interface SendPayloadOptions { - url: string, - logger: Logger, - secret?: string, - client?: string, - headers?: { [key: string]: string }, - changesetVersion?: string, -} -/** - * Use HTTP POST to send this payload to the endpoint. - * - * Override this function with `options.send` to use custom auth. - * - * @private - * @param {Object} payload - JS object to be posted as form data - * @param {String} options.url - Target URL - * @param {String} options.secret - (optional) used for HMAC header if provided - * @param {String} options.client - (optional) used for HMAC header if provided - * @param {Logger} options.logger - A logger for when `verbose` is true - * @param {Object} options.headers - (optional) extra headers for the request - * @return {Promise} -*/ -function sendPayload(payload: any, options: SendPayloadOptions) { - var syncUrl = options.url - var key = options.secret - var clientName = options.client - var logger = options.logger - // Prepare JS object as form data - var postData = JSON.stringify(payload) - - // Get parts of URL for request options - var parsedURL = url.parse(syncUrl) - - // Prep options for HTTP request - var defaultHeaders: {[key: string]: string} = { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(postData).toString() - } - - if (options.changesetVersion) { - logger.log("Changeset Version: ", logger.bright(options.changesetVersion)) - defaultHeaders["Changeset-Version"] = options.changesetVersion - } - var allHeaders = Object.assign({}, options.headers, defaultHeaders) - - var httpOptions = { - protocol: parsedURL.protocol, - hostname: parsedURL.hostname, - port: parsedURL.port, - path: parsedURL.path, - auth: parsedURL.auth, - method: 'POST', - headers: allHeaders, - }; - - // If an auth key was provided, add a HMAC header - var authDigest = null - if (key) { - authDigest = crypto.createHmac('sha256', key) - .update(postData) - .digest('hex') - var header = "GraphQL::Pro " + clientName + " " + authDigest - httpOptions.headers["Authorization"] = header - } - - var headerNames = Object.keys(httpOptions.headers) - logger.log("[Sync] " + headerNames.length + " Headers:") - headerNames.forEach((headerName) => { - logger.log("[Sync] " + headerName + ": " + httpOptions.headers[headerName]) - }) - logger.log("[Sync] Data:", postData) - - var httpClient = parsedURL.protocol === "https:" ? https : http - var promise = new Promise(function(resolve, reject) { - // Make the request, - // hook up response handler - const req = httpClient.request(httpOptions, (res) => { - res.setEncoding('utf8'); - // Gather the response from the server - var body = "" - res.on('data', (chunk) => { - body += chunk - }); - - res.on("end", () => { - logger.log("[Sync] Response Headers: ", JSON.stringify(res.headers)) - logger.log("[Sync] Response Body: ", body) - - var status = res.statusCode - // 422 gets special treatment because - // the body has error messages - if (status && status > 299 && status != 422) { - reject(" Server responded with " + res.statusCode) - } else { - resolve(body) - } - }) - }); - - req.on('error', (e) => { - reject(e) - }); - - // Send the data, fire the request - req.write(postData); - req.end(); - }) - - return promise -} - - -export default sendPayload diff --git a/vendor/gems/graphql/javascript_client/tsconfig.json b/vendor/gems/graphql/javascript_client/tsconfig.json deleted file mode 100644 index 88634cf02f9..00000000000 --- a/vendor/gems/graphql/javascript_client/tsconfig.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "include": ["./src"], - "exclude": ["**/*.js"], - "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./", /* Redirect output structure to the directory. */ - "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} diff --git a/vendor/gems/graphql/lib/generators/graphql/core.rb b/vendor/gems/graphql/lib/generators/graphql/core.rb deleted file mode 100644 index 09628a59567..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/core.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators/base' - -module Graphql - module Generators - module Core - def self.included(base) - base.send( - :class_option, - :directory, - type: :string, - default: "app/graphql", - desc: "Directory where generated files should be saved" - ) - end - - def insert_root_type(type, name) - log :add_root_type, type - sentinel = /< GraphQL::Schema\s*\n/m - - in_root do - if File.exist?(schema_file_path) - inject_into_file schema_file_path, " #{type}(Types::#{name})\n", after: sentinel, verbose: false, force: false - end - end - end - - def schema_file_path - "#{options[:directory]}/#{schema_name.underscore}.rb" - end - - def create_dir(dir) - empty_directory(dir) - if !options[:skip_keeps] - create_file("#{dir}/.keep") - end - end - - def module_namespacing_when_supported - if defined?(module_namespacing) - module_namespacing { yield } - else - yield - end - end - - private - - def schema_name - @schema_name ||= begin - if options[:schema] - options[:schema] - else - "#{parent_name}Schema" - end - end - end - - def parent_name - require File.expand_path("config/application", destination_root) - if Rails.application.class.respond_to?(:module_parent_name) - Rails.application.class.module_parent_name - else - Rails.application.class.parent_name - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/enum_generator.rb b/vendor/gems/graphql/lib/generators/graphql/enum_generator.rb deleted file mode 100644 index 50685b36e5f..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/enum_generator.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -require 'generators/graphql/type_generator' - -module Graphql - module Generators - # Generate an enum type by name, with the given values. - # To add a `value:` option, add another value after a `:`. - # - # ``` - # rails g graphql:enum ProgrammingLanguage RUBY PYTHON PERL PERL6:"PERL" - # ``` - class EnumGenerator < TypeGeneratorBase - desc "Create a GraphQL::EnumType with the given name and values" - source_root File.expand_path('../templates', __FILE__) - - private - - def graphql_type - "enum" - end - - def prepared_values - custom_fields.map { |v| v.split(":", 2) } - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/field_extractor.rb b/vendor/gems/graphql/lib/generators/graphql/field_extractor.rb deleted file mode 100644 index 8d39b35fc7a..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/field_extractor.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators/base' - -module Graphql - module Generators - module FieldExtractor - def fields - columns = [] - columns += (klass&.columns&.map { |c| generate_column_string(c) } || []) - columns + custom_fields - end - - def generate_column_string(column) - name = column.name - required = column.null ? "" : "!" - type = column_type_string(column) - "#{name}:#{required}#{type}" - end - - def column_type_string(column) - column.name == "id" ? "ID" : column.type.to_s.camelize - end - - def klass - @klass ||= Module.const_get(name.camelize) - rescue NameError - @klass = nil - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/input_generator.rb b/vendor/gems/graphql/lib/generators/graphql/input_generator.rb deleted file mode 100644 index ac6d3674bf3..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/input_generator.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true -require 'generators/graphql/type_generator' -require 'generators/graphql/field_extractor' - -module Graphql - module Generators - # Generate an input type by name, - # with the specified fields. - # - # ``` - # rails g graphql:object PostType name:string! - # ``` - class InputGenerator < TypeGeneratorBase - desc "Create a GraphQL::InputObjectType with the given name and fields" - source_root File.expand_path('../templates', __FILE__) - include FieldExtractor - - def self.normalize_type_expression(type_expression, mode:, null: true) - case type_expression.camelize - when "Text", "Citext" - ["String", null] - when "Decimal" - ["Float", null] - when "DateTime", "Datetime" - ["GraphQL::Types::ISO8601DateTime", null] - when "Date" - ["GraphQL::Types::ISO8601Date", null] - when "Json", "Jsonb", "Hstore" - ["GraphQL::Types::JSON", null] - else - super - end - end - - private - - def graphql_type - "input" - end - - def type_ruby_name - super.gsub(/Type\z/, "InputType") - end - - def type_file_name - super.gsub(/_type\z/, "_input_type") - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/install/mutation_root_generator.rb b/vendor/gems/graphql/lib/generators/graphql/install/mutation_root_generator.rb deleted file mode 100644 index 5b506bafc15..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/install/mutation_root_generator.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -require "rails/generators/base" -require_relative "../core" - -module Graphql - module Generators - module Install - class MutationRootGenerator < Rails::Generators::Base - include Core - - desc "Create mutation base type, mutation root type, and adds the latter to the schema" - source_root File.expand_path('../templates', __FILE__) - - class_option :schema, - type: :string, - default: nil, - desc: "Name for the schema constant (default: {app_name}Schema)" - - class_option :skip_keeps, - type: :boolean, - default: false, - desc: "Skip .keep files for source control" - - def generate - create_dir("#{options[:directory]}/mutations") - template("base_mutation.erb", "#{options[:directory]}/mutations/base_mutation.rb", { skip: true }) - template("mutation_type.erb", "#{options[:directory]}/types/mutation_type.rb", { skip: true }) - insert_root_type('mutation', 'MutationType') - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/install/templates/base_mutation.erb b/vendor/gems/graphql/lib/generators/graphql/install/templates/base_mutation.erb deleted file mode 100644 index 4bb26d978ca..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/install/templates/base_mutation.erb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Mutations - class BaseMutation < GraphQL::Schema::RelayClassicMutation - argument_class Types::BaseArgument - field_class Types::BaseField - input_object_class Types::BaseInputObject - object_class Types::BaseObject - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/install/templates/mutation_type.erb b/vendor/gems/graphql/lib/generators/graphql/install/templates/mutation_type.erb deleted file mode 100644 index 84fb4d3ea30..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/install/templates/mutation_type.erb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class MutationType < Types::BaseObject - # TODO: remove me - field :test_field, String, null: false, - description: "An example field added by the generator" - def test_field - "Hello World" - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/install_generator.rb b/vendor/gems/graphql/lib/generators/graphql/install_generator.rb deleted file mode 100644 index e93a15f9901..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/install_generator.rb +++ /dev/null @@ -1,246 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators' -require 'rails/generators/base' -require_relative 'core' -require_relative 'relay' - -module Graphql - module Generators - # Add GraphQL to a Rails app with `rails g graphql:install`. - # - # Setup a folder structure for GraphQL: - # - # ``` - # - app/ - # - graphql/ - # - resolvers/ - # - types/ - # - base_argument.rb - # - base_field.rb - # - base_enum.rb - # - base_input_object.rb - # - base_interface.rb - # - base_object.rb - # - base_scalar.rb - # - base_union.rb - # - query_type.rb - # - loaders/ - # - mutations/ - # - base_mutation.rb - # - {app_name}_schema.rb - # ``` - # - # (Add `.gitkeep`s by default, support `--skip-keeps`) - # - # Add a controller for serving GraphQL queries: - # - # ``` - # app/controllers/graphql_controller.rb - # ``` - # - # Add a route for that controller: - # - # ```ruby - # # config/routes.rb - # post "/graphql", to: "graphql#execute" - # ``` - # - # Add ActiveRecord::QueryLogs metadata: - # ```ruby - # current_graphql_operation: -> { GraphQL::Current.operation_name }, - # current_graphql_field: -> { GraphQL::Current.field&.path }, - # current_dataloader_source: -> { GraphQL::Current.dataloader_source_class }, - # ``` - # - # Accept a `--batch` option which adds `GraphQL::Batch` setup. - # - # Use `--skip-graphiql` to skip `graphiql-rails` installation. - # - # TODO: also add base classes - class InstallGenerator < Rails::Generators::Base - include Core - include Relay - - desc "Install GraphQL folder structure and boilerplate code" - source_root File.expand_path('../templates', __FILE__) - - class_option :schema, - type: :string, - default: nil, - desc: "Name for the schema constant (default: {app_name}Schema)" - - class_option :skip_keeps, - type: :boolean, - default: false, - desc: "Skip .keep files for source control" - - class_option :skip_graphiql, - type: :boolean, - default: false, - desc: "Skip graphiql-rails installation" - - class_option :skip_mutation_root_type, - type: :boolean, - default: false, - desc: "Skip creation of the mutation root type" - - class_option :relay, - type: :boolean, - default: true, - desc: "Include installation of Relay conventions (nodes, connections, edges)" - - class_option :batch, - type: :boolean, - default: false, - desc: "Include GraphQL::Batch installation" - - class_option :playground, - type: :boolean, - default: false, - desc: "Use GraphQL Playground over Graphiql as IDE" - - class_option :skip_query_logs, - type: :boolean, - default: false, - desc: "Skip ActiveRecord::QueryLogs hooks in config/application.rb" - - # These two options are taken from Rails' own generators' - class_option :api, - type: :boolean, - desc: "Preconfigure smaller stack for API only apps" - - def create_folder_structure - create_dir("#{options[:directory]}/types") - template("schema.erb", schema_file_path) - - ["base_object", "base_argument", "base_field", "base_enum", "base_input_object", "base_interface", "base_scalar", "base_union"].each do |base_type| - template("#{base_type}.erb", "#{options[:directory]}/types/#{base_type}.rb") - end - - # All resolvers are defined as living in their own module, including this class. - template("base_resolver.erb", "#{options[:directory]}/resolvers/base_resolver.rb") - - # Note: You can't have a schema without the query type, otherwise introspection breaks - template("query_type.erb", "#{options[:directory]}/types/query_type.rb") - insert_root_type('query', 'QueryType') - - invoke "graphql:install:mutation_root" unless options.skip_mutation_root_type? - - template("graphql_controller.erb", "app/controllers/graphql_controller.rb") - route('post "/graphql", to: "graphql#execute"') - - if options[:batch] - gem("graphql-batch") - create_dir("#{options[:directory]}/loaders") - end - - if options.api? - say("Skipped graphiql, as this rails project is API only") - say(" You may wish to use GraphiQL.app for development: https://github.com/skevy/graphiql-app") - elsif !options[:skip_graphiql] - # `gem(...)` uses `gsub_file(...)` under the hood, which is a no-op for `rails destroy...` (when `behavior == :revoke`). - # So handle that case by calling `gsub_file` with `force: true`. - if behavior == :invoke && !File.read(Rails.root.join("Gemfile")).include?("graphiql-rails") - gem("graphiql-rails", group: :development) - elsif behavior == :revoke - gemfile_pattern = /\n\s*gem ('|")graphiql-rails('|"), :?group(:| =>) :development/ - gsub_file Rails.root.join("Gemfile"), gemfile_pattern, "", { force: true } - end - - # This is a little cheat just to get cleaner shell output: - log :route, 'graphiql-rails' - shell.mute do - # Rails 5.2 has better support for `route`? - if Rails::VERSION::STRING > "5.2" - route <<-RUBY -if Rails.env.development? - mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql" -end -RUBY - else - route <<-RUBY -if Rails.env.development? - mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql" - end -RUBY - end - end - end - - if options[:playground] - gem("graphql_playground-rails", group: :development) - - log :route, 'graphql_playground-rails' - shell.mute do - if Rails::VERSION::STRING > "5.2" - route <<-RUBY -if Rails.env.development? - mount GraphqlPlayground::Rails::Engine, at: "/playground", graphql_path: "/graphql" -end -RUBY - else - route <<-RUBY -if Rails.env.development? - mount GraphqlPlayground::Rails::Engine, at: "/playground", graphql_path: "/graphql" - end -RUBY - end - end - end - - if options[:relay] - install_relay - end - - if !options[:skip_query_logs] - config_file = "config/application.rb" - current_app_rb = File.read(Rails.root.join(config_file)) - existing_log_tags_pattern = /config.active_record.query_log_tags = \[\n?(\s*:[a-z_]+,?\s*\n?|\s*#[^\]]*\n)*/m - existing_log_tags = existing_log_tags_pattern.match(current_app_rb) - if existing_log_tags && behavior == :invoke - code = <<-RUBY - # GraphQL-Ruby query log tags: - current_graphql_operation: -> { GraphQL::Current.operation_name }, - current_graphql_field: -> { GraphQL::Current.field&.path }, - current_dataloader_source: -> { GraphQL::Current.dataloader_source_class }, -RUBY - if !existing_log_tags.to_s.end_with?(",") - code = ",\n#{code} " - end - # Try to insert this code _after_ any plain symbol entries in the array of query log tags: - after_code = existing_log_tags_pattern - else - code = <<-RUBY - config.active_record.query_log_tags_enabled = true - config.active_record.query_log_tags = [ - # Rails query log tags: - :application, :controller, :action, :job, - # GraphQL-Ruby query log tags: - current_graphql_operation: -> { GraphQL::Current.operation_name }, - current_graphql_field: -> { GraphQL::Current.field&.path }, - current_dataloader_source: -> { GraphQL::Current.dataloader_source_class }, - ] -RUBY - after_code = "class Application < Rails::Application\n" - end - insert_into_file(config_file, code, after: after_code) - end - - if gemfile_modified? - say "Gemfile has been modified, make sure you `bundle install`" - end - end - - private - - def gemfile_modified? - @gemfile_modified - end - - def gem(*args) - @gemfile_modified = true - super(*args) - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/interface_generator.rb b/vendor/gems/graphql/lib/generators/graphql/interface_generator.rb deleted file mode 100644 index 9828740017c..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/interface_generator.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -require 'generators/graphql/type_generator' - -module Graphql - module Generators - # Generate an interface type by name, - # with the specified fields. - # - # ``` - # rails g graphql:interface NamedEntityType name:String! - # ``` - class InterfaceGenerator < TypeGeneratorBase - desc "Create a GraphQL::InterfaceType with the given name and fields" - source_root File.expand_path('../templates', __FILE__) - - private - - def graphql_type - "interface" - end - - def fields - custom_fields - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/loader_generator.rb b/vendor/gems/graphql/lib/generators/graphql/loader_generator.rb deleted file mode 100644 index d953f005d7c..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/loader_generator.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators' -require "rails/generators/named_base" -require_relative "core" - -module Graphql - module Generators - # @example Generate a `GraphQL::Batch` loader by name. - # rails g graphql:loader RecordLoader - class LoaderGenerator < Rails::Generators::NamedBase - include Core - - desc "Create a GraphQL::Batch::Loader by name" - source_root File.expand_path('../templates', __FILE__) - - def create_loader_file - template "loader.erb", "#{options[:directory]}/loaders/#{file_path}.rb" - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/mutation_create_generator.rb b/vendor/gems/graphql/lib/generators/graphql/mutation_create_generator.rb deleted file mode 100644 index 049cb7284d2..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/mutation_create_generator.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -require_relative 'orm_mutations_base' - -module Graphql - module Generators - # TODO: What other options should be supported? - # - # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name - # rails g graphql:mutation CreatePostMutation - class MutationCreateGenerator < OrmMutationsBase - - desc "Scaffold a Relay Classic ORM create mutation for the given model class" - source_root File.expand_path('../templates', __FILE__) - - private - - def operation_type - "create" - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/mutation_delete_generator.rb b/vendor/gems/graphql/lib/generators/graphql/mutation_delete_generator.rb deleted file mode 100644 index bf33a7606b6..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/mutation_delete_generator.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -require_relative 'orm_mutations_base' - -module Graphql - module Generators - # TODO: What other options should be supported? - # - # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name - # rails g graphql:mutation DeletePostMutation - class MutationDeleteGenerator < OrmMutationsBase - - desc "Scaffold a Relay Classic ORM delete mutation for the given model class" - source_root File.expand_path('../templates', __FILE__) - - private - - def operation_type - "delete" - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/mutation_generator.rb b/vendor/gems/graphql/lib/generators/graphql/mutation_generator.rb deleted file mode 100644 index 5eebd2adaa1..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/mutation_generator.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators' -require 'rails/generators/named_base' -require_relative 'core' - -module Graphql - module Generators - # TODO: What other options should be supported? - # - # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name - # rails g graphql:mutation CreatePostMutation - class MutationGenerator < Rails::Generators::NamedBase - include Core - - desc "Create a Relay Classic mutation by name" - source_root File.expand_path('../templates', __FILE__) - - def create_mutation_file - template "mutation.erb", File.join(options[:directory], "/mutations/", class_path, "#{file_name}.rb") - - sentinel = /class .*MutationType\s*<\s*[^\s]+?\n/m - in_root do - path = "#{options[:directory]}/types/mutation_type.rb" - invoke "graphql:install:mutation_root" unless File.exist?(path) - inject_into_file "#{options[:directory]}/types/mutation_type.rb", " field :#{file_name}, mutation: Mutations::#{class_name}\n", after: sentinel, verbose: false, force: false - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/mutation_update_generator.rb b/vendor/gems/graphql/lib/generators/graphql/mutation_update_generator.rb deleted file mode 100644 index 6200dcc570a..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/mutation_update_generator.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -require_relative 'orm_mutations_base' - -module Graphql - module Generators - # TODO: What other options should be supported? - # - # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name - # rails g graphql:mutation UpdatePostMutation - class MutationUpdateGenerator < OrmMutationsBase - - desc "Scaffold a Relay Classic ORM update mutation for the given model class" - source_root File.expand_path('../templates', __FILE__) - - private - - def operation_type - "update" - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/object_generator.rb b/vendor/gems/graphql/lib/generators/graphql/object_generator.rb deleted file mode 100644 index 3cebb232670..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/object_generator.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true -require 'generators/graphql/type_generator' -require 'generators/graphql/field_extractor' - -module Graphql - module Generators - # Generate an object type by name, - # with the specified fields. - # - # ``` - # rails g graphql:object PostType name:String! - # ``` - # - # Add the Node interface with `--node`. - class ObjectGenerator < TypeGeneratorBase - desc "Create a GraphQL::ObjectType with the given name and fields." \ - "If the given type name matches an existing ActiveRecord model, the generated type will automatically include fields for the models database columns." - source_root File.expand_path('../templates', __FILE__) - include FieldExtractor - - class_option :node, - type: :boolean, - default: false, - desc: "Include the Relay Node interface" - - def self.normalize_type_expression(type_expression, mode:, null: true) - case type_expression.camelize - when "Text", "Citext" - ["String", null] - when "Decimal" - ["Float", null] - when "DateTime", "Datetime" - ["GraphQL::Types::ISO8601DateTime", null] - when "Date" - ["GraphQL::Types::ISO8601Date", null] - when "Json", "Jsonb", "Hstore" - ["GraphQL::Types::JSON", null] - else - super - end - end - - private - - def graphql_type - "object" - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/orm_mutations_base.rb b/vendor/gems/graphql/lib/generators/graphql/orm_mutations_base.rb deleted file mode 100644 index 74a35d163fb..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/orm_mutations_base.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators' -require 'rails/generators/named_base' -require_relative 'core' - -module Graphql - module Generators - # TODO: What other options should be supported? - # - # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name - # rails g graphql:mutation CreatePostMutation - class OrmMutationsBase < Rails::Generators::NamedBase - include Core - include Rails::Generators::ResourceHelpers - - desc "Create a Relay Classic mutation by name" - - class_option :orm, banner: "NAME", type: :string, required: true, - desc: "ORM to generate the controller for" - - class_option :namespaced_types, - type: :boolean, - required: false, - default: false, - banner: "Namespaced", - desc: "If the generated types will be namespaced" - - def create_mutation_file - template "mutation_#{operation_type}.erb", File.join(options[:directory], "/mutations/", class_path, "#{file_name}_#{operation_type}.rb") - - sentinel = /class .*MutationType\s*<\s*[^\s]+?\n/m - in_root do - path = "#{options[:directory]}/types/mutation_type.rb" - invoke "graphql:install:mutation_root" unless File.exist?(path) - inject_into_file "#{options[:directory]}/types/mutation_type.rb", " field :#{file_name}_#{operation_type}, mutation: Mutations::#{class_name}#{operation_type.classify}\n", after: sentinel, verbose: false, force: false - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/relay.rb b/vendor/gems/graphql/lib/generators/graphql/relay.rb deleted file mode 100644 index 86ab567fc68..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/relay.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true -module Graphql - module Generators - module Relay - def install_relay - # Add Node, `node(id:)`, and `nodes(ids:)` - template("node_type.erb", "#{options[:directory]}/types/node_type.rb") - in_root do - fields = <<-RUBY - field :node, Types::NodeType, null: true, description: "Fetches an object given its ID." do - argument :id, ID, required: true, description: "ID of the object." - end - - def node(id:) - context.schema.object_from_id(id, context) - end - - field :nodes, [Types::NodeType, null: true], null: true, description: "Fetches a list of objects given a list of IDs." do - argument :ids, [ID], required: true, description: "IDs of the objects." - end - - def nodes(ids:) - ids.map { |id| context.schema.object_from_id(id, context) } - end - - RUBY - inject_into_file "#{options[:directory]}/types/query_type.rb", fields, after: /class .*QueryType\s*<\s*[^\s]+?\n/m, force: false - end - - # Add connections and edges - template("base_connection.erb", "#{options[:directory]}/types/base_connection.rb") - template("base_edge.erb", "#{options[:directory]}/types/base_edge.rb") - connectionable_type_files = { - "#{options[:directory]}/types/base_object.rb" => /class .*BaseObject\s*<\s*[^\s]+?\n/m, - "#{options[:directory]}/types/base_union.rb" => /class .*BaseUnion\s*<\s*[^\s]+?\n/m, - "#{options[:directory]}/types/base_interface.rb" => /include GraphQL::Schema::Interface\n/m, - } - in_root do - connectionable_type_files.each do |type_class_file, sentinel| - inject_into_file type_class_file, " connection_type_class(Types::BaseConnection)\n", after: sentinel, force: false - inject_into_file type_class_file, " edge_type_class(Types::BaseEdge)\n", after: sentinel, force: false - end - end - - # Add object ID hooks & connection plugin - schema_code = <<-RUBY - - # Relay-style Object Identification: - - # Return a string UUID for `object` - def self.id_from_object(object, type_definition, query_ctx) - # For example, use Rails' GlobalID library (https://github.com/rails/globalid): - object.to_gid_param - end - - # Given a string UUID, find the object - def self.object_from_id(global_id, query_ctx) - # For example, use Rails' GlobalID library (https://github.com/rails/globalid): - GlobalID.find(global_id) - end -RUBY - inject_into_file schema_file_path, schema_code, before: /^end\n/m, force: false - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/relay_generator.rb b/vendor/gems/graphql/lib/generators/graphql/relay_generator.rb deleted file mode 100644 index a3ad8fa7557..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/relay_generator.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators' -require 'rails/generators/base' -require_relative 'core' -require_relative 'relay' - -module Graphql - module Generators - class RelayGenerator < Rails::Generators::Base - include Core - include Relay - - desc "Add base types and fields for Relay-style nodes and connections" - source_root File.expand_path('../templates', __FILE__) - - def install_relay - super - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/scalar_generator.rb b/vendor/gems/graphql/lib/generators/graphql/scalar_generator.rb deleted file mode 100644 index 0107dc803e8..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/scalar_generator.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -require 'generators/graphql/type_generator' - -module Graphql - module Generators - # Generate a scalar type by given name. - # - # ``` - # rails g graphql:scalar Date - # ``` - class ScalarGenerator < TypeGeneratorBase - desc "Create a GraphQL::ScalarType with the given name" - source_root File.expand_path('../templates', __FILE__) - - private - - def graphql_type - "scalar" - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_argument.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_argument.erb deleted file mode 100644 index e8519cd0c71..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_argument.erb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseArgument < GraphQL::Schema::Argument - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_connection.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_connection.erb deleted file mode 100644 index c98c60dc471..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_connection.erb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseConnection < Types::BaseObject - # add `nodes` and `pageInfo` fields, as well as `edge_type(...)` and `node_nullable(...)` overrides - include GraphQL::Types::Relay::ConnectionBehaviors - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_edge.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_edge.erb deleted file mode 100644 index a8ae98d27cd..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_edge.erb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseEdge < Types::BaseObject - # add `node` and `cursor` fields, as well as `node_type(...)` override - include GraphQL::Types::Relay::EdgeBehaviors - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_enum.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_enum.erb deleted file mode 100644 index 22804640ef0..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_enum.erb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseEnum < GraphQL::Schema::Enum - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_field.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_field.erb deleted file mode 100644 index aad5e3aefc5..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_field.erb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseField < GraphQL::Schema::Field - argument_class Types::BaseArgument - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_input_object.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_input_object.erb deleted file mode 100644 index 1aeb7011ca4..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_input_object.erb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseInputObject < GraphQL::Schema::InputObject - argument_class Types::BaseArgument - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_interface.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_interface.erb deleted file mode 100644 index c096abe3bdd..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_interface.erb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - module BaseInterface - include GraphQL::Schema::Interface - - field_class Types::BaseField - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_object.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_object.erb deleted file mode 100644 index cb68e9b43a7..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_object.erb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseObject < GraphQL::Schema::Object - field_class Types::BaseField - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_resolver.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_resolver.erb deleted file mode 100644 index 770c24cac7e..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_resolver.erb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Resolvers - class BaseResolver < GraphQL::Schema::Resolver - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_scalar.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_scalar.erb deleted file mode 100644 index ac291e2c118..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_scalar.erb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseScalar < GraphQL::Schema::Scalar - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/base_union.erb b/vendor/gems/graphql/lib/generators/graphql/templates/base_union.erb deleted file mode 100644 index 2793219c46c..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/base_union.erb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class BaseUnion < GraphQL::Schema::Union - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/enum.erb b/vendor/gems/graphql/lib/generators/graphql/templates/enum.erb deleted file mode 100644 index 8f607433afa..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/enum.erb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class <%= ruby_class_name %> < Types::BaseEnum - description "<%= human_name %> enum" - -<% prepared_values.each do |v| %> value "<%= v[0] %>"<%= v.length > 1 ? ", value: #{v[1]}" : "" %> -<% end %> end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/graphql_controller.erb b/vendor/gems/graphql/lib/generators/graphql/templates/graphql_controller.erb deleted file mode 100644 index 379f0436873..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/graphql_controller.erb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -class GraphqlController < ApplicationController - # If accessing from outside this domain, nullify the session - # This allows for outside API access while preventing CSRF attacks, - # but you'll have to authenticate your user separately - # protect_from_forgery with: :null_session - - def execute - variables = prepare_variables(params[:variables]) - query = params[:query] - operation_name = params[:operationName] - context = { - # Query context goes here, for example: - # current_user: current_user, - } - result = <%= schema_name %>.execute(query, variables: variables, context: context, operation_name: operation_name) - render json: result - rescue StandardError => e - raise e unless Rails.env.development? - handle_error_in_development(e) - end - - private - - # Handle variables in form data, JSON body, or a blank value - def prepare_variables(variables_param) - case variables_param - when String - if variables_param.present? - JSON.parse(variables_param) || {} - else - {} - end - when Hash - variables_param - when ActionController::Parameters - variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables. - when nil - {} - else - raise ArgumentError, "Unexpected parameter: #{variables_param}" - end - end - - def handle_error_in_development(e) - logger.error e.message - logger.error e.backtrace.join("\n") - - render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500 - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/input.erb b/vendor/gems/graphql/lib/generators/graphql/templates/input.erb deleted file mode 100644 index 371735f1155..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/input.erb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class <%= ruby_class_name %> < Types::BaseInputObject -<% normalized_fields.each do |f| %> <%= f.to_input_argument %> -<% end %> end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/interface.erb b/vendor/gems/graphql/lib/generators/graphql/templates/interface.erb deleted file mode 100644 index 4c269d1290e..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/interface.erb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - module <%= ruby_class_name %> - include Types::BaseInterface -<% normalized_fields.each do |f| %> <%= f.to_object_field %> -<% end %> end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/loader.erb b/vendor/gems/graphql/lib/generators/graphql/templates/loader.erb deleted file mode 100644 index face85eb773..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/loader.erb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Loaders - class <%= class_name %> < GraphQL::Batch::Loader - # Define `initialize` to store grouping arguments, eg - # - # Loaders::<%= class_name %>.for(group).load(value) - # - # def initialize() - # end - - # `keys` contains each key from `.load(key)`. - # Find the corresponding values, then - # call `fulfill(key, value)` or `fulfill(key, nil)` - # for each key. - def perform(keys) - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/mutation.erb b/vendor/gems/graphql/lib/generators/graphql/templates/mutation.erb deleted file mode 100644 index adf53ed3820..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/mutation.erb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Mutations - class <%= class_name %> < BaseMutation - # TODO: define return fields - # field :post, Types::PostType, null: false - - # TODO: define arguments - # argument :name, String, required: true - - # TODO: define resolve method - # def resolve(name:) - # { post: ... } - # end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/mutation_create.erb b/vendor/gems/graphql/lib/generators/graphql/templates/mutation_create.erb deleted file mode 100644 index cb7cd5615bb..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/mutation_create.erb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Mutations - class <%= class_name %>Create < BaseMutation - description "Creates a new <%= file_name %>" - - field :<%= file_name %>, Types::<%= options[:namespaced_types] ? 'Objects::' : '' %><%= class_name %>Type, null: false - - argument :<%= file_name %>_input, Types::<%= options[:namespaced_types] ? 'Inputs::' : '' %><%= class_name %>InputType, required: true - - def resolve(<%= file_name %>_input:) - <%= singular_table_name %> = ::<%= orm_class.build(class_name, "**#{file_name}_input") %> - raise GraphQL::ExecutionError.new "Error creating <%= file_name %>", extensions: <%= singular_table_name %>.errors.to_hash unless <%= orm_instance.save %> - - { <%= file_name %>: <%= singular_table_name %> } - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/mutation_delete.erb b/vendor/gems/graphql/lib/generators/graphql/templates/mutation_delete.erb deleted file mode 100644 index 53761e92410..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/mutation_delete.erb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Mutations - class <%= class_name %>Delete < BaseMutation - description "Deletes a <%= file_name %> by ID" - - field :<%= file_name %>, Types::<%= options[:namespaced_types] ? 'Objects::' : '' %><%= class_name %>Type, null: false - - argument :id, ID, required: true - - def resolve(id:) - <%= singular_table_name %> = ::<%= orm_class.find(class_name, "id") %> - raise GraphQL::ExecutionError.new "Error deleting <%= file_name %>", extensions: <%= singular_table_name %>.errors.to_hash unless <%= orm_instance.destroy %> - - { <%= file_name %>: <%= singular_table_name %> } - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/mutation_update.erb b/vendor/gems/graphql/lib/generators/graphql/templates/mutation_update.erb deleted file mode 100644 index cf4469b05f3..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/mutation_update.erb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Mutations - class <%= class_name %>Update < BaseMutation - description "Updates a <%= file_name %> by id" - - field :<%= file_name %>, Types::<%= options[:namespaced_types] ? 'Objects::' : '' %><%= class_name %>Type, null: false - - argument :id, ID, required: true - argument :<%= file_name %>_input, Types::<%= options[:namespaced_types] ? 'Inputs::' : '' %><%= class_name %>InputType, required: true - - def resolve(id:, <%= file_name %>_input:) - <%= singular_table_name %> = ::<%= orm_class.find(class_name, "id") %> - raise GraphQL::ExecutionError.new "Error updating <%= file_name %>", extensions: <%= singular_table_name %>.errors.to_hash unless <%= orm_instance.update("**#{file_name}_input") %> - - { <%= file_name %>: <%= singular_table_name %> } - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/node_type.erb b/vendor/gems/graphql/lib/generators/graphql/templates/node_type.erb deleted file mode 100644 index 91e4d07d88a..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/node_type.erb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - module NodeType - include Types::BaseInterface - # Add the `id` field - include GraphQL::Types::Relay::NodeBehaviors - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/object.erb b/vendor/gems/graphql/lib/generators/graphql/templates/object.erb deleted file mode 100644 index 8927419f387..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/object.erb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class <%= ruby_class_name %> < Types::BaseObject -<% if options.node %> implements GraphQL::Types::Relay::Node -<% end %><% normalized_fields.each do |f| %> <%= f.to_object_field %> -<% end %> end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/query_type.erb b/vendor/gems/graphql/lib/generators/graphql/templates/query_type.erb deleted file mode 100644 index 842a635ed59..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/query_type.erb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class QueryType < Types::BaseObject - # Add root-level fields here. - # They will be entry points for queries on your schema. - - # TODO: remove me - field :test_field, String, null: false, - description: "An example field added by the generator" - def test_field - "Hello World!" - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/scalar.erb b/vendor/gems/graphql/lib/generators/graphql/templates/scalar.erb deleted file mode 100644 index f2ffc48b173..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/scalar.erb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class <%= ruby_class_name %> < Types::BaseScalar - def self.coerce_input(input_value, context) - # Override this to prepare a client-provided GraphQL value for your Ruby code - input_value - end - - def self.coerce_result(ruby_value, context) - # Override this to serialize a Ruby value for the GraphQL response - ruby_value.to_s - end - end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/schema.erb b/vendor/gems/graphql/lib/generators/graphql/templates/schema.erb deleted file mode 100644 index 583d241202c..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/schema.erb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -class <%= schema_name %> < GraphQL::Schema - query(Types::QueryType) -<% if options[:batch] %> - # GraphQL::Batch setup: - use GraphQL::Batch -<% else %> - # For batch-loading (see https://graphql-ruby.org/dataloader/overview.html) - use GraphQL::Dataloader -<% end %> - # GraphQL-Ruby calls this when something goes wrong while running a query: - def self.type_error(err, context) - # if err.is_a?(GraphQL::InvalidNullError) - # # report to your bug tracker here - # return nil - # end - super - end - - # Union and Interface Resolution - def self.resolve_type(abstract_type, obj, ctx) - # TODO: Implement this method - # to return the correct GraphQL object type for `obj` - raise(GraphQL::RequiredImplementationMissingError) - end - - # Limit the size of incoming queries: - max_query_string_tokens(5000) - - # Stop validating when it encounters this many errors: - validate_max_errors(100) -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/templates/union.erb b/vendor/gems/graphql/lib/generators/graphql/templates/union.erb deleted file mode 100644 index 4b142ce9c7c..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/templates/union.erb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -<% module_namespacing_when_supported do -%> -module Types - class <%= ruby_class_name %> < Types::BaseUnion -<% if custom_fields.any? %> possible_types <%= normalized_possible_types.join(", ") %> -<% end %> end -end -<% end -%> diff --git a/vendor/gems/graphql/lib/generators/graphql/type_generator.rb b/vendor/gems/graphql/lib/generators/graphql/type_generator.rb deleted file mode 100644 index 55f207c532f..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/type_generator.rb +++ /dev/null @@ -1,135 +0,0 @@ -# frozen_string_literal: true -require 'rails/generators' -require 'rails/generators/base' -require 'graphql' -require 'active_support' -require 'active_support/core_ext/string/inflections' -require_relative 'core' - -module Graphql - module Generators - class TypeGeneratorBase < Rails::Generators::NamedBase - include Core - - class_option :namespaced_types, - type: :boolean, - required: false, - default: false, - banner: "Namespaced", - desc: "If the generated types will be namespaced" - - argument :custom_fields, - type: :array, - default: [], - banner: "name:type name:type ...", - desc: "Fields for this object (type may be expressed as Ruby or GraphQL)" - - - attr_accessor :graphql_type - - def create_type_file - template "#{graphql_type}.erb", "#{options[:directory]}/types#{subdirectory}/#{type_file_name}.rb" - end - - # Take a type expression in any combination of GraphQL or Ruby styles - # and return it in a specified output style - # TODO: nullability / list with `mode: :graphql` doesn't work - # @param type_expresson [String] - # @param mode [Symbol] - # @param null [Boolean] - # @return [(String, Boolean)] The type expression, followed by `null:` value - def self.normalize_type_expression(type_expression, mode:, null: true) - if type_expression.start_with?("!") - normalize_type_expression(type_expression[1..-1], mode: mode, null: false) - elsif type_expression.end_with?("!") - normalize_type_expression(type_expression[0..-2], mode: mode, null: false) - elsif type_expression.start_with?("[") && type_expression.end_with?("]") - name, is_null = normalize_type_expression(type_expression[1..-2], mode: mode, null: null) - ["[#{name}]", is_null] - elsif type_expression.end_with?("Type") - normalize_type_expression(type_expression[0..-5], mode: mode, null: null) - elsif type_expression.start_with?("Types::") - normalize_type_expression(type_expression[7..-1], mode: mode, null: null) - elsif type_expression.start_with?("types.") - normalize_type_expression(type_expression[6..-1], mode: mode, null: null) - else - case mode - when :ruby - case type_expression - when "Int" - ["Integer", null] - when "Integer", "Float", "Boolean", "String", "ID" - [type_expression, null] - else - ["Types::#{type_expression.camelize}Type", null] - end - when :graphql - [type_expression.camelize, null] - else - raise "Unexpected normalize mode: #{mode}" - end - end - end - - private - - # @return [String] The user-provided type name, normalized to Ruby code - def type_ruby_name - @type_ruby_name ||= self.class.normalize_type_expression(name, mode: :ruby)[0] - end - - # @return [String] The user-provided type name, as a GraphQL name - def type_graphql_name - @type_graphql_name ||= self.class.normalize_type_expression(name, mode: :graphql)[0] - end - - # @return [String] The user-provided type name, as a file name (without extension) - def type_file_name - @type_file_name ||= "#{type_graphql_name}Type".underscore - end - - # @return [Array] User-provided fields, in `(name, Ruby type name)` pairs - def normalized_fields - @normalized_fields ||= fields.map { |f| - name, raw_type = f.split(":", 2) - type_expr, null = self.class.normalize_type_expression(raw_type, mode: :ruby) - NormalizedField.new(name, type_expr, null) - } - end - - def ruby_class_name - class_prefix = - if options[:namespaced_types] - "#{graphql_type.pluralize.camelize}::" - else - "" - end - @ruby_class_name || class_prefix + type_ruby_name.sub(/^Types::/, "") - end - - def subdirectory - if options[:namespaced_types] - "/#{graphql_type.pluralize}" - else - "" - end - end - - class NormalizedField - def initialize(name, type_expr, null) - @name = name - @type_expr = type_expr - @null = null - end - - def to_object_field - "field :#{@name}, #{@type_expr}#{@null ? '' : ', null: false'}" - end - - def to_input_argument - "argument :#{@name}, #{@type_expr}, required: false" - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/generators/graphql/union_generator.rb b/vendor/gems/graphql/lib/generators/graphql/union_generator.rb deleted file mode 100644 index 03bb24fa129..00000000000 --- a/vendor/gems/graphql/lib/generators/graphql/union_generator.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true -require 'generators/graphql/type_generator' - -module Graphql - module Generators - # Generate a union type by name - # with the specified member types. - # - # ``` - # rails g graphql:union SearchResultType ImageType AudioType - # ``` - class UnionGenerator < TypeGeneratorBase - desc "Create a GraphQL::UnionType with the given name and possible types" - source_root File.expand_path('../templates', __FILE__) - - argument :possible_types, - type: :array, - default: [], - banner: "type type ...", - desc: "Possible types for this union (expressed as Ruby or GraphQL)" - - private - - def graphql_type - "union" - end - - def normalized_possible_types - custom_fields.map { |t| self.class.normalize_type_expression(t, mode: :ruby)[0] } - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql.rb b/vendor/gems/graphql/lib/graphql.rb deleted file mode 100644 index 8889c2c0322..00000000000 --- a/vendor/gems/graphql/lib/graphql.rb +++ /dev/null @@ -1,134 +0,0 @@ -# frozen_string_literal: true -require "delegate" -require "json" -require "set" -require "singleton" -require "forwardable" -require "fiber/storage" -require "graphql/autoload" - -module GraphQL - extend Autoload - - # Load all `autoload`-configured classes, and also eager-load dependents who have autoloads of their own. - def self.eager_load! - super - Query.eager_load! - Types.eager_load! - Schema.eager_load! - end - - class Error < StandardError - end - - # This error is raised when GraphQL-Ruby encounters a situation - # that it *thought* would never happen. Please report this bug! - class InvariantError < Error - def initialize(message) - message += " - -This is probably a bug in GraphQL-Ruby, please report this error on GitHub: https://github.com/rmosolgo/graphql-ruby/issues/new?template=bug_report.md" - super(message) - end - end - - class RequiredImplementationMissingError < Error - end - - class << self - def default_parser - @default_parser ||= GraphQL::Language::Parser - end - - attr_writer :default_parser - end - - # Turn a query string or schema definition into an AST - # @param graphql_string [String] a GraphQL query string or schema definition - # @return [GraphQL::Language::Nodes::Document] - def self.parse(graphql_string, trace: GraphQL::Tracing::NullTrace, filename: nil, max_tokens: nil) - default_parser.parse(graphql_string, trace: trace, filename: filename, max_tokens: max_tokens) - end - - # Read the contents of `filename` and parse them as GraphQL - # @param filename [String] Path to a `.graphql` file containing IDL or query - # @return [GraphQL::Language::Nodes::Document] - def self.parse_file(filename) - content = File.read(filename) - default_parser.parse(content, filename: filename) - end - - # @return [Array] - def self.scan(graphql_string) - default_parser.scan(graphql_string) - end - - def self.parse_with_racc(string, filename: nil, trace: GraphQL::Tracing::NullTrace) - warn "`GraphQL.parse_with_racc` is deprecated; GraphQL-Ruby no longer uses racc for parsing. Call `GraphQL.parse` or `GraphQL::Language::Parser.parse` instead." - GraphQL::Language::Parser.parse(string, filename: filename, trace: trace) - end - - def self.scan_with_ruby(graphql_string) - GraphQL::Language::Lexer.tokenize(graphql_string) - end - - NOT_CONFIGURED = Object.new - private_constant :NOT_CONFIGURED - module EmptyObjects - EMPTY_HASH = {}.freeze - EMPTY_ARRAY = [].freeze - end - - class << self - # If true, the parser should raise when an integer or float is followed immediately by an identifier (instead of a space or punctuation) - attr_accessor :reject_numbers_followed_by_names - end - - self.reject_numbers_followed_by_names = false - - autoload :ExecutionError, "graphql/execution_error" - autoload :RuntimeTypeError, "graphql/runtime_type_error" - autoload :UnresolvedTypeError, "graphql/unresolved_type_error" - autoload :InvalidNullError, "graphql/invalid_null_error" - autoload :AnalysisError, "graphql/analysis_error" - autoload :CoercionError, "graphql/coercion_error" - autoload :InvalidNameError, "graphql/invalid_name_error" - autoload :IntegerDecodingError, "graphql/integer_decoding_error" - autoload :IntegerEncodingError, "graphql/integer_encoding_error" - autoload :StringEncodingError, "graphql/string_encoding_error" - autoload :DateEncodingError, "graphql/date_encoding_error" - autoload :DurationEncodingError, "graphql/duration_encoding_error" - autoload :TypeKinds, "graphql/type_kinds" - autoload :NameValidator, "graphql/name_validator" - autoload :Language, "graphql/language" - - autoload :Analysis, "graphql/analysis" - autoload :Tracing, "graphql/tracing" - autoload :Dig, "graphql/dig" - autoload :Execution, "graphql/execution" - autoload :Pagination, "graphql/pagination" - autoload :Schema, "graphql/schema" - autoload :Query, "graphql/query" - autoload :Dataloader, "graphql/dataloader" - autoload :Types, "graphql/types" - autoload :StaticValidation, "graphql/static_validation" - autoload :Execution, "graphql/execution" - autoload :Introspection, "graphql/introspection" - autoload :Relay, "graphql/relay" - autoload :Subscriptions, "graphql/subscriptions" - autoload :ParseError, "graphql/parse_error" - autoload :Backtrace, "graphql/backtrace" - - autoload :UnauthorizedError, "graphql/unauthorized_error" - autoload :UnauthorizedEnumValueError, "graphql/unauthorized_enum_value_error" - autoload :UnauthorizedFieldError, "graphql/unauthorized_field_error" - autoload :LoadApplicationObjectFailedError, "graphql/load_application_object_failed_error" - autoload :Testing, "graphql/testing" - autoload :Current, "graphql/current" - if defined?(::Rails::Engine) - autoload :Dashboard, 'graphql/dashboard' - end -end - -require "graphql/version" -require "graphql/railtie" if defined? Rails::Railtie diff --git a/vendor/gems/graphql/lib/graphql/analysis.rb b/vendor/gems/graphql/lib/graphql/analysis.rb deleted file mode 100644 index 1d6c0df3ea6..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true -require "graphql/analysis/visitor" -require "graphql/analysis/analyzer" -require "graphql/analysis/field_usage" -require "graphql/analysis/query_complexity" -require "graphql/analysis/max_query_complexity" -require "graphql/analysis/query_depth" -require "graphql/analysis/max_query_depth" -require "timeout" - -module GraphQL - module Analysis - AST = self - module_function - # Analyze a multiplex, and all queries within. - # Multiplex analyzers are ran for all queries, keeping state. - # Query analyzers are ran per query, without carrying state between queries. - # - # @param multiplex [GraphQL::Execution::Multiplex] - # @param analyzers [Array] - # @return [Array] Results from multiplex analyzers - def analyze_multiplex(multiplex, analyzers) - multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) } - - multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do - query_results = multiplex.queries.map do |query| - if query.valid? - analyze_query( - query, - query.analyzers, - multiplex_analyzers: multiplex_analyzers - ) - else - [] - end - end - - multiplex_results = multiplex_analyzers.map(&:result) - multiplex_errors = analysis_errors(multiplex_results) - - multiplex.queries.each_with_index do |query, idx| - query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx]) - end - multiplex_results - end - end - - # @param query [GraphQL::Query] - # @param analyzers [Array] - # @return [Array] Results from those analyzers - def analyze_query(query, analyzers, multiplex_analyzers: []) - query.current_trace.analyze_query(query: query) do - query_analyzers = analyzers - .map { |analyzer| analyzer.new(query) } - .tap { _1.select!(&:analyze?) } - - analyzers_to_run = query_analyzers + multiplex_analyzers - if !analyzers_to_run.empty? - - analyzers_to_run.select!(&:visit?) - if !analyzers_to_run.empty? - visitor = GraphQL::Analysis::Visitor.new( - query: query, - analyzers: analyzers_to_run - ) - - # `nil` or `0` causes no timeout - Timeout::timeout(query.validate_timeout_remaining) do - visitor.visit - end - - if !visitor.rescued_errors.empty? - return visitor.rescued_errors - end - end - - query_analyzers.map(&:result) - else - [] - end - end - rescue Timeout::Error - [GraphQL::AnalysisError.new("Timeout on validation of query")] - rescue GraphQL::UnauthorizedError, GraphQL::ExecutionError - # This error was raised during analysis and will be returned the client before execution - [] - end - - def analysis_errors(results) - results.flatten.tap { _1.select! { |r| r.is_a?(GraphQL::AnalysisError) } } - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/analyzer.rb b/vendor/gems/graphql/lib/graphql/analysis/analyzer.rb deleted file mode 100644 index ab9943d51d4..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/analyzer.rb +++ /dev/null @@ -1,90 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - # Query analyzer for query ASTs. Query analyzers respond to visitor style methods - # but are prefixed by `enter` and `leave`. - # - # When an analyzer is initialized with a Multiplex, you can always get the current query from - # `visitor.query` in the visit methods. - # - # @param [GraphQL::Query, GraphQL::Execution::Multiplex] The query or multiplex to analyze - class Analyzer - def initialize(subject) - @subject = subject - - if subject.is_a?(GraphQL::Query) - @query = subject - @multiplex = nil - else - @multiplex = subject - @query = nil - end - end - - # Analyzer hook to decide at analysis time whether a query should - # be analyzed or not. - # @return [Boolean] If the query should be analyzed or not - def analyze? - true - end - - # Analyzer hook to decide at analysis time whether analysis - # requires a visitor pass; can be disabled for precomputed results. - # @return [Boolean] If analysis requires visitation or not - def visit? - true - end - - # The result for this analyzer. Returning {GraphQL::AnalysisError} results - # in a query error. - # @return [Any] The analyzer result - def result - raise GraphQL::RequiredImplementationMissingError - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - class << self - private - - def build_visitor_hooks(member_name) - class_eval(<<-EOS, __FILE__, __LINE__ + 1) - def on_enter_#{member_name}(node, parent, visitor) - end - - def on_leave_#{member_name}(node, parent, visitor) - end - EOS - end - end - - build_visitor_hooks :argument - build_visitor_hooks :directive - build_visitor_hooks :document - build_visitor_hooks :enum - build_visitor_hooks :field - build_visitor_hooks :fragment_spread - build_visitor_hooks :inline_fragment - build_visitor_hooks :input_object - build_visitor_hooks :list_type - build_visitor_hooks :non_null_type - build_visitor_hooks :null_value - build_visitor_hooks :operation_definition - build_visitor_hooks :type_name - build_visitor_hooks :variable_definition - build_visitor_hooks :variable_identifier - build_visitor_hooks :abstract_node - # rubocop:enable Development/NoEvalCop - protected - - # @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing - attr_reader :subject - - # @return [GraphQL::Query, nil] `nil` if this analyzer is visiting a multiplex - # (When this is `nil`, use `visitor.query` inside visit methods to get the current query) - attr_reader :query - - # @return [GraphQL::Execution::Multiplex, nil] `nil` if this analyzer is visiting a query - attr_reader :multiplex - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/field_usage.rb b/vendor/gems/graphql/lib/graphql/analysis/field_usage.rb deleted file mode 100644 index a54767dd782..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/field_usage.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - class FieldUsage < Analyzer - def initialize(query) - super - @used_fields = Set.new - @used_deprecated_fields = Set.new - @used_deprecated_arguments = Set.new - @used_deprecated_enum_values = Set.new - end - - def on_leave_field(node, parent, visitor) - field_defn = visitor.field_definition - field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}" - @used_fields << field - @used_deprecated_fields << field if field_defn.deprecation_reason - arguments = visitor.query.arguments_for(node, field_defn) - # If there was an error when preparing this argument object, - # then this might be an error or something: - if arguments.respond_to?(:argument_values) - extract_deprecated_arguments(arguments.argument_values) - end - end - - def result - { - used_fields: @used_fields.to_a, - used_deprecated_fields: @used_deprecated_fields.to_a, - used_deprecated_arguments: @used_deprecated_arguments.to_a, - used_deprecated_enum_values: @used_deprecated_enum_values.to_a, - } - end - - private - - def extract_deprecated_arguments(argument_values) - argument_values.each_pair do |_argument_name, argument| - if argument.definition.deprecation_reason - @used_deprecated_arguments << argument.definition.path - end - - arg_val = argument.value - - next if arg_val.nil? - - argument_type = argument.definition.type - if argument_type.non_null? - argument_type = argument_type.of_type - end - - if argument_type.kind.input_object? - extract_deprecated_arguments(argument.original_value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance - elsif argument_type.kind.enum? - extract_deprecated_enum_value(argument_type, arg_val) - elsif argument_type.list? - inner_type = argument_type.unwrap - case inner_type.kind - when TypeKinds::INPUT_OBJECT - argument.original_value.each do |value| - extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance - end - when TypeKinds::ENUM - arg_val.each do |value| - extract_deprecated_enum_value(inner_type, value) - end - else - # Not a kind of input that we track - end - end - end - end - - def extract_deprecated_enum_value(enum_type, value) - enum_value = @query.types.enum_values(enum_type).find { |ev| ev.value == value } - if enum_value&.deprecation_reason - @used_deprecated_enum_values << enum_value.path - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/max_query_complexity.rb b/vendor/gems/graphql/lib/graphql/analysis/max_query_complexity.rb deleted file mode 100644 index 53235b64039..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/max_query_complexity.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - # Used under the hood to implement complexity validation, - # see {Schema#max_complexity} and {Query#max_complexity} - class MaxQueryComplexity < QueryComplexity - def result - return if subject.max_complexity.nil? - - total_complexity = max_possible_complexity - - if total_complexity > subject.max_complexity - GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{subject.max_complexity}") - else - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/max_query_depth.rb b/vendor/gems/graphql/lib/graphql/analysis/max_query_depth.rb deleted file mode 100644 index b5a1ef4fca6..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/max_query_depth.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - class MaxQueryDepth < QueryDepth - def result - configured_max_depth = if query - query.max_depth - else - multiplex.schema.max_depth - end - - if configured_max_depth && @max_depth > configured_max_depth - GraphQL::AnalysisError.new("Query has depth of #{@max_depth}, which exceeds max depth of #{configured_max_depth}") - else - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/query_complexity.rb b/vendor/gems/graphql/lib/graphql/analysis/query_complexity.rb deleted file mode 100644 index 6d59b3dd5d1..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/query_complexity.rb +++ /dev/null @@ -1,183 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - # Calculate the complexity of a query, using {Field#complexity} values. - class QueryComplexity < Analyzer - # State for the query complexity calculation: - # - `complexities_on_type` holds complexity scores for each type - def initialize(query) - super - @skip_introspection_fields = !query.schema.max_complexity_count_introspection_fields - @complexities_on_type_by_query = {} - end - - # Override this method to use the complexity result - def result - max_possible_complexity - end - - # ScopedTypeComplexity models a tree of GraphQL types mapped to inner selections, ie: - # Hash> - class ScopedTypeComplexity < Hash - # A proc for defaulting empty namespace requests as a new scope hash. - DEFAULT_PROC = ->(h, k) { h[k] = {} } - - attr_reader :field_definition, :response_path, :query - - # @param parent_type [Class] The owner of `field_definition` - # @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration - # @param query [GraphQL::Query] Used for `query.possible_types` - # @param response_path [Array] The path to the response key for the field - # @return [Hash>] - def initialize(parent_type, field_definition, query, response_path) - super(&DEFAULT_PROC) - @parent_type = parent_type - @field_definition = field_definition - @query = query - @response_path = response_path - @nodes = [] - end - - # @return [Array] - attr_reader :nodes - - def own_complexity(child_complexity) - @field_definition.calculate_complexity(query: @query, nodes: @nodes, child_complexity: child_complexity) - end - end - - def on_enter_field(node, parent, visitor) - # We don't want to visit fragment definitions, - # we'll visit them when we hit the spreads instead - return if visitor.visiting_fragment_definition? - return if visitor.skipping? - return if @skip_introspection_fields && visitor.field_definition.introspection? - parent_type = visitor.parent_type_definition - field_key = node.alias || node.name - - # Find or create a complexity scope stack for this query. - scopes_stack = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)] - - # Find or create the complexity costing node for this field. - scope = scopes_stack.last[parent_type][field_key] ||= ScopedTypeComplexity.new(parent_type, visitor.field_definition, visitor.query, visitor.response_path) - scope.nodes.push(node) - scopes_stack.push(scope) - end - - def on_leave_field(node, parent, visitor) - # We don't want to visit fragment definitions, - # we'll visit them when we hit the spreads instead - return if visitor.visiting_fragment_definition? - return if visitor.skipping? - return if @skip_introspection_fields && visitor.field_definition.introspection? - scopes_stack = @complexities_on_type_by_query[visitor.query] - scopes_stack.pop - end - - private - - # @return [Integer] - def max_possible_complexity - @complexities_on_type_by_query.reduce(0) do |total, (query, scopes_stack)| - total + merged_max_complexity_for_scopes(query, [scopes_stack.first]) - end - end - - # @param query [GraphQL::Query] Used for `query.possible_types` - # @param scopes [Array] Array of scoped type complexities - # @return [Integer] - def merged_max_complexity_for_scopes(query, scopes) - # Aggregate a set of all possible scope types encountered (scope keys). - # Use a hash, but ignore the values; it's just a fast way to work with the keys. - possible_scope_types = scopes.each_with_object({}) do |scope, memo| - memo.merge!(scope) - end - - # Expand abstract scope types into their concrete implementations; - # overlapping abstracts coalesce through their intersecting types. - possible_scope_types.keys.each do |possible_scope_type| - next unless possible_scope_type.kind.abstract? - - query.types.possible_types(possible_scope_type).each do |impl_type| - possible_scope_types[impl_type] ||= true - end - possible_scope_types.delete(possible_scope_type) - end - - # Aggregate the lexical selections that may apply to each possible type, - # and then return the maximum cost among possible typed selections. - possible_scope_types.each_key.reduce(0) do |max, possible_scope_type| - # Collect inner selections from all scopes that intersect with this possible type. - all_inner_selections = scopes.each_with_object([]) do |scope, memo| - scope.each do |scope_type, inner_selections| - memo << inner_selections if types_intersect?(query, scope_type, possible_scope_type) - end - end - - # Find the maximum complexity for the scope type among possible lexical branches. - complexity = merged_max_complexity(query, all_inner_selections) - complexity > max ? complexity : max - end - end - - def types_intersect?(query, a, b) - return true if a == b - - a_types = query.types.possible_types(a) - query.types.possible_types(b).any? { |t| a_types.include?(t) } - end - - # A hook which is called whenever a field's max complexity is calculated. - # Override this method to capture individual field complexity details. - # - # @param scoped_type_complexity [ScopedTypeComplexity] - # @param max_complexity [Numeric] Field's maximum complexity including child complexity - # @param child_complexity [Numeric, nil] Field's child complexity - def field_complexity(scoped_type_complexity, max_complexity:, child_complexity: nil) - end - - # @param inner_selections [Array>] Field selections for a scope - # @return [Integer] Total complexity value for all these selections in the parent scope - def merged_max_complexity(query, inner_selections) - # Aggregate a set of all unique field selection keys across all scopes. - # Use a hash, but ignore the values; it's just a fast way to work with the keys. - unique_field_keys = inner_selections.each_with_object({}) do |inner_selection, memo| - memo.merge!(inner_selection) - end - - # Add up the total cost for each unique field name's coalesced selections - unique_field_keys.each_key.reduce(0) do |total, field_key| - composite_scopes = nil - field_cost = 0 - - # Collect composite selection scopes for further aggregation, - # leaf selections report their costs directly. - inner_selections.each do |inner_selection| - child_scope = inner_selection[field_key] - next unless child_scope - - # Empty child scopes are leaf nodes with zero child complexity. - if child_scope.empty? - field_cost = child_scope.own_complexity(0) - field_complexity(child_scope, max_complexity: field_cost, child_complexity: nil) - else - composite_scopes ||= [] - composite_scopes << child_scope - end - end - - if composite_scopes - child_complexity = merged_max_complexity_for_scopes(query, composite_scopes) - - # This is the last composite scope visited; assume it's representative (for backwards compatibility). - # Note: it would be more correct to score each composite scope and use the maximum possibility. - field_cost = composite_scopes.last.own_complexity(child_complexity) - field_complexity(composite_scopes.last, max_complexity: field_cost, child_complexity: child_complexity) - end - - total + field_cost - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/query_depth.rb b/vendor/gems/graphql/lib/graphql/analysis/query_depth.rb deleted file mode 100644 index b6859bb119e..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/query_depth.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - # A query reducer for measuring the depth of a given query. - # - # See https://graphql-ruby.org/queries/ast_analysis.html for more examples. - # - # @example Logging the depth of a query - # class LogQueryDepth < GraphQL::Analysis::QueryDepth - # def result - # log("GraphQL query depth: #{@max_depth}") - # end - # end - # - # # In your Schema file: - # - # class MySchema < GraphQL::Schema - # query_analyzer LogQueryDepth - # end - # - # # When you run the query, the depth will get logged: - # - # Schema.execute(query_str) - # # GraphQL query depth: 8 - # - class QueryDepth < Analyzer - def initialize(query) - @max_depth = 0 - @current_depth = 0 - @count_introspection_fields = query.schema.count_introspection_fields - super - end - - def on_enter_field(node, parent, visitor) - return if visitor.skipping? || - visitor.visiting_fragment_definition? || - (@count_introspection_fields == false && visitor.field_definition.introspection?) - - @current_depth += 1 - end - - def on_leave_field(node, parent, visitor) - return if visitor.skipping? || - visitor.visiting_fragment_definition? || - (@count_introspection_fields == false && visitor.field_definition.introspection?) - - if @max_depth < @current_depth - @max_depth = @current_depth - end - @current_depth -= 1 - end - - def result - @max_depth - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis/visitor.rb b/vendor/gems/graphql/lib/graphql/analysis/visitor.rb deleted file mode 100644 index 7e4b87e7823..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis/visitor.rb +++ /dev/null @@ -1,285 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Analysis - # Depth first traversal through a query AST, calling AST analyzers - # along the way. - # - # The visitor is a special case of GraphQL::Language::StaticVisitor, visiting - # only the selected operation, providing helpers for common use cases such - # as skipped fields and visiting fragment spreads. - # - # @see {GraphQL::Analysis::Analyzer} AST Analyzers for queries - class Visitor < GraphQL::Language::StaticVisitor - def initialize(query:, analyzers:) - @analyzers = analyzers - @path = [] - @object_types = [] - @directives = [] - @field_definitions = [] - @argument_definitions = [] - @directive_definitions = [] - @rescued_errors = [] - @query = query - @schema = query.schema - @types = query.types - @response_path = [] - @skip_stack = [false] - super(query.selected_operation) - end - - # @return [GraphQL::Query] the query being visited - attr_reader :query - - # @return [Array] Types whose scope we've entered - attr_reader :object_types - - # @return [Array] The path to the response key for the current field - def response_path - @response_path.dup - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - # Visitor Hooks - [ - :operation_definition, :fragment_definition, - :inline_fragment, :field, :directive, :argument, :fragment_spread - ].each do |node_type| - module_eval <<-RUBY, __FILE__, __LINE__ - def call_on_enter_#{node_type}(node, parent) - @analyzers.each do |a| - begin - a.on_enter_#{node_type}(node, parent, self) - rescue AnalysisError => err - @rescued_errors << err - end - end - end - - def call_on_leave_#{node_type}(node, parent) - @analyzers.each do |a| - begin - a.on_leave_#{node_type}(node, parent, self) - rescue AnalysisError => err - @rescued_errors << err - end - end - end - - RUBY - end - # rubocop:enable Development/NoEvalCop - - def on_operation_definition(node, parent) - object_type = @schema.root_type_for_operation(node.operation_type) - @object_types.push(object_type) - @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}") - call_on_enter_operation_definition(node, parent) - super - call_on_leave_operation_definition(node, parent) - @object_types.pop - @path.pop - end - - def on_fragment_definition(node, parent) - on_fragment_with_type(node) do - @path.push("fragment #{node.name}") - @in_fragment_def = false - call_on_enter_fragment_definition(node, parent) - super - @in_fragment_def = false - call_on_leave_fragment_definition(node, parent) - end - end - - def on_inline_fragment(node, parent) - on_fragment_with_type(node) do - @path.push("...#{node.type ? " on #{node.type.name}" : ""}") - @skipping = @skip_stack.last || skip?(node) - @skip_stack << @skipping - - call_on_enter_inline_fragment(node, parent) - super - @skipping = @skip_stack.pop - call_on_leave_inline_fragment(node, parent) - end - end - - def on_field(node, parent) - @response_path.push(node.alias || node.name) - parent_type = @object_types.last - # This could be nil if the previous field wasn't found: - field_definition = parent_type && @types.field(parent_type, node.name) - @field_definitions.push(field_definition) - if !field_definition.nil? - next_object_type = field_definition.type.unwrap - @object_types.push(next_object_type) - else - @object_types.push(nil) - end - @path.push(node.alias || node.name) - - @skipping = @skip_stack.last || skip?(node) - @skip_stack << @skipping - - call_on_enter_field(node, parent) - super - @skipping = @skip_stack.pop - call_on_leave_field(node, parent) - @response_path.pop - @field_definitions.pop - @object_types.pop - @path.pop - end - - def on_directive(node, parent) - directive_defn = @schema.directives[node.name] - @directive_definitions.push(directive_defn) - call_on_enter_directive(node, parent) - super - call_on_leave_directive(node, parent) - @directive_definitions.pop - end - - def on_argument(node, parent) - argument_defn = if (arg = @argument_definitions.last) - arg_type = arg.type.unwrap - if arg_type.kind.input_object? - @types.argument(arg_type, node.name) - else - nil - end - elsif (directive_defn = @directive_definitions.last) - @types.argument(directive_defn, node.name) - elsif (field_defn = @field_definitions.last) - @types.argument(field_defn, node.name) - else - nil - end - - @argument_definitions.push(argument_defn) - @path.push(node.name) - call_on_enter_argument(node, parent) - super - call_on_leave_argument(node, parent) - @argument_definitions.pop - @path.pop - end - - def on_fragment_spread(node, parent) - @path.push("... #{node.name}") - @skipping = @skip_stack.last || skip?(node) - @skip_stack << @skipping - - call_on_enter_fragment_spread(node, parent) - enter_fragment_spread_inline(node) - super - @skipping = @skip_stack.pop - leave_fragment_spread_inline(node) - call_on_leave_fragment_spread(node, parent) - @path.pop - end - - # @return [GraphQL::BaseType] The current object type - def type_definition - @object_types.last - end - - # @return [GraphQL::BaseType] The type which the current type came from - def parent_type_definition - @object_types[-2] - end - - # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one - def field_definition - @field_definitions.last - end - - # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to - def previous_field_definition - @field_definitions[-2] - end - - # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one - def directive_definition - @directive_definitions.last - end - - # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one - def argument_definition - @argument_definitions.last - end - - # @return [GraphQL::Argument, nil] The previous GraphQL argument - def previous_argument_definition - @argument_definitions[-2] - end - - private - - # Visit a fragment spread inline instead of visiting the definition - # by itself. - def enter_fragment_spread_inline(fragment_spread) - fragment_def = query.fragments[fragment_spread.name] - - object_type = if fragment_def.type - @types.type(fragment_def.type.name) - else - object_types.last - end - - object_types << object_type - - on_fragment_definition_children(fragment_def) - end - - # Visit a fragment spread inline instead of visiting the definition - # by itself. - def leave_fragment_spread_inline(_fragment_spread) - object_types.pop - end - - def skip?(ast_node) - dir = ast_node.directives - !dir.empty? && !GraphQL::Execution::DirectiveChecks.include?(dir, query) - end - - def on_fragment_with_type(node) - object_type = if node.type - @types.type(node.type.name) - else - @object_types.last - end - @object_types.push(object_type) - yield(node) - @object_types.pop - @path.pop - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/analysis_error.rb b/vendor/gems/graphql/lib/graphql/analysis_error.rb deleted file mode 100644 index 2493c473fed..00000000000 --- a/vendor/gems/graphql/lib/graphql/analysis_error.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class AnalysisError < GraphQL::ExecutionError - end -end diff --git a/vendor/gems/graphql/lib/graphql/autoload.rb b/vendor/gems/graphql/lib/graphql/autoload.rb deleted file mode 100644 index 82aa538cfc4..00000000000 --- a/vendor/gems/graphql/lib/graphql/autoload.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - # @see GraphQL::Railtie for automatic Rails integration - module Autoload - # Register a constant named `const_name` to be loaded from `path`. - # This is like `Kernel#autoload` but it tracks the constants so they can be eager-loaded with {#eager_load!} - # @param const_name [Symbol] - # @param path [String] - # @return [void] - def autoload(const_name, path) - @_eagerloaded_constants ||= [] - @_eagerloaded_constants << const_name - - super const_name, path - end - - # Call this to load this constant's `autoload` dependents and continue calling recursively - # @return [void] - def eager_load! - @_eager_loading = true - if @_eagerloaded_constants - @_eagerloaded_constants.each { |const_name| const_get(const_name) } - @_eagerloaded_constants = nil - end - nil - ensure - @_eager_loading = false - end - - private - - # @return [Boolean] `true` if GraphQL-Ruby is currently eager-loading its constants - def eager_loading? - @_eager_loading ||= false - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/backtrace.rb b/vendor/gems/graphql/lib/graphql/backtrace.rb deleted file mode 100644 index c97543d1fe0..00000000000 --- a/vendor/gems/graphql/lib/graphql/backtrace.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true -require "graphql/backtrace/table" -require "graphql/backtrace/traced_error" -module GraphQL - # Wrap unhandled errors with {TracedError}. - # - # {TracedError} provides a GraphQL backtrace with arguments and return values. - # The underlying error is available as {TracedError#cause}. - # - # @example toggling backtrace annotation - # class MySchema < GraphQL::Schema - # if Rails.env.development? || Rails.env.test? - # use GraphQL::Backtrace - # end - # end - # - class Backtrace - include Enumerable - extend Forwardable - - def_delegators :to_a, :each, :[] - - def self.use(schema_defn) - schema_defn.using_backtrace = true - end - - def initialize(context, value: nil) - @table = Table.new(context, value: value) - end - - def inspect - @table.to_table - end - - alias :to_s :inspect - - def to_a - @table.to_backtrace - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/backtrace/table.rb b/vendor/gems/graphql/lib/graphql/backtrace/table.rb deleted file mode 100644 index dc36728cec2..00000000000 --- a/vendor/gems/graphql/lib/graphql/backtrace/table.rb +++ /dev/null @@ -1,181 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Backtrace - # A class for turning a context into a human-readable table or array - class Table - MIN_COL_WIDTH = 4 - MAX_COL_WIDTH = 100 - HEADERS = [ - "Loc", - "Field", - "Object", - "Arguments", - "Result", - ] - - def initialize(context, value:) - @context = context - @override_value = value - end - - # @return [String] A table layout of backtrace with metadata - def to_table - @to_table ||= render_table(rows) - end - - # @return [Array] An array of position + field name entries - def to_backtrace - @to_backtrace ||= begin - backtrace = rows.map { |r| "#{r[0]}: #{r[1]}" } - # skip the header entry - backtrace.shift - backtrace - end - end - - private - - def rows - @rows ||= begin - query = @context.query - query_ctx = @context - runtime_inst = query_ctx.namespace(:interpreter_runtime)[:runtime] - result = runtime_inst.instance_variable_get(:@response) - rows = [] - result_path = [] - last_part = nil - path = @context.current_path - path.each do |path_part| - value = value_at(runtime_inst, result_path) - - if result_path.empty? - name = query.selected_operation.operation_type || "query" - if (n = query.selected_operation_name) - name += " #{n}" - end - args = query.variables - else - name = result.graphql_field.path - args = result.graphql_arguments - end - - object = result.graphql_parent ? result.graphql_parent.graphql_application_value : result.graphql_application_value - object = object.object.inspect - - rows << [ - result.ast_node.position.join(":"), - name, - "#{object}", - args.to_h.inspect, - inspect_result(value), - ] - - result_path << path_part - if path_part == path.last - last_part = path_part - else - result = result[path_part] - end - end - - - object = result.graphql_application_value.object.inspect - ast_node = result.graphql_selections.find { |s| s.alias == last_part || s.name == last_part } - field_defn = query.get_field(result.graphql_result_type, ast_node.name) - args = query.arguments_for(ast_node, field_defn).to_h - field_path = field_defn.path - if ast_node.alias - field_path += " as #{ast_node.alias}" - end - - rows << [ - ast_node.position.join(":"), - field_path, - "#{object}", - args.inspect, - inspect_result(@override_value) - ] - - rows << HEADERS - rows.reverse! - rows - end - end - - # @return [String] - def render_table(rows) - max = Array.new(HEADERS.length, MIN_COL_WIDTH) - - rows.each do |row| - row.each_with_index do |col, idx| - col_len = col.length - max_len = max[idx] - if col_len > max_len - if col_len > MAX_COL_WIDTH - max[idx] = MAX_COL_WIDTH - else - max[idx] = col_len - end - end - end - end - - table = "".dup - last_col_idx = max.length - 1 - rows.each do |row| - table << row.map.each_with_index do |col, idx| - max_len = max[idx] - if idx < last_col_idx - col = col.ljust(max_len) - end - if col.length > max_len - col = col[0, max_len - 3] + "..." - end - col - end.join(" | ") - table << "\n" - end - table - end - - - def value_at(runtime, path) - response = runtime.final_result - path.each do |key| - response && (response = response[key]) - end - response - end - - def inspect_result(obj) - case obj - when Hash - "{" + - obj.map do |key, val| - "#{key}: #{inspect_truncated(val)}" - end.join(", ") + - "}" - when Array - "[" + - obj.map { |v| inspect_truncated(v) }.join(", ") + - "]" - else - inspect_truncated(obj) - end - end - - def inspect_truncated(obj) - case obj - when Hash - "{...}" - when Array - "[...]" - when GraphQL::Execution::Lazy - "(unresolved)" - else - "#{obj.inspect}" - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/backtrace/traced_error.rb b/vendor/gems/graphql/lib/graphql/backtrace/traced_error.rb deleted file mode 100644 index caa7c7d7311..00000000000 --- a/vendor/gems/graphql/lib/graphql/backtrace/traced_error.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Backtrace - # When {Backtrace} is enabled, raised errors are wrapped with {TracedError}. - class TracedError < GraphQL::Error - # @return [Array] Printable backtrace of GraphQL error context - attr_reader :graphql_backtrace - - # @return [GraphQL::Query::Context] The context at the field where the error was raised - attr_reader :context - - MESSAGE_TEMPLATE = <<-MESSAGE -Unhandled error during GraphQL execution: - - %{cause_message} - %{cause_backtrace} - %{cause_backtrace_more} -Use #cause to access the original exception (including #cause.backtrace). - -GraphQL Backtrace: -%{graphql_table} -MESSAGE - - # This many lines of the original Ruby backtrace - # are included in the message - CAUSE_BACKTRACE_PREVIEW_LENGTH = 10 - - def initialize(err, current_ctx) - @context = current_ctx - backtrace = Backtrace.new(current_ctx, value: err) - @graphql_backtrace = backtrace.to_a - - cause_backtrace_preview = err.backtrace.first(CAUSE_BACKTRACE_PREVIEW_LENGTH).join("\n ") - - cause_backtrace_remainder_length = err.backtrace.length - CAUSE_BACKTRACE_PREVIEW_LENGTH - cause_backtrace_more = if cause_backtrace_remainder_length < 0 - "" - elsif cause_backtrace_remainder_length == 1 - "... and 1 more line\n" - else - "... and #{cause_backtrace_remainder_length} more lines\n" - end - - message = MESSAGE_TEMPLATE % { - cause_message: err.message, - cause_backtrace: cause_backtrace_preview, - cause_backtrace_more: cause_backtrace_more, - graphql_table: backtrace.inspect, - } - super(message) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/coercion_error.rb b/vendor/gems/graphql/lib/graphql/coercion_error.rb deleted file mode 100644 index 8980d6c0abf..00000000000 --- a/vendor/gems/graphql/lib/graphql/coercion_error.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class CoercionError < GraphQL::ExecutionError - end -end diff --git a/vendor/gems/graphql/lib/graphql/current.rb b/vendor/gems/graphql/lib/graphql/current.rb deleted file mode 100644 index dfe4512f7a5..00000000000 --- a/vendor/gems/graphql/lib/graphql/current.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - # This module exposes Fiber-level runtime information. - # - # It won't work across unrelated fibers, although it will work in child Fibers. - # - # @example Setting Up ActiveRecord::QueryLogs - # - # config.active_record.query_log_tags = [ - # :namespaced_controller, - # :action, - # :job, - # # ... - # { - # # GraphQL runtime info: - # current_graphql_operation: -> { GraphQL::Current.operation_name }, - # current_graphql_field: -> { GraphQL::Current.field&.path }, - # current_dataloader_source: -> { GraphQL::Current.dataloader_source_class }, - # # ... - # }, - # ] - # - module Current - # @return [String, nil] Comma-joined operation names for the currently-running {Multiplex}. `nil` if all operations are anonymous. - def self.operation_name - if (m = Fiber[:__graphql_current_multiplex]) - m.context[:__graphql_current_operation_name] ||= begin - names = m.queries.map { |q| q.selected_operation_name } - if names.all?(&:nil?) - nil - else - names.join(",") - end - end - else - nil - end - end - - # @see GraphQL::Field#path for a string identifying this field - # @return [GraphQL::Field, nil] The currently-running field, if there is one. - def self.field - Fiber[:__graphql_runtime_info]&.values&.first&.current_field - end - - # @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one. - def self.dataloader_source_class - Fiber[:__graphql_current_dataloader_source]&.class - end - - # @return [GraphQL::Dataloader::Source, nil] The currently-running source, if there is one - def self.dataloader_source - Fiber[:__graphql_current_dataloader_source] - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dashboard.rb b/vendor/gems/graphql/lib/graphql/dashboard.rb deleted file mode 100644 index c77e9c49f5b..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard.rb +++ /dev/null @@ -1,142 +0,0 @@ -# frozen_string_literal: true -require 'rails/engine' - -module Graphql - # `GraphQL::Dashboard` is a `Rails::Engine`-based dashboard for viewing metadata about your GraphQL schema. - # - # Pass the class name of your schema when mounting it. - # @see GraphQL::Tracing::DetailedTrace DetailedTrace for viewing production traces in the Dashboard - # - # @example Mounting the Dashboard in your app - # mount GraphQL::Dashboard, at: "graphql_dashboard", schema: "MySchema" - # - # @example Authenticating the Dashboard with HTTP Basic Auth - # # config/initializers/graphql_dashboard.rb - # GraphQL::Dashboard.middleware.use(Rack::Auth::Basic) do |username, password| - # # Compare the provided username/password to an application setting: - # ActiveSupport::SecurityUtils.secure_compare(Rails.application.credentials.graphql_dashboard_username, username) && - # ActiveSupport::SecurityUtils.secure_compare(Rails.application.credentials.graphql_dashboard_username, password) - # end - # - # @example Custom Rails authentication - # # config/initializers/graphql_dashboard.rb - # ActiveSupport.on_load(:graphql_dashboard_application_controller) do - # # context here is GraphQL::Dashboard::ApplicationController - # - # before_action do - # raise ActionController::RoutingError.new('Not Found') unless current_user&.admin? - # end - # - # def current_user - # # load current user - # end - # end - # - class Dashboard < Rails::Engine - engine_name "graphql_dashboard" - isolate_namespace(Graphql::Dashboard) - routes.draw do - root "landings#show" - resources :statics, only: :show, constraints: { id: /[0-9A-Za-z\-.]+/ } - delete "/traces/delete_all", to: "traces#delete_all", as: :traces_delete_all - resources :traces, only: [:index, :show, :destroy] - end - - class ApplicationController < ActionController::Base - protect_from_forgery with: :exception - prepend_view_path(File.join(__FILE__, "../dashboard/views")) - - content_security_policy do |policy| - policy.default_src(:self) if policy.default_src(*policy.default_src).blank? - policy.connect_src(:self) if policy.connect_src(*policy.connect_src).blank? - policy.base_uri(:none) if policy.base_uri(*policy.base_uri).blank? - policy.font_src(:self) if policy.font_src(*policy.font_src).blank? - policy.img_src(:self, :data) if policy.img_src(*policy.img_src).blank? - policy.object_src(:none) if policy.object_src(*policy.object_src).blank? - policy.script_src(:self) if policy.script_src(*policy.script_src).blank? - policy.style_src(:self) if policy.style_src(*policy.style_src).blank? - policy.form_action(:self) if policy.form_action(*policy.form_action).blank? - policy.frame_ancestors(:none) if policy.frame_ancestors(*policy.frame_ancestors).blank? - end - - def schema_class - @schema_class ||= begin - schema_param = request.query_parameters["schema"] || params[:schema] - case schema_param - when Class - schema_param - when String - schema_param.constantize - else - raise "Missing `params[:schema]`, please provide a class or string to `mount GraphQL::Dashboard, schema: ...`" - end - end - end - helper_method :schema_class - end - - class LandingsController < ApplicationController - def show - end - end - - class TracesController < ApplicationController - def index - @detailed_trace_installed = !!schema_class.detailed_trace - if @detailed_trace_installed - @last = params[:last]&.to_i || 50 - @before = params[:before]&.to_i - @traces = schema_class.detailed_trace.traces(last: @last, before: @before) - end - end - - def show - trace = schema_class.detailed_trace.find_trace(params[:id].to_i) - send_data(trace.trace_data) - end - - def destroy - schema_class.detailed_trace.delete_trace(params[:id]) - head :no_content - end - - def delete_all - schema_class.detailed_trace.delete_all_traces - head :no_content - end - end - - class StaticsController < ApplicationController - skip_after_action :verify_same_origin_request - # Use an explicit list of files to avoid any chance of reading other files from disk - STATICS = {} - - [ - "icon.png", - "header-icon.png", - "dashboard.css", - "dashboard.js", - "bootstrap-5.3.3.min.css", - "bootstrap-5.3.3.min.js", - ].each do |static_file| - STATICS[static_file] = File.expand_path("../dashboard/statics/#{static_file}", __FILE__) - end - - def show - expires_in 1.year, public: true - if (filepath = STATICS[params[:id]]) - render file: filepath - else - head :not_found - end - end - end - end -end - -# Rails expects the engine to be called `Graphql::Dashboard`, -# but `GraphQL::Dashboard` is consistent with this gem's naming. -# So define both constants to refer to the same class. -GraphQL::Dashboard = Graphql::Dashboard - -ActiveSupport.run_load_hooks(:graphql_dashboard_application_controller, GraphQL::Dashboard::ApplicationController) diff --git a/vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css b/vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css deleted file mode 100644 index aac8bee0ab3..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +++ /dev/null @@ -1,6 +0,0 @@ -@charset "UTF-8";/*! - * Bootstrap v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control-plaintext~label::after,.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.form-floating>.form-control:disabled~label::after,.form-floating>:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin:calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ diff --git a/vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js b/vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js deleted file mode 100644 index d705b8dac58..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function M(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${M(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${M(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${M(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>n(t))).join(","):null},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="next",lt="prev",ct="left",ht="right",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt="carousel",yt="active",wt=".active",At=".carousel-item",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return"carousel"}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)),rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,bt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt=".bs.collapse",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt="show",Pt="collapse",jt="collapsing",Mt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle="collapse"]',Ht={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Pt),this._element.classList.add(jt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt,Nt),this._element.style[e]="",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(jt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(Mt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,It,Ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt="top",Rt="bottom",qt="right",Vt="left",Kt="auto",Qt=[zt,Rt,qt,Vt],Xt="start",Yt="end",Ut="clippingParents",Gt="viewport",Jt="popper",Zt="reference",te=Qt.reduce((function(t,e){return t.concat([e+"-"+Xt,e+"-"+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e,e+"-"+Xt,e+"-"+Yt])}),[]),ie="beforeRead",ne="read",se="afterRead",oe="beforeMain",re="main",ae="afterMain",le="beforeWrite",ce="write",he="afterWrite",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"").toLowerCase():null}function fe(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t instanceof HTMLElement}function ge(t){return"undefined"!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];me(s)&&ue(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function be(t){return t.split("-")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Oe(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return["table","td","th"].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return"html"===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&"fixed"!==xe(t).position?t.offsetParent:null}function $e(t){for(var e=fe(t),i=De(t);i&&ke(i)&&"static"===xe(i).position;)i=De(i);return i&&("html"===ue(i)||"body"===ue(i)&&"static"===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&"fixed"===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&["html","body"].indexOf(ue(i))<0;){var n=xe(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Ie(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function je(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const Me={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Pe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:je(t,Qt))}(s.padding,i),d=Ce(o),u="y"===l?zt:Vt,f="y"===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Fe(t){return t.split("-")[1]}var He={top:"auto",right:"auto",bottom:"auto",left:"auto"};function We(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Vt,y=zt,w=window;if(c){var A=$e(i),E="clientHeight",T="clientWidth";A===fe(i)&&"static"!==xe(A=Le(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Be={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,We(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var ze={passive:!0};const Re={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=fe(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,ze)})),a&&l.addEventListener("resize",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,ze)})),a&&l.removeEventListener("resize",i.update,ze)}},data:{}};var qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:"end",end:"start"};function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return["html","body","#document"].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var i=Te(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return"rtl"===xe(s||i).direction&&(a+=ve(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe("number"!=typeof g?g:je(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Je(Se(t)),i=["absolute","fixed"].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&"body"!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference),E=ei({reference:A,element:v,strategy:"absolute",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function ni(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function oi(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ii(e,{elementContext:"reference"}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},li={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Vt,qt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},ci={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},hi={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ii(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?zt:Vt,D="y"===y?Rt:qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],j=f?-T[$]/2:0,M=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?zt:Vt,tt="x"===y?Rt:qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var fi={placement:"bottom",modifiers:[],strategy:"absolute"};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi="backdrop",Ki="show",Qi=`mousedown.bs.${Vi}`,Xi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Yi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",An="show",En="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,jn=`hide${xn}`,Mn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,jn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,Mn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,Mn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}N.on(document,Wn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Kn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
    "},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))Es.getOrCreateInstance(t)})),m(Es);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",js="Home",Ms="End",Fs="active",Hs="fade",Ws="show",Bs=".dropdown-toggle",zs=`:not(${Bs})`,Rs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',qs=`.nav-link${zs}, .list-group-item${zs}, [role="tab"]${zs}, ${Rs}`,Vs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Ks extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,js,Ms].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([js,Ms].includes(t.key))i=e[t.key===js?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Ks.getOrCreateInstance(i).show())}_getChildren(){return z.find(qs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(Bs,Fs),n(".dropdown-menu",Ws),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(qs)?t:z.findOne(qs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Ks.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ls,Rs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Ks.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t of z.find(Vs))Ks.getOrCreateInstance(t)})),m(Ks);const Qs=".bs.toast",Xs=`mouseover${Qs}`,Ys=`mouseout${Qs}`,Us=`focusin${Qs}`,Gs=`focusout${Qs}`,Js=`hide${Qs}`,Zs=`hidden${Qs}`,to=`show${Qs}`,eo=`shown${Qs}`,io="hide",no="show",so="showing",oo={animation:"boolean",autohide:"boolean",delay:"number"},ro={animation:!0,autohide:!0,delay:5e3};class ao extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return ro}static get DefaultType(){return oo}static get NAME(){return"toast"}show(){N.trigger(this._element,to).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(io),d(this._element),this._element.classList.add(no,so),this._queueCallback((()=>{this._element.classList.remove(so),N.trigger(this._element,eo),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,Js).defaultPrevented||(this._element.classList.add(so),this._queueCallback((()=>{this._element.classList.add(io),this._element.classList.remove(so,no),N.trigger(this._element,Zs)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(no),super.dispose()}isShown(){return this._element.classList.contains(no)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Xs,(t=>this._onInteraction(t,!0))),N.on(this._element,Ys,(t=>this._onInteraction(t,!1))),N.on(this._element,Us,(t=>this._onInteraction(t,!0))),N.on(this._element,Gs,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ao.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(ao),m(ao),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Ks,Toast:ao,Tooltip:cs}})); -//# sourceMappingURL=bootstrap.bundle.min.js.map diff --git a/vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.css b/vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.css deleted file mode 100644 index 9cfb6c02368..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.css +++ /dev/null @@ -1,3 +0,0 @@ -#header-icon { - max-height: 2em; -} diff --git a/vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.js b/vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.js deleted file mode 100644 index 26f454ce02b..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard/statics/dashboard.js +++ /dev/null @@ -1,78 +0,0 @@ -function detectTheme() { - var storedTheme = localStorage.getItem("graphql_dashboard:theme") - var preferredTheme = !!window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light" - setTheme(storedTheme || preferredTheme) -} - -function toggleTheme() { - var nextTheme = document.documentElement.getAttribute("data-bs-theme") == "dark" ? "light" : "dark" - setTheme(nextTheme) -} - -function setTheme(theme) { - localStorage.setItem("graphql_dashboard:theme", theme) - document.documentElement.setAttribute("data-bs-theme", theme) - var icon = theme == "dark" ? "🌙" : "🌞" - var toggle = document.getElementById("themeToggle") - if (toggle) { - toggle.innerText = icon - } else { - document.addEventListener("DOMContentLoaded", function(_ev) { - document.getElementById("themeToggle").innerText = icon - }) - } -} - -detectTheme() - -var perfettoUrl = "https://ui.perfetto.dev" -async function openOnPerfetto(operationName, tracePath) { - var resp = await fetch(tracePath); - var blob = await resp.blob(); - var nextPerfettoData = await blob.arrayBuffer(); - nextPerfettoWindow = window.open(perfettoUrl) - - var messageHandler = function(event) { - if (event.origin == perfettoUrl && event.data == "PONG") { - clearInterval(perfettoWaiting) - window.removeEventListener("message", messageHandler) - nextPerfettoWindow.postMessage({ - perfetto: { - buffer: nextPerfettoData, - title: operationName + " - GraphQL", - filename: "perfetto-" + operationName + ".dump", - } - }, perfettoUrl) - } - } - - window.addEventListener("message", messageHandler, false) - perfettoWaiting = setInterval(function() { - nextPerfettoWindow.postMessage("PING", perfettoUrl) - }, 100) -} - -async function deleteTrace(tracePath, event) { - if (confirm("Are you sure you want to permanently delete this trace?")) { - var response = await fetch(tracePath, { method: "DELETE", headers: { - "X-CSRF-Token": document.querySelector("meta[name='csrf-token']").content - } }) - if (response.ok) { - var row = event.target.closest("tr") - row.remove() - } else { - console.error("Delete request failed for", tracePath, response) - } - } -} - -document.addEventListener("click", function(event) { - var dataset = event.target.dataset - if (dataset.perfettoOpen) { - openOnPerfetto(dataset.perfettoOpen, dataset.perfettoPath) - } else if (dataset.perfettoDelete) { - deleteTrace(dataset.perfettoDelete, event) - } else if (event.target.id == "themeToggle") { - toggleTheme() - } -}) diff --git a/vendor/gems/graphql/lib/graphql/dashboard/statics/header-icon.png b/vendor/gems/graphql/lib/graphql/dashboard/statics/header-icon.png deleted file mode 100644 index ebdcc2762ed3fb71f932a174532a590236d59df2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7522 zcmY*;by!qg*ZvG0Lw66U^bDOtcZWE1Nh2VQ)X*V~z(`Ajq#)fQqO^#Bba!|9!{>c} z&-Z<2UuUm-uXXQx?REY*=eo{{(biPL!=c6j004L@%JRC8*5dItz`}T(A#JJAj|S+W zt0W7k9--ZSTxi?CRctjh0344P3xEP72B7|>JPtsV0|4zG3;?h_S^xkwANW7fe3XBq z7Wt_EVuQcLv_uK-M-f8@eYhuFLtV_;^(Bwx3s)-}9^aR4e+d8yU$IB{(#F#g?EBK$ z#Y4C13FRzb}504K&kE^>KFH}@ilo!Is%g4w4$l&(ybMdtF<#zF4{*TE2*O9mJ zuy%KF^K@`^0sqytv~uGG*E*}3a>#;!IzY$(24}|w$?Z>SW zf4yQ_?hZDOng8m;pc4Nu|Bvs#c_euMCjVcD`A?<)cppoJ;YjfQ``BPORA$SV0051# zioA@zFL2)!*HeG8Wx+qs!$5kaYYKT=0Z2HjDaoIRX z{rtJVllcb&S2D*hm`~YY<*`_*`tmaFGT;!fEF#H=4g1>=3;*U`U{^50Seb7WMEq;m zHYZzjbMO8z_|KoNKT`{v-P77s1>J<`kxwD=A&fX6ouxQ!L|%ef#zzN5S?Uo(IH@%mRkA2N7DOKtDOuNsi;Pih@Osc&Urk=>G}EAClMPu6%w@Wx5% z0)xh__Ldd=aJ84NR;2p}XepY9ogUsQo#o#X-Jx4iDp@XB!#mT}#Y`CoKX9rO!1-13 zc6jRdzF-D_@jb|^x;r@{Ecz{?t_WezjAj>+>!Rb)S-Sql-3@D$iG$^OZlhy}_-@J` zf~tgpAF83mv6QHrt)U&{F)Ep>qLgbXNPNl6qjr)_5i&qf*fYflc_)H-6d7K!^tyQ| zAa1y(X(2tM)u(vSMgb(Et{^#dxgMX)Jt{aI*LrN%a;LZ}WW%33e_E=z02=B}Ayef}D5* zc(_WsL!qQ&f#m9PeR@=Ap%*w?Hl9QkW$JF8LKaGup#YkEQBPQo92rarDNBxGj^@y1w{Sji zZ~oo%^Q{u7>yKIDd^|3yDlYCz1Li!&miBXz5C{v>2;QfM`MCqR$63T@FtR;8PL5Nz zFsf=%`{V_MOT*hr;M@aM{uR*kswbhJj=PMZ4llpcY&7>yu{32S|4y=bsMP-nSzH7T z1MrSQwACoVVkj$?lM}Ntp?9m@dOq?-4XB>E!8p3PDPp^#9De64e|F0ibQtyBjTz(w zSjl68zgC3@OdkCj8ZA7ZVBq5TA(q*pC!Mc=$O6UwT)i|nn~V-KQ97{@32ivPz%In^ z)G=ouLuG5uYNy)v!wfI4Xwi0A+Fv&L$rL&_kOmKfVJ){mC2hMOY^3h4O>YU6B0qlV>ieWU3V!kw2zZAH?dF0!0d# zHwVZn=ikbV!h5v5Gl4AzD{6e8%7f94_FMzuTN(OGv@+2lOEmJ5!|*lCs~GiWYshZ|TGfe;h7D;O~T9 zHT<}Kqm7#sWEKUQ`OID3X>&=Zm5^4Fm$k!BOVFKR0)ih+TpN70Bd74uXTZV~&)giG z!1+O|*y}}3a)}xhlaD%Q|G;-Tm=dwyMi7G3;Ro`I-hAZn2%v92$zz;u;gJsL@6=9_ zDtA7a^d$*qFUs3qVJk!8LZtJS+~53|+#%_EviD~8!pH4RMkr-k8R~h-$mQ&{#u}}> zRO=GAi$je=)9g>DEo&-J@1{uV{<}fEhY7NGBpzW+pa&L542cX;sMagIT$C;%EO5a( zCS#fwbCTaH%lU=$CDSh#EbB==eCoTygxpNF+&{-~74HvzFN_6zJD`i^H4BTi|HAL< zb7$*otLHyOvvuCi>~F;VKFvx9hMtWtjVJob3>@BkdcEh7k5b5~16{HqGIZJxrF#YQW_VC}C8Ae0iFvbX@ssa-Suu1C8XAkdV9nMt~5s$^$2 ztJHH+=vDG8skAe7k(R2n5}*z8q-zPlmyg*Tf~4jgMi&9V@i_5ghhv4)!4~jMF`{O* zovePTnAOY6GSGkey}_3TJA@tH@9trP2`{}Y3fD08zT!IGy=lL0s~wf0MhZ8TP(I+L zYlxit@Q*Wxa(!O?kUB8~d%?ksIy|ujm?q!5?`Vg=if2|f_hhdwnwZKV*g0?rjMuW_ zKE8$R8AbhfSnCy9dq}n(zog#M&hQsRWJsHf#!+!g{0t`LEFCs{(q_6*Oyf|se7Z(D z4ix|4@S5gIQDA`>8)3ggNU|YESo4kk676{_oy&DR{8#w#^EavIE{%@U%^V`!GlY6s zdAmF%$Md%|n~cwNWs?P=$xzjz83`5s_Y*|g=tUCfc(0dkKB<4@OX;*vldgJPa@YDl|o*(uNi`<_S$dGv9d2Q|>tyHcDUSx_X7Ulj?Zf=8N z!XjzYGf%W#fAA%07P@;IW93c|*4}xo99!j>X5|?z#=AO28C;ciyn1&EJH_pBottV_ zGP0nKqB7;CXM#iWpYuYxNi}qNF!|*esh@!7F#4yLPHPO*C2~`j7J`^(>0VV!{+97_ z-{({Ql7@WgkW|9_eI?j@ti8$0@-0Gwd*q(|-Mp6a2z}3z9@N9`x*?YUuo3@MMnOLZ z&Ap}2BI-ahKz5b(IHVl|tqU;pbpxo-i#tIIFKmW${2BQ9`2h3SxeM7)Dn$3KgrTr^ zYHXtJi@nir>uJ?qWNOLCD-cH~Or!`em=3SV>{edWb6HO|R+{yy^ZAlBmGeInkbtsUKO~+=_C=g7RIcXRRV^ zf0o(IrcM9(joTs+c$3^)gbw)4$iOJnc^e@Ln6)F`EV(8ktB&?4ps`5 zCJ_?C?(#QoQL;N@3gxmR4)X#J5lPf-ZC*^C8=thv*7&?-?j_bI;&xJ=p58pMRx*6v z#skB+<0}*R-kl&XbWR;xok+B@_WSrc-m{UUSqmtTBq-l=kVHDZF0Y$RmCRV^iI>iW zHh^TP8lubs$ydwsy~GG`#EW!7eg3OeAzr+G|MN4pqDS_ zkQ&}-1&!nefh3dwv)c0Sy3Z4b%M{+yb;&l2hhuCKIbhK6R`NoxN%u z=NYiahu>&6_~fbvTibuV zcb_hxNc@=S-;g%MYA5cIU~y=QITVSmXtA9A300YnSmPNOe|uS9`a5bf*Tl)Exy(Fo zxBOdX6(h&9yKzW+Q_n|}kcZZ>DOF$D53Zii`}eNL%5FK~+b8B3Zx>@2Q)tA1Aj(KV z(BQ@djPtainSQ56hx8JqcdfBA0y-B_r-OsuCgsZ-B)lX%UW+$%3&kBgzJ2$i!+J1f zM3cdtHLl8A*zaO%MlfAtA6<8nw8xVEWnV%^)3HAT;Kee+>5DWZclVg4s-7)Qf^ffA z_}r_&B$Hq8TVehsSsS`?G4tg$QG3*r^Agj%jB{SmCz<^Lox`i1KH`=SlwldYQF3FE zj4-igR;cx%qn4AeIAzf$UM*|-#=Q{v@u5Q6pbV$(q?6#6JVhKk{sv6@?@d;w*K}7< zS8pn8vgkcje8q08_aRnFoTkM}Go_tzbASPGMy(m7H15(A);Q+) z{Jg{jURg(_i^XnYSY|S=oCZt=AD?Yn9ZiK{S#Z^Sb@qRJeW$$;2os`R3Y&2@ALakm zJe*ll6ShNx$Fwf9ZhB~OqPNr##xOc6KcTUGcWpH9-xdo=_Xi&Cf>~Q)4#nV$FtlDK$iKM zwMtIinq&dzE-HSWZM45$RLX=8*MvTIXJ3Lt__D%Tr$$|8gB=&gwV8TbF^w*T5=OJ{ zFfJg0=tB4o1TfThp@*I67e;taU%Q^-5G~&1w_?Q%-b&vM$^_sQv@1I9VPow*5pKDV z!9!OayVH|KkJvp`Fn{Bdye#noC+rLkgo_eKZx))mD5}{_{H#%j!OiqYAitI^l4rLy z$|V`1A81qD@plfRbm8f_2eTq0>jf6u4e9R6i+J94S*&NCaW;Hz0|?21MuC=d46X5G z<;!cr<3I*|@>i;zjJ^H77DLxYSlfj{x8oYPyHW&YNVl+UVUvTJaT6|OAATpnvO7CZ zv_$q=y=(Wi zw@@4cpnPmDZk<^8aZLSl>=3d((5p9M%jW!9t3L#LnOsu&rg;7pJBDT}11PL#N`L0= zS41)z&-;F&eg4UrwK=@)uS}g43f6S~L64uamm@5+a==0fVj>2d>#tv92%1c)Hd}Az z`^GO43+DN$$xyNCtPFMq-TOFYKSsTegoUBayz*yLAJqyNHqhCQu3{rr9 z0=9T0M&#m!Xk9<49jkEl*YKWg9cVZHtess@bUuO|MYL)g`s!@BaC(?)LX-y$Lzv6^ z{YCo$E2}m{2^kt9#-jq)=KVIQQmrBwQ7kM?VT`)J=A4JWsr>$MNl6EhE)2n%3DdDq zu0Fe_ZY*7!>{@XVVCWsz-8N>rW~+s}eY{!DVxG+E3hFoo1xKad)@K8`I3ZJY>?=9d*FFTg0vK`$mX! zqYTb&A~V}YpvY!blhw(>O`84=B;|xFz>hI-iuv^QlRHzsO7gg|wpPMF`(KRT2$}wl z_{N`7 zO6NPS#y0UU)KKYqj;(`LuowXi+NBKWlzFuaeI9%L@GYyaU^{CEKN^UJXhgqJ=rw7l z@fJVZif{|lAoP>uOUSBwFG3N?&X6<4Nl2YS?E$`Xvs-s88VEW;or3-7vTLs#%7);{ z{4i&fn82pe4ipUfyx2|eI+ArneDg*7ZMeuU8*pOvk@n=D=k(6~EXLz6`JjMvq=>Mz|0!t{u+EE9d`!LWObPH&TL7#a^D^pwn(>@@7~ zYseKE9}fr+s%j}U^4&6(1DZypfw4#4b6EPcGVw$d@|j!JP^T1n0~A>@Mphx4QQD4{ zCc{JfL+bMa&F0Kz$SRBkMpt?PJ4whR+H@{xyc`CPx&bWZS|Udh+@AwS%jj^~UujVG zrvb@R&F$4^6W&3H26V&c%zM-~1f#JZEGCYOw!u&5Gt=_b@WffKAY}y>Jr2UoQ<{Us zQ7bX-kAHUPmMb-2d;X?;PE3u0W`Jk%iriqGE0S8*D>=LO;b}RO*=@q<6c;Nh2x6|1 z?2JiYb~KEg)OK}|Zyk2Pud;0`zSm=~otim6Fy`K4u6nBnj^WJgg`Y7`i^gZn*xS)H z3Z~3bP3y!fWFWpTvs)-qqhv3e#^glVZj=gE#;k6d7szC%6P<={>T-oWh#wV$L5{iB z`$+N#dp0))TLpfD9ib4;Keeywyc0GjnqQKGO~Q7>UsF20XKih*v~4$H>5BCf+yh3W zRgm8Xf(EAPv~{L8MwUarg>MCb(@361!&NFR)NTy07fKL#Yu+%=AMyfZAc51a1 zLcp*>ktIpcgN|uT;+w3;3N8-~6?>d;ja0k%N?N7; zExy4YH9G*~$BMT1c6qrii>~0sL9{B?wdJP>9?j32nAE{*L#`}}I`h6{H%oa@*LK~5 zhA7B-w=LwuG}%A~tJ-cAP3DJ{bhBuTP5xd)6k?gfTgK$8Gl(DBx+;>*&)~`eR$7 ztSCfKx_ue{GYFfzduU)ntd$`5(4mSu@dp`~jCNX9@&y@BUss)VUunh~EaLK$eto-B z&{*efoGQ!>=PmyTpm$|j9YAt+#*3`Jt@FC29m3etT+&vuTC44|Y%6_jL_Ns-LKZtZ z*vw#Af2hRV(k~gN6tk55eCfhk)3(#k^l(17ds#hArUyVBfWYoFIHYL2bIgO(sTs$G z1Sbfe`2n$n`NX+Bv|ib%=^0Y8z0K!?;fSMCY#&y02kbG*wVCH>ZY6=;P(pyb(WhP|F6q9fIeC_HwT7YF(3oL@CG&_3bGNGe zQ!hIM;m|hgKFnqIrh3#wdU`321)4Ir@Y zxPekINlW4#y$Si!-dE}kf9ecZMj0V`{#BETD9}kz8B(%@_{xgsmdzH>xKVEkm$=>! zE&2W&gYy`*3=;zq{Ra&>R(XJ0=lWSapQ5?0IB&U-(l_R=DAeDyS&3JE;vh2Ts_%$_ zLg2tq5E-MjC8vacAiKEH(EK2RbuECeYM81c8F3{Lx_W6JQZ578g-YuA`_+=<9nqhG zz%1K=mX?mLi0_t#R=zN9_^K&QVq%5vVrg488ORsQ$js4^GR}?R+o1-y(gN?&__axf zk^d#p=x%)|Ff|%DSFjIWUL~*29>Y zJ^>*{WU_L}Ku<$9S66=%m1Ph>sbu#*ABH@buQqPs?%OGea#Bhc?WylOd>#q6LE=mv z`Q8{l>Bikqf2o-BOv+h`cTb&)Yfse4bKRIHV{Tiy>MOAe-JF_Hzd+JVA4LZeQ{J_w zBeAw_`Eq+(()eqO(8cHc9@!JUW59x)7I6HUI>4r)V^D~kMzK27CSREQ*gSneOI@Q= z{3G=6=-tAW*M+tR*2LfLl|?j)94Y0$4Xr<4Nw3T)BZfT)_!(&ab}UsCH07&hEkgev DJU;SB diff --git a/vendor/gems/graphql/lib/graphql/dashboard/statics/icon.png b/vendor/gems/graphql/lib/graphql/dashboard/statics/icon.png deleted file mode 100644 index 47c4bdb26e4f693fec572cbb60e889e3424efcae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4976 zcmV-$6OZhPP)8_q> zVtwcQr+a2@-MaU`_x|syNhD&#h!G=3j2JOu#E20iMvNFSUf)=pnD3rR^S7u)R-8OY}-xED0R84vV7r^~`O~~5 zK2YmqR=3SNv|zdK3vBO4==W*Wx9?k*&~Q_;tR=;P%IeeaTvdw*bMPvK5BuOAJCX_;d%=AVIHGW*6t{0WP`5uYEa zb@Pl1ineG46TS{+lrg_VI2C`w=bd>+%qiO5>}p9`>3Lv)3>UGi-x8yw<@REM-)>$% z5>cdq!MEW09c6v)Bi^@IY!RM0@1{W^*)<=cW=(~`4N64tyx>f%*72%*emT=Q-6< zV7V7#^1rOmpFv`spAwgt*N;R&N}P)Y_yxwkyS%?)eXpmHVa~fL&@*Gk!8rOK#vn%# zL1H@;<+Zm@&QA4PW*=TH^};^Kskt0|{Gq(hkYF}~kyp}|H?JR!aNR}d*ytJCabG~{5m_Q7&Ozw0V6 zrqus>oUEhS*1Uc+ta36bY@lnTUIepm&SQ@nKP))Tm2)=xm=Mr`k*(fwfg@JQlA?6H zwRy+OVL~_K`#n(34?sDu^ey+Qpv4ol*y(G!yASf6%)%0bd~LPdjsAb3oF>hvXK4s5 zo;P-nIEO{^kLYR-EO9P8YkJrBV@@D9!0*0-zSpX`agLUEo8Q`TEd3s{pK(QwgKiZb zWalyrY+ImqiMt-6;_QOe*%q{QqNpYR>4|=N>wf6rlXWH>3@EtAZC zs=<2fXgF{m1*4zmF2v;5^ugKU>7yu+N$-Q9?%4zTe=D54kMkO{pK4J|B;Gi5+?-2s z>a59um8P=Z5Spwn!Y~N(aO861y$BqToZ`MMZ@WL>Zcy^x@g6N$=}X9bvwgkby+R9! z4Bm`|zT$=FI_lAslMLOv( zH9}ER6r%r6VD69Udx;k)4)c~f&1~Bc@J6`dzeyd5q*8j4)(R+d}F82G+_kX1gdrlNcxdYDu$HTYGXY`87uyI z-0&GK4!ma|8opd;$-LEU8wLE9`G-TteAibp7bc+P-B>(aw3zKmP&&!>?m#;R{9Y?K zVsX!uxl_z`QGika;^_0Z*YDJV+uqZ@<9<4TIcv6!1nd~c|I=WE+Ku@9J*<+i^lDif zx%{I{4V>&*fu-X9IBDV`S|*7R?nQ-JNO|XB!PEu`bS!^Su0cwYvR@{}zsh}qykEg& zy{b2Lq+_?~}=>miC5*yBlkn3=%Hj1eAr$kl(8Oh|;xz|C%A zT)dJjjsjuY@k-q9xj>6LI6>}FX1o1hl7!!ZYk$pa@wp{$dH>u9g-mb}mS7`d5pKl2 zcBp)X<2`^=c>dz4^QzCFsccru>~=;p{iORLbo8FSWXG|vK>h)8?g1=`|fm_@*?@%z|OTIP|c4HxaPAkc57Zny^Bj%lsIk?v?5PL7- zzPms_$M^HeL#9y+;AyXwJ%tK9m&gqliOPatEZ_FNB~?7fyBAFVDHvU(XR8&U;O;N< zrQGA|O@5DrRaWO=%F0rKH5GDLR*QL?w>mp z0H0zLo^!zQ!*NtRwZ&@ud|knwbM#=iB-h=25NzhtanVntjx_F6sXZMZ->$!Vd!bxb z@o2BXVj1_26g+vj-$w3UDp9{Bv#=IDqjJcWxP+TWp9JxcZ)MIj+eHa(WO&9WY2Brq zQ%a5PQst`^A9vty{|8?Cea`G7)x1Il%NYKM2?;E~7e_Jyqk!@;Eb;w74|)$QpzG@~ zBmI`Hc8E>}2{{K&{$`36LIp|MiVM{PmA0ITv-%A4n|kRv3~2gwn=J+^2y zZx|KSNwmcUxYz$kExR}9cu(ynB=ttV)qNlOdNveAPhycTt|eMpE#&mNZPN zHIVvgMARbIssW47AUbcaz`x-H@pLOsBX%7fiSh1(T(xo_13U9MIeB_CcDex_I2^q)1asY8oGPX#m|_cE_p(OA$nkEY zOf_#Y+Em=$C$S$ z<6rp#Ji+`(LKP;#aZr=5&~T6}@4M6iQS8d02nWoJ(^gt6P`uxZI=q{M9zox8CF4Xo zX~V46*P>R)I((v1n>Up4Zy32Rv4HLCi4zV<3?yd1Id6%JA-7nXF{~`lw_PrZS{OPr zg_QUJ>od(8*9uwzOs4p%EJTTi^)y3^iRisnX1b)Exd^uTQ{j9bf&TkjnvQ1MT0!36 z^uAx}^d)&%Nw+eDKLF=)el9dHCf&q98x{e7EMg(nG3FDHDatocnI?L6w3G%Qo6n4* zK9akI70t>tbd(AFybkF$D4M836myTleR?<_<=I=JH!5Aj&E$hO+_d+JYRbWYZaf(Sh!LU31ROZZjO}QV# z!a-jb>ToWd7I7D=lN%LZB6!;2bp8zuif}hRLuc(~c;9B*v4G|-nTr1IB#%Jb!1$#w zCeC9jHE`TdF@K=zkHlPtO3XeKIC)wmx4=1LG>@V{J8J3kA&&0T${KRo7I)=saYn6+ zus4x=gP%%grIN`i-t!E^!<9@enr)-Tl4QpbOkW1NERoU6A4~hFxg3+(wi*-{!H7t@ zr=dSMv#KPA4&O4F%AQ7Ea!$-0C|!r2bG7(2E1<2RG(M*CvX)B2QCAEwenbb#Y(E+X zTIL_l zR4jrigfM9`UkCYg5`+Clp;xMfO5)qzHNNsY zn7DBtfpd1!SR%Y+J{cYGfevj|?FMi9{iJtllqU2rP*%cXI(MJdq8B0WxbLoZ5jc6N zJyZG~+wr-$2(e(cYcMA9u_HcJQ}U1l%l-cC)2mCOkooi@WQqAYXEBh+$!d7nVlmNv z2#)MIxZxwdHc^>`7=WUA_h?BE?y$aAMmt?rw=qJwUZ&jBYL&nBAWZsP1p1BMOkxY> zaMoz2fO?P2hv@8A{)EzD?p<=4dvZOsNMiyS9VESvV$d6T^hzcw#Sx#;+Mg`%eM^^9 z;I89@)iYS)+V8+VH`|RDitTfbz#Muoem)t)=<9zLtfSk^b|v&AW*kfrgE@2O?_fL! z3jIg2wewv(6=wT}f(}f$2oHv_ZZ5(dvPGOu1youDT2?tIzBM2^eA?L)Oxyj0mXOiy zxC1i$49Q#p^%}$x7UK%;2mx)M*B2T zX&q^2Y6rfrf`he?VG9;esk$CM-_`0J=Tqb;r6)GS3%#sbz0pWeuj!=vU{&2iux#c_ zD?1P7<1us=Rh=tDETgqj>pec*)6Ww>v_fw}ppNre2;06JM?^6yTh>zG}{LAtER($^+#LSnW_e#v)hM!mQ>dSOEmiH$#1l^>gOE&;nBz5QAU{RSG3VYi;A;q^Dp)NW_;Aur z7*7h@HVchdlt9uCXt*bv^KKgOc*-!|J_?1Q3}&b%o^3^u_(mcs0YAM+7}f2J5uFES{9=88Sr-di|EWj0LQc0X!>x%0zmS zymv3gAXQ>WjfuGqzCz(Ssq^CuIc#Q~4>N_uW;8!5L8s`%T$h_(`os!)!x!Oqp9AB+jd9-)Sl+*|vbqt4 z0^<(uxFG3X7BD9p7OR5#RVaHLcbAG`(PvSolw(kZ$oKkA=0k&zrM91N2p4I8k=@rV zn2cxAU+Ond>55^;TeZ{?r`MYtuLfLbnasjk6Sqoj3ez)Z8OfsY%*stDn17W?_flWi z@2e7SEW%3l&bJ1h1kHSvKwn1NpJ5SvU&AN+n<}=`Xz*Aw(a)2h>X5=kz;0a5^d?V< zc}=~bp3(2K<3lH^Y=m*2yJbq8!-qo5_6Iz;2z;85c>%d^5%|7|WpwC<6)x2YCdnlf zE_OOYii!SSyW_-M4b@~hzDlRXJ>FN@x$rqH+gqzj-^ZZ> z?{x>Y2!}DLlli2t9ta;&foG-X<;JiGxl%d0s$LKjIecpIKHtuq{THcls|6Qv&df z%T&iKzM_~UQ`uSScS*jxjui_f=6es~``;aK;e_#mdL5Y(N9xg4r4#Zv34C3>RQ07| zwIY1)bLd&~yLzn+&Djln0w!jDoluLQUXCOl_xHW~fmY41n0@%W-BMS#Z3t57b7SU5 z1jcEg13oJ&Fusf|@9Dtqyl}Ka;TU58cXHp|D z}DS^>OhX3Ei0^bB(t39LkO18W5wj0 z@qXgwzz56Sef1KI5lct-?C;m&KwAUAd@@TP87J~9{vR2Y - -
    -
    -
    -
    -
    -

    - Welcome to the GraphQL-Ruby Dashboard -

    -
    -

    - Click the links above to see data about your schema (<%= schema_class %>). -

    -
    -
    -
    -
    diff --git a/vendor/gems/graphql/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb b/vendor/gems/graphql/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb deleted file mode 100644 index db64fe71bca..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +++ /dev/null @@ -1,63 +0,0 @@ -<% content_for(:title, "Profiles") %> -
    -
    -

    Detailed Profiles

    -
    -
    -<% if !@detailed_trace_installed %> -
    -
    -
    -
    -
    -

    - Traces aren't installed yet -

    -
    -

    - GraphQL-Ruby can instrument production traffic and save tracing artifacts here for later review. -

    -

    - Read more in <%= link_to "the tracing docs", "https://graphql-ruby.org/queries/tracing#detailed-traces" %>. -

    -
    -
    -
    -
    -<% else %> -
    -
    - - - - - - - - - - - <% if @traces.empty? %> - - - - <% end %> - <% @traces.each do |trace| %> - - - - - - - - <% end %> - -
    OperationDuration (ms) TimestampOpen in Perfetto UI
    - No traces saved yet. Read about saving traces <%= link_to "in the docs", "https://graphql-ruby.org/queries/tracing#detailed-profiles" %>. -
    <%= trace.operation_name %><%= trace.duration_ms.round(2) %><%= Time.at(trace.begin_ms / 1000.0).strftime("%Y-%m-%d %H:%M:%S.%L") %><%= link_to "View ↗", "#", data: { perfetto_open: trace.operation_name, perfetto_path: "#{graphql_dashboard.traces_path}/#{trace.id}" } %><%= link_to "Delete", "#", data: { perfetto_delete: "#{graphql_dashboard.traces_path}/#{trace.id}" }, class: "text-danger" %>
    - <% if @last && @traces.size >= @last %> - <%= link_to("Previous >", graphql_dashboard.traces_path(last: @last, before: @traces.last.begin_ms), class: "btn btn-outline-primary") %> - <% end %> -
    -
    -<% end %> diff --git a/vendor/gems/graphql/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb b/vendor/gems/graphql/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb deleted file mode 100644 index f2c82af2b38..00000000000 --- a/vendor/gems/graphql/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +++ /dev/null @@ -1,60 +0,0 @@ - - - - " /> - - - GraphQL Dashboard <%= content_for?(:title) ? " · #{content_for(:title)}" : "" %> - <%= stylesheet_link_tag graphql_dashboard.static_path("bootstrap-5.3.3.min.css") %> - <%= stylesheet_link_tag graphql_dashboard.static_path("dashboard.css") %> - <%= javascript_include_tag graphql_dashboard.static_path("bootstrap-5.3.3.min.js") %> - <%= javascript_include_tag graphql_dashboard.static_path("dashboard.js") %> - <%= csrf_meta_tags %> - - -
    -
    -
    -
    - -
    -
    -
    -
    - <%= yield %> -
    -
    -
    -
    -
    -
    -
    -

    - GraphQL-Ruby v<%= GraphQL::VERSION %> · <%= schema_class %> -

    -
    -
    -
    -
    -
    - - diff --git a/vendor/gems/graphql/lib/graphql/dataloader.rb b/vendor/gems/graphql/lib/graphql/dataloader.rb deleted file mode 100644 index 92ddac4f6ca..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader.rb +++ /dev/null @@ -1,334 +0,0 @@ -# frozen_string_literal: true - -require "graphql/dataloader/null_dataloader" -require "graphql/dataloader/request" -require "graphql/dataloader/request_all" -require "graphql/dataloader/source" -require "graphql/dataloader/active_record_association_source" -require "graphql/dataloader/active_record_source" - -module GraphQL - # This plugin supports Fiber-based concurrency, along with {GraphQL::Dataloader::Source}. - # - # @example Installing Dataloader - # - # class MySchema < GraphQL::Schema - # use GraphQL::Dataloader - # end - # - # @example Waiting for batch-loaded data in a GraphQL field - # - # field :team, Types::Team, null: true - # - # def team - # dataloader.with(Sources::Record, Team).load(object.team_id) - # end - # - class Dataloader - class << self - attr_accessor :default_nonblocking, :default_fiber_limit - end - - def self.use(schema, nonblocking: nil, fiber_limit: nil) - dataloader_class = if nonblocking - warn("`nonblocking: true` is deprecated from `GraphQL::Dataloader`, please use `GraphQL::Dataloader::AsyncDataloader` instead. Docs: https://graphql-ruby.org/dataloader/async_dataloader.") - Class.new(self) { self.default_nonblocking = true } - else - self - end - - if fiber_limit - dataloader_class = Class.new(dataloader_class) - dataloader_class.default_fiber_limit = fiber_limit - end - - schema.dataloader_class = dataloader_class - end - - # Call the block with a Dataloader instance, - # then run all enqueued jobs and return the result of the block. - def self.with_dataloading(&block) - dataloader = self.new - result = nil - dataloader.append_job { - result = block.call(dataloader) - } - dataloader.run - result - end - - def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit) - @source_cache = Hash.new { |h, k| h[k] = {} } - @pending_jobs = [] - if !nonblocking.nil? - @nonblocking = nonblocking - end - @fiber_limit = fiber_limit - end - - # @return [Integer, nil] - attr_reader :fiber_limit - - def nonblocking? - @nonblocking - end - - # This is called before the fiber is spawned, from the parent context (i.e. from - # the thread or fiber that it is scheduled from). - # - # @return [Hash] Current fiber-local variables - def get_fiber_variables - fiber_vars = {} - Thread.current.keys.each do |fiber_var_key| - fiber_vars[fiber_var_key] = Thread.current[fiber_var_key] - end - fiber_vars - end - - # Set up the fiber variables in a new fiber. - # - # This is called within the fiber, right after it is spawned. - # - # @param vars [Hash] Fiber-local variables from {get_fiber_variables} - # @return [void] - def set_fiber_variables(vars) - vars.each { |k, v| Thread.current[k] = v } - nil - end - - # This method is called when Dataloader is finished using a fiber. - # Use it to perform any cleanup, such as releasing database connections (if required manually) - def cleanup_fiber - end - - # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on. - # - # @param source_class [Class] - # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`, - # and cached for the lifetime of this {Multiplex}. - if RUBY_VERSION < "3" || RUBY_ENGINE != "ruby" # truffle-ruby wasn't doing well with the implementation below - def with(source_class, *batch_args) - batch_key = source_class.batch_key_for(*batch_args) - @source_cache[source_class][batch_key] ||= begin - source = source_class.new(*batch_args) - source.setup(self) - source - end - end - else - def with(source_class, *batch_args, **batch_kwargs) - batch_key = source_class.batch_key_for(*batch_args, **batch_kwargs) - @source_cache[source_class][batch_key] ||= begin - source = source_class.new(*batch_args, **batch_kwargs) - source.setup(self) - source - end - end - end - # Tell the dataloader that this fiber is waiting for data. - # - # Dataloader will resume the fiber after the requested data has been loaded (by another Fiber). - # - # @return [void] - def yield(source = Fiber[:__graphql_current_dataloader_source]) - trace = Fiber[:__graphql_current_multiplex]&.current_trace - trace&.dataloader_fiber_yield(source) - Fiber.yield - trace&.dataloader_fiber_resume(source) - nil - end - - # @api private Nothing to see here - def append_job(&job) - # Given a block, queue it up to be worked through when `#run` is called. - # (If the dataloader is already running, than a Fiber will pick this up later.) - @pending_jobs.push(job) - nil - end - - # Clear any already-loaded objects from {Source} caches - # @return [void] - def clear_cache - @source_cache.each do |_source_class, batched_sources| - batched_sources.each_value(&:clear_cache) - end - nil - end - - # Use a self-contained queue for the work in the block. - def run_isolated - prev_queue = @pending_jobs - prev_pending_keys = {} - @source_cache.each do |source_class, batched_sources| - batched_sources.each do |batch_args, batched_source_instance| - if batched_source_instance.pending? - prev_pending_keys[batched_source_instance] = batched_source_instance.pending.dup - batched_source_instance.pending.clear - end - end - end - - @pending_jobs = [] - res = nil - # Make sure the block is inside a Fiber, so it can `Fiber.yield` - append_job { - res = yield - } - run - res - ensure - @pending_jobs = prev_queue - prev_pending_keys.each do |source_instance, pending| - pending.each do |key, value| - if !source_instance.results.key?(key) - source_instance.pending[key] = value - end - end - end - end - - def run - trace = Fiber[:__graphql_current_multiplex]&.current_trace - jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit - job_fibers = [] - next_job_fibers = [] - source_fibers = [] - next_source_fibers = [] - first_pass = true - manager = spawn_fiber do - trace&.begin_dataloader(self) - while first_pass || !job_fibers.empty? - first_pass = false - - while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber(trace)))) - if f.alive? - finished = run_fiber(f) - if !finished - next_job_fibers << f - end - end - end - join_queues(job_fibers, next_job_fibers) - - while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }) - while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber(trace))) - if f.alive? - finished = run_fiber(f) - if !finished - next_source_fibers << f - end - end - end - join_queues(source_fibers, next_source_fibers) - end - end - - trace&.end_dataloader(self) - end - - run_fiber(manager) - - if manager.alive? - raise "Invariant: Manager fiber didn't terminate properly." - end - - if !job_fibers.empty? - raise "Invariant: job fibers should have exited but #{job_fibers.size} remained" - end - if !source_fibers.empty? - raise "Invariant: source fibers should have exited but #{source_fibers.size} remained" - end - - rescue UncaughtThrowError => e - throw e.tag, e.value - end - - def run_fiber(f) - f.resume - end - - def spawn_fiber - fiber_vars = get_fiber_variables - Fiber.new(blocking: !@nonblocking) { - set_fiber_variables(fiber_vars) - yield - cleanup_fiber - } - end - - # Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere. - # These will be used by {Dataloader::ActiveRecordSource}, {Dataloader::ActiveRecordAssociationSource} and their helper - # methods, `dataload_record` and `dataload_association`. - # @param records [Array] Already-loaded records to warm the cache with - # @param index_by [Symbol] The attribute to use as the cache key. (Should match `find_by:` when using {ActiveRecordSource}) - # @return [void] - def merge_records(records, index_by: :id) - records_by_class = Hash.new { |h, k| h[k] = {} } - records.each do |r| - records_by_class[r.class][r.public_send(index_by)] = r - end - records_by_class.each do |r_class, records| - with(ActiveRecordSource, r_class).merge(records) - end - end - - private - - def calculate_fiber_limit - total_fiber_limit = @fiber_limit || Float::INFINITY - if total_fiber_limit < 4 - raise ArgumentError, "Dataloader fiber limit is too low (#{total_fiber_limit}), it must be at least 4" - end - total_fiber_limit -= 1 # deduct one fiber for `manager` - # Deduct at least one fiber for sources - jobs_fiber_limit = total_fiber_limit - 2 - return jobs_fiber_limit, total_fiber_limit - end - - def join_queues(prev_queue, new_queue) - @nonblocking && Fiber.scheduler.run - prev_queue.concat(new_queue) - new_queue.clear - end - - def spawn_job_fiber(trace) - if !@pending_jobs.empty? - spawn_fiber do - trace&.dataloader_spawn_execution_fiber(@pending_jobs) - while job = @pending_jobs.shift - job.call - end - trace&.dataloader_fiber_exit - end - end - end - - def spawn_source_fiber(trace) - pending_sources = nil - @source_cache.each_value do |source_by_batch_params| - source_by_batch_params.each_value do |source| - if source.pending? - pending_sources ||= [] - pending_sources << source - end - end - end - - if pending_sources - spawn_fiber do - trace&.dataloader_spawn_source_fiber(pending_sources) - pending_sources.each do |source| - Fiber[:__graphql_current_dataloader_source] = source - trace&.begin_dataloader_source(source) - source.run_pending_keys - trace&.end_dataloader_source(source) - end - trace&.dataloader_fiber_exit - end - end - end - end -end - -require "graphql/dataloader/async_dataloader" diff --git a/vendor/gems/graphql/lib/graphql/dataloader/active_record_association_source.rb b/vendor/gems/graphql/lib/graphql/dataloader/active_record_association_source.rb deleted file mode 100644 index 9f87793e6fc..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/active_record_association_source.rb +++ /dev/null @@ -1,64 +0,0 @@ -# frozen_string_literal: true -require "graphql/dataloader/source" -require "graphql/dataloader/active_record_source" - -module GraphQL - class Dataloader - class ActiveRecordAssociationSource < GraphQL::Dataloader::Source - RECORD_SOURCE_CLASS = ActiveRecordSource - - def initialize(association, scope = nil) - @association = association - @scope = scope - end - - def load(record) - if (assoc = record.association(@association)).loaded? - assoc.target - else - super - end - end - - def fetch(records) - record_classes = Set.new.compare_by_identity - associated_classes = Set.new.compare_by_identity - records.each do |record| - if record_classes.add?(record.class) - reflection = record.class.reflect_on_association(@association) - if !reflection.polymorphic? && reflection.klass - associated_classes.add(reflection.klass) - end - end - end - - available_records = [] - associated_classes.each do |assoc_class| - already_loaded_records = dataloader.with(RECORD_SOURCE_CLASS, assoc_class).results.values - available_records.concat(already_loaded_records) - end - - ::ActiveRecord::Associations::Preloader.new(records: records, associations: @association, available_records: available_records, scope: @scope).call - - loaded_associated_records = records.map { |r| r.public_send(@association) } - records_by_model = {} - loaded_associated_records.each do |record| - if record - updates = records_by_model[record.class] ||= {} - updates[record.id] = record - end - end - - if @scope.nil? - # Don't cache records loaded via scope because they might have reduced `SELECT`s - # Could check .select_values here? - records_by_model.each do |model_class, updates| - dataloader.with(RECORD_SOURCE_CLASS, model_class).merge(updates) - end - end - - loaded_associated_records - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dataloader/active_record_source.rb b/vendor/gems/graphql/lib/graphql/dataloader/active_record_source.rb deleted file mode 100644 index 7641baaf217..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/active_record_source.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -require "graphql/dataloader/source" - -module GraphQL - class Dataloader - class ActiveRecordSource < GraphQL::Dataloader::Source - def initialize(model_class, find_by: model_class.primary_key) - @model_class = model_class - @find_by = find_by - @type_for_column = @model_class.type_for_attribute(@find_by) - end - - def load(requested_key) - casted_key = @type_for_column.cast(requested_key) - super(casted_key) - end - - def fetch(record_ids) - records = @model_class.where(@find_by => record_ids) - record_lookup = {} - records.each { |r| record_lookup[r.public_send(@find_by)] = r } - record_ids.map { |id| record_lookup[id] } - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dataloader/async_dataloader.rb b/vendor/gems/graphql/lib/graphql/dataloader/async_dataloader.rb deleted file mode 100644 index cdd77d4e61a..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/async_dataloader.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Dataloader - class AsyncDataloader < Dataloader - def yield(source = Fiber[:__graphql_current_dataloader_source]) - trace = Fiber[:__graphql_current_multiplex]&.current_trace - trace&.dataloader_fiber_yield(source) - if (condition = Fiber[:graphql_dataloader_next_tick]) - condition.wait - else - Fiber.yield - end - trace&.dataloader_fiber_resume(source) - nil - end - - def run - trace = Fiber[:__graphql_current_multiplex]&.current_trace - jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit - job_fibers = [] - next_job_fibers = [] - source_tasks = [] - next_source_tasks = [] - first_pass = true - sources_condition = Async::Condition.new - manager = spawn_fiber do - trace&.begin_dataloader(self) - while first_pass || !job_fibers.empty? - first_pass = false - fiber_vars = get_fiber_variables - - while (f = (job_fibers.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size) < jobs_fiber_limit) && spawn_job_fiber(trace)))) - if f.alive? - finished = run_fiber(f) - if !finished - next_job_fibers << f - end - end - end - job_fibers.concat(next_job_fibers) - next_job_fibers.clear - - Sync do |root_task| - set_fiber_variables(fiber_vars) - while !source_tasks.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) } - while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition, trace)))) - if task.alive? - root_task.yield # give the source task a chance to run - next_source_tasks << task - end - end - sources_condition.signal - source_tasks.concat(next_source_tasks) - next_source_tasks.clear - end - end - end - trace&.end_dataloader(self) - end - - manager.resume - if manager.alive? - raise "Invariant: Manager didn't terminate successfully: #{manager}" - end - - rescue UncaughtThrowError => e - throw e.tag, e.value - end - - private - - def spawn_source_task(parent_task, condition, trace) - pending_sources = nil - @source_cache.each_value do |source_by_batch_params| - source_by_batch_params.each_value do |source| - if source.pending? - pending_sources ||= [] - pending_sources << source - end - end - end - - if pending_sources - fiber_vars = get_fiber_variables - parent_task.async do - trace&.dataloader_spawn_source_fiber(pending_sources) - set_fiber_variables(fiber_vars) - Fiber[:graphql_dataloader_next_tick] = condition - pending_sources.each do |s| - trace&.begin_dataloader_source(s) - s.run_pending_keys - trace&.end_dataloader_source(s) - end - cleanup_fiber - trace&.dataloader_fiber_exit - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dataloader/null_dataloader.rb b/vendor/gems/graphql/lib/graphql/dataloader/null_dataloader.rb deleted file mode 100644 index db6488a67f6..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/null_dataloader.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Dataloader - # The default implementation of dataloading -- all no-ops. - # - # The Dataloader interface isn't public, but it enables - # simple internal code while adding the option to add Dataloader. - class NullDataloader < Dataloader - # These are all no-ops because code was - # executed synchronously. - def run; end - def run_isolated; yield; end - def yield(_source) - raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources." - end - - def append_job - yield - nil - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dataloader/request.rb b/vendor/gems/graphql/lib/graphql/dataloader/request.rb deleted file mode 100644 index c66a41fed3c..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/request.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Dataloader - # @see Source#request which returns an instance of this - class Request - def initialize(source, key) - @source = source - @key = key - end - - # Call this method to cause the current Fiber to wait for the results of this request. - # - # @return [Object] the object loaded for `key` - def load - @source.load(@key) - end - - def load_with_deprecation_warning - warn("Returning `.request(...)` from GraphQL::Dataloader is deprecated, use `.load(...)` instead. (See usage of #{@source} with #{@key.inspect}).") - load - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dataloader/request_all.rb b/vendor/gems/graphql/lib/graphql/dataloader/request_all.rb deleted file mode 100644 index dbcea6558d2..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/request_all.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Dataloader - # @see Source#request_all which returns an instance of this. - class RequestAll < Request - def initialize(source, keys) - @source = source - @keys = keys - end - - # Call this method to cause the current Fiber to wait for the results of this request. - # - # @return [Array] One object for each of `keys` - def load - @source.load_all(@keys) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dataloader/source.rb b/vendor/gems/graphql/lib/graphql/dataloader/source.rb deleted file mode 100644 index 86ce88726e0..00000000000 --- a/vendor/gems/graphql/lib/graphql/dataloader/source.rb +++ /dev/null @@ -1,199 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Dataloader - class Source - # Called by {Dataloader} to prepare the {Source}'s internal state - # @api private - def setup(dataloader) - # These keys have been requested but haven't been fetched yet - @pending = {} - # These keys have been passed to `fetch` but haven't been finished yet - @fetching = {} - # { key => result } - @results = {} - @dataloader = dataloader - end - - attr_reader :dataloader - - # @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result. - def request(value) - res_key = result_key_for(value) - if !@results.key?(res_key) - @pending[res_key] ||= value - end - Dataloader::Request.new(self, value) - end - - # Implement this method to return a stable identifier if different - # key objects should load the same data value. - # - # @param value [Object] A value passed to `.request` or `.load`, for which a value will be loaded - # @return [Object] The key for tracking this pending data - def result_key_for(value) - value - end - - # @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results. - def request_all(values) - values.each do |v| - res_key = result_key_for(v) - if !@results.key?(res_key) - @pending[res_key] ||= v - end - end - Dataloader::RequestAll.new(self, values) - end - - # @param value [Object] A loading value which will be passed to {#fetch} if it isn't already in the internal cache. - # @return [Object] The result from {#fetch} for `key`. If `key` hasn't been loaded yet, the Fiber will yield until it's loaded. - def load(value) - result_key = result_key_for(value) - if @results.key?(result_key) - result_for(result_key) - else - @pending[result_key] ||= value - sync([result_key]) - result_for(result_key) - end - end - - # @param values [Array] Loading keys which will be passed to `#fetch` (or read from the internal cache). - # @return [Object] The result from {#fetch} for `keys`. If `keys` haven't been loaded yet, the Fiber will yield until they're loaded. - def load_all(values) - result_keys = [] - pending_keys = [] - values.each { |v| - k = result_key_for(v) - result_keys << k - if !@results.key?(k) - @pending[k] ||= v - pending_keys << k - end - } - - if !pending_keys.empty? - sync(pending_keys) - end - - result_keys.map { |k| result_for(k) } - end - - # Subclasses must implement this method to return a value for each of `keys` - # @param keys [Array] keys passed to {#load}, {#load_all}, {#request}, or {#request_all} - # @return [Array] A loaded value for each of `keys`. The array must match one-for-one to the list of `keys`. - def fetch(keys) - # somehow retrieve these from the backend - raise "Implement `#{self.class}#fetch(#{keys.inspect}) to return a record for each of the keys" - end - - MAX_ITERATIONS = 1000 - # Wait for a batch, if there's anything to batch. - # Then run the batch and update the cache. - # @return [void] - def sync(pending_result_keys) - @dataloader.yield(self) - iterations = 0 - while pending_result_keys.any? { |key| !@results.key?(key) } - iterations += 1 - if iterations > MAX_ITERATIONS - raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}." - end - @dataloader.yield(self) - end - nil - end - - # @return [Boolean] True if this source has any pending requests for data. - def pending? - !@pending.empty? - end - - # Add these key-value pairs to this source's cache - # (future loads will use these merged values). - # @param new_results [Hash Object>] key-value pairs to cache in this source - # @return [void] - def merge(new_results) - new_results.each do |new_k, new_v| - key = result_key_for(new_k) - @results[key] = new_v - end - nil - end - - # Called by {GraphQL::Dataloader} to resolve and pending requests to this source. - # @api private - # @return [void] - def run_pending_keys - if !@fetching.empty? - @fetching.each_key { |k| @pending.delete(k) } - end - return if @pending.empty? - fetch_h = @pending - @pending = {} - @fetching.merge!(fetch_h) - results = fetch(fetch_h.values) - fetch_h.each_with_index do |(key, _value), idx| - @results[key] = results[idx] - end - nil - rescue StandardError => error - fetch_h.each_key { |key| @results[key] = error } - ensure - fetch_h && fetch_h.each_key { |k| @fetching.delete(k) } - end - - # These arguments are given to `dataloader.with(source_class, ...)`. The object - # returned from this method is used to de-duplicate batch loads under the hood - # by using it as a Hash key. - # - # By default, the arguments are all put in an Array. To customize how this source's - # batches are merged, override this method to return something else. - # - # For example, if you pass `ActiveRecord::Relation`s to `.with(...)`, you could override - # this method to call `.to_sql` on them, thus merging `.load(...)` calls when they apply - # to equivalent relations. - # - # @param batch_args [Array] - # @param batch_kwargs [Hash] - # @return [Object] - def self.batch_key_for(*batch_args, **batch_kwargs) - [*batch_args, **batch_kwargs] - end - - # Clear any already-loaded objects for this source - # @return [void] - def clear_cache - @results.clear - nil - end - - attr_reader :pending, :results - - private - - # Reads and returns the result for the key from the internal cache, or raises an error if the result was an error - # @param key [Object] key passed to {#load} or {#load_all} - # @return [Object] The result from {#fetch} for `key`. - # @api private - def result_for(key) - if !@results.key?(key) - raise GraphQL::InvariantError, <<-ERR -Fetching result for a key on #{self.class} that hasn't been loaded yet (#{key.inspect}, loaded: #{@results.keys}) - -This key should have been loaded already. This is a bug in GraphQL::Dataloader, please report it on GitHub: https://github.com/rmosolgo/graphql-ruby/issues/new. -ERR - end - result = @results[key] - if result.is_a?(StandardError) - # Dup it because the rescuer may modify it. - # (This happens for GraphQL::ExecutionErrors, at least) - raise result.dup - end - - result - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/date_encoding_error.rb b/vendor/gems/graphql/lib/graphql/date_encoding_error.rb deleted file mode 100644 index 197c7f09b2e..00000000000 --- a/vendor/gems/graphql/lib/graphql/date_encoding_error.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # This error is raised when `Types::ISO8601Date` is asked to return a value - # that cannot be parsed to a Ruby Date. - # - # @see GraphQL::Types::ISO8601Date which raises this error - class DateEncodingError < GraphQL::RuntimeTypeError - # The value which couldn't be encoded - attr_reader :date_value - - def initialize(value) - @date_value = value - super("Date cannot be parsed: #{value}. \nDate must be be able to be parsed as a Ruby Date object.") - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/dig.rb b/vendor/gems/graphql/lib/graphql/dig.rb deleted file mode 100644 index fc6c8f44ce6..00000000000 --- a/vendor/gems/graphql/lib/graphql/dig.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Dig - # implemented using the old activesupport #dig instead of the ruby built-in - # so we can use some of the magic in Schema::InputObject and Interpreter::Arguments - # to handle stringified/symbolized keys. - # - # @param args [Array<[String, Symbol>] Retrieves the value object corresponding to the each key objects repeatedly - # @return [Object] - def dig(own_key, *rest_keys) - val = self[own_key] - if val.nil? || rest_keys.empty? - val - else - val.dig(*rest_keys) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/duration_encoding_error.rb b/vendor/gems/graphql/lib/graphql/duration_encoding_error.rb deleted file mode 100644 index 9611bb77950..00000000000 --- a/vendor/gems/graphql/lib/graphql/duration_encoding_error.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # This error is raised when `Types::ISO8601Duration` is asked to return a value - # that cannot be parsed as an ISO8601-formatted duration by ActiveSupport::Duration. - # - # @see GraphQL::Types::ISO8601Duration which raises this error - class DurationEncodingError < GraphQL::RuntimeTypeError - # The value which couldn't be encoded - attr_reader :duration_value - - def initialize(value) - @duration_value = value - super("Duration cannot be parsed: #{value}. \nDuration must be an ISO8601-formatted duration.") - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution.rb b/vendor/gems/graphql/lib/graphql/execution.rb deleted file mode 100644 index b9eddc263ab..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -require "graphql/execution/directive_checks" -require "graphql/execution/interpreter" -require "graphql/execution/lazy" -require "graphql/execution/lookahead" -require "graphql/execution/multiplex" -require "graphql/execution/errors" - -module GraphQL - module Execution - # @api private - class Skip < GraphQL::Error; end - - # Just a singleton for implementing {Query::Context#skip} - # @api private - SKIP = Skip.new - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/directive_checks.rb b/vendor/gems/graphql/lib/graphql/execution/directive_checks.rb deleted file mode 100644 index 52e4738b14c..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/directive_checks.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Execution - # Boolean checks for how an AST node's directives should - # influence its execution - # @api private - module DirectiveChecks - SKIP = "skip" - INCLUDE = "include" - - module_function - - # @return [Boolean] Should this node be included in the query? - def include?(directive_ast_nodes, query) - directive_ast_nodes.each do |directive_ast_node| - name = directive_ast_node.name - directive_defn = query.schema.directives[name] - case name - when SKIP - args = query.arguments_for(directive_ast_node, directive_defn) - if args[:if] == true - return false - end - when INCLUDE - args = query.arguments_for(directive_ast_node, directive_defn) - if args[:if] == false - return false - end - else - # Undefined directive, or one we don't care about - end - end - true - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/errors.rb b/vendor/gems/graphql/lib/graphql/execution/errors.rb deleted file mode 100644 index d4dcb775093..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/errors.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Errors - # Register this handler, updating the - # internal handler index to maintain least-to-most specific. - # - # @param error_class [Class] - # @param error_handlers [Hash] - # @param error_handler [Proc] - # @return [void] - def self.register_rescue_from(error_class, error_handlers, error_handler) - subclasses_handlers = {} - this_level_subclasses = [] - # During this traversal, do two things: - # - Identify any already-registered subclasses of this error class - # and gather them up to be inserted _under_ this class - # - Find the point in the index where this handler should be inserted - # (That is, _under_ any superclasses, or at top-level, if there are no superclasses registered) - while (error_handlers) do - this_level_subclasses.clear - # First, identify already-loaded handlers that belong - # _under_ this one. (That is, they're handlers - # for subclasses of `error_class`.) - error_handlers.each do |err_class, handler| - if err_class < error_class - subclasses_handlers[err_class] = handler - this_level_subclasses << err_class - end - end - # Any handlers that we'll be moving, delete them from this point in the index - this_level_subclasses.each do |err_class| - error_handlers.delete(err_class) - end - - # See if any keys in this hash are superclasses of this new class: - next_index_point = error_handlers.find { |err_class, handler| error_class < err_class } - if next_index_point - error_handlers = next_index_point[1][:subclass_handlers] - else - # this new handler doesn't belong to any sub-handlers, - # so insert it in the current set of `handlers` - break - end - end - # Having found the point at which to insert this handler, - # register it and merge any subclass handlers back in at this point. - this_class_handlers = error_handlers[error_class] - this_class_handlers[:handler] = error_handler - this_class_handlers[:subclass_handlers].merge!(subclasses_handlers) - nil - end - - # @return [Proc, nil] The handler for `error_class`, if one was registered on this schema or inherited - def self.find_handler_for(schema, error_class) - handlers = schema.error_handlers[:subclass_handlers] - handler = nil - while (handlers) do - _err_class, next_handler = handlers.find { |err_class, handler| error_class <= err_class } - if next_handler - handlers = next_handler[:subclass_handlers] - handler = next_handler - else - # Don't reassign `handler` -- - # let the previous assignment carry over outside this block. - break - end - end - - # check for a handler from a parent class: - if schema.superclass.respond_to?(:error_handlers) - parent_handler = find_handler_for(schema.superclass, error_class) - end - - # If the inherited handler is more specific than the one defined here, - # use it. - # If it's a tie (or there is no parent handler), use the one defined here. - # If there's an inherited one, but not one defined here, use the inherited one. - # Otherwise, there's no handler for this error, return `nil`. - if parent_handler && handler && parent_handler[:class] < handler[:class] - parent_handler - elsif handler - handler - elsif parent_handler - parent_handler - else - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter.rb deleted file mode 100644 index f2a4a6481d2..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter.rb +++ /dev/null @@ -1,176 +0,0 @@ -# frozen_string_literal: true -require "fiber" -require "graphql/execution/interpreter/argument_value" -require "graphql/execution/interpreter/arguments" -require "graphql/execution/interpreter/arguments_cache" -require "graphql/execution/interpreter/execution_errors" -require "graphql/execution/interpreter/runtime" -require "graphql/execution/interpreter/resolve" -require "graphql/execution/interpreter/handles_raw_value" - -module GraphQL - module Execution - class Interpreter - class << self - # Used internally to signal that the query shouldn't be executed - # @api private - NO_OPERATION = GraphQL::EmptyObjects::EMPTY_HASH - - # @param schema [GraphQL::Schema] - # @param queries [Array] - # @param context [Hash] - # @param max_complexity [Integer, nil] - # @return [Array] One result per query - def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity) - queries = query_options.map do |opts| - case opts - when Hash - schema.query_class.new(schema, nil, **opts) - when GraphQL::Query - opts - else - raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})" - end - end - - - multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity) - Fiber[:__graphql_current_multiplex] = multiplex - trace = multiplex.current_trace - trace.begin_execute_multiplex(multiplex) - trace.execute_multiplex(multiplex: multiplex) do - schema = multiplex.schema - queries = multiplex.queries - lazies_at_depth = Hash.new { |h, k| h[k] = [] } - multiplex_analyzers = schema.multiplex_analyzers - if multiplex.max_complexity - multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity] - end - - trace.begin_analyze_multiplex(multiplex, multiplex_analyzers) - schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers) - trace.end_analyze_multiplex(multiplex, multiplex_analyzers) - - begin - # Since this is basically the batching context, - # share it for a whole multiplex - multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy(deprecation_warning: false).new - # Do as much eager evaluation of the query as possible - results = [] - queries.each_with_index do |query, idx| - if query.subscription? && !query.subscription_update? - subs_namespace = query.context.namespace(:subscriptions) - subs_namespace[:events] = [] - subs_namespace[:subscriptions] = {} - end - multiplex.dataloader.append_job { - operation = query.selected_operation - result = if operation.nil? || !query.valid? || !query.context.errors.empty? - NO_OPERATION - else - begin - # Although queries in a multiplex _share_ an Interpreter instance, - # they also have another item of state, which is private to that query - # in particular, assign it here: - runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth) - query.context.namespace(:interpreter_runtime)[:runtime] = runtime - - query.current_trace.execute_query(query: query) do - runtime.run_eager - end - rescue GraphQL::ExecutionError => err - query.context.errors << err - NO_OPERATION - end - end - results[idx] = result - } - end - - multiplex.dataloader.run - - # Then, work through lazy results in a breadth-first way - multiplex.dataloader.append_job { - query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil - queries = multiplex ? multiplex.queries : [query] - final_values = queries.map do |query| - runtime = query.context.namespace(:interpreter_runtime)[:runtime] - # it might not be present if the query has an error - runtime ? runtime.final_result : nil - end - final_values.compact! - multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do - Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader) - end - } - multiplex.dataloader.run - - # Then, find all errors and assign the result to the query object - results.each_with_index do |data_result, idx| - query = queries[idx] - if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty? - schema.subscriptions.write_subscription(query, events) - end - # Assign the result so that it can be accessed in instrumentation - query.result_values = if data_result.equal?(NO_OPERATION) - if !query.valid? || !query.context.errors.empty? - # A bit weird, but `Query#static_errors` _includes_ `query.context.errors` - { "errors" => query.static_errors.map(&:to_h) } - else - data_result - end - else - result = {} - - if !query.context.errors.empty? - error_result = query.context.errors.map(&:to_h) - result["errors"] = error_result - end - - result["data"] = query.context.namespace(:interpreter_runtime)[:runtime].final_result - - result - end - if query.context.namespace?(:__query_result_extensions__) - query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__) - end - # Get the Query::Result, not the Hash - results[idx] = query.result - end - - results - rescue Exception - # TODO rescue at a higher level so it will catch errors in analysis, too - # Assign values here so that the query's `@executed` becomes true - queries.map { |q| q.result_values ||= {} } - raise - ensure - Fiber[:__graphql_current_multiplex] = nil - queries.map { |query| - runtime = query.context.namespace(:interpreter_runtime)[:runtime] - if runtime - runtime.delete_all_interpreter_context - end - } - end - end - ensure - trace&.end_execute_multiplex(multiplex) - end - end - - class ListResultFailedError < GraphQL::Error - def initialize(value:, path:, field:) - message = "Failed to build a GraphQL list result for field `#{field.path}` at path `#{path.join(".")}`.\n".dup - - message << "Expected `#{value.inspect}` (#{value.class}) to implement `.each` to satisfy the GraphQL return type `#{field.type.to_type_signature}`.\n" - - if field.connection? - message << "\nThis field was treated as a Relay-style connection; add `connection: false` to the `field(...)` to disable this behavior." - end - super(message) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/argument_value.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/argument_value.rb deleted file mode 100644 index ca7845b05ba..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/argument_value.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - # A container for metadata regarding arguments present in a GraphQL query. - # @see Interpreter::Arguments#argument_values for a hash of these objects. - class ArgumentValue - def initialize(definition:, value:, original_value:, default_used:) - @definition = definition - @value = value - @original_value = original_value - @default_used = default_used - end - - # @return [Object] The Ruby-ready value for this Argument - attr_reader :value - - # @return [Object] The value of this argument _before_ `prepare` is applied. - attr_reader :original_value - - # @return [GraphQL::Schema::Argument] The definition instance for this argument - attr_reader :definition - - # @return [Boolean] `true` if the schema-defined `default_value:` was applied in this case. (No client-provided value was present.) - def default_used? - @default_used - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/arguments.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/arguments.rb deleted file mode 100644 index 4da23f25d4a..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/arguments.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - # A wrapper for argument hashes in GraphQL queries. - # - # This object is immutable so that the runtime code can be sure that - # modifications don't leak from one use to another - # - # @see GraphQL::Query#arguments_for to get access to these objects. - class Arguments - extend Forwardable - include GraphQL::Dig - - # The Ruby-style arguments hash, ready for a resolver. - # This hash is the one used at runtime. - # - # @return [Hash] - attr_reader :keyword_arguments - - # @param argument_values [nil, Hash{Symbol => ArgumentValue}] - # @param keyword_arguments [nil, Hash{Symbol => Object}] - def initialize(keyword_arguments: nil, argument_values:) - @empty = argument_values.nil? || argument_values.empty? - # This is only present when `extras` have been merged in: - if keyword_arguments - # This is a little crazy. We expect the `:argument_details` extra to _include extras_, - # but the object isn't created until _after_ extras are put together. - # So, we have to use a special flag here to say, "at the last minute, add yourself to the keyword args." - # - # Otherwise: - # - We can't access the final Arguments instance _while_ we're preparing extras - # - After we _can_ access it, it's frozen, so we can't add anything. - # - # So, this flag gives us a chance to sneak it in before freezing, _and_ while we have access - # to the new Arguments instance itself. - if keyword_arguments[:argument_details] == :__arguments_add_self - keyword_arguments[:argument_details] = self - end - @keyword_arguments = keyword_arguments.freeze - elsif !@empty - @keyword_arguments = {} - argument_values.each do |name, arg_val| - @keyword_arguments[name] = arg_val.value - end - @keyword_arguments.freeze - else - @keyword_arguments = NO_ARGS - end - @argument_values = argument_values ? argument_values.freeze : NO_ARGS - freeze - end - - # @return [Hash{Symbol => ArgumentValue}] - attr_reader :argument_values - - def empty? - @empty - end - - def_delegators :keyword_arguments, :key?, :[], :fetch, :keys, :each, :values, :size, :to_h - def_delegators :argument_values, :each_value - - def inspect - "#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>" - end - - # Create a new arguments instance which includes these extras. - # - # This is called by the runtime to implement field `extras: [...]` - # - # @param extra_args [Hash Object>] - # @return [Interpreter::Arguments] - # @api private - def merge_extras(extra_args) - self.class.new( - argument_values: argument_values, - keyword_arguments: keyword_arguments.merge(extra_args) - ) - end - - NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH - EMPTY = self.new(argument_values: nil, keyword_arguments: NO_ARGS).freeze - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/arguments_cache.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/arguments_cache.rb deleted file mode 100644 index 92cd9ab7141..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/arguments_cache.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - class ArgumentsCache - def initialize(query) - @query = query - @dataloader = query.context.dataloader - @storage = Hash.new do |h, argument_owner| - h[argument_owner] = if argument_owner.arguments_statically_coercible? - shared_values_cache = {} - Hash.new do |h2, ignored_parent_object| - h2[ignored_parent_object] = shared_values_cache - end.compare_by_identity - else - Hash.new do |h2, parent_object| - h2[parent_object] = {}.compare_by_identity - end.compare_by_identity - end - end.compare_by_identity - end - - def fetch(ast_node, argument_owner, parent_object) - # This runs eagerly if no block is given - @storage[argument_owner][parent_object][ast_node] ||= begin - args_hash = self.class.prepare_args_hash(@query, ast_node) - kwarg_arguments = argument_owner.coerce_arguments(parent_object, args_hash, @query.context) - @query.after_lazy(kwarg_arguments) do |resolved_args| - @storage[argument_owner][parent_object][ast_node] = resolved_args - end - end - - end - - # @yield [Interpreter::Arguments, Lazy] The finally-loaded arguments - def dataload_for(ast_node, argument_owner, parent_object, &block) - # First, normalize all AST or Ruby values to a plain Ruby hash - arg_storage = @storage[argument_owner][parent_object] - if (args = arg_storage[ast_node]) - yield(args) - else - args_hash = self.class.prepare_args_hash(@query, ast_node) - argument_owner.coerce_arguments(parent_object, args_hash, @query.context) do |resolved_args| - arg_storage[ast_node] = resolved_args - yield(resolved_args) - end - end - nil - end - - private - - NO_ARGUMENTS = GraphQL::EmptyObjects::EMPTY_HASH - NO_VALUE_GIVEN = NOT_CONFIGURED - - def self.prepare_args_hash(query, ast_arg_or_hash_or_value) - case ast_arg_or_hash_or_value - when Hash - if ast_arg_or_hash_or_value.empty? - return NO_ARGUMENTS - end - args_hash = {} - ast_arg_or_hash_or_value.each do |k, v| - args_hash[k] = prepare_args_hash(query, v) - end - args_hash - when Array - ast_arg_or_hash_or_value.map { |v| prepare_args_hash(query, v) } - when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InputObject, GraphQL::Language::Nodes::Directive - if ast_arg_or_hash_or_value.arguments.empty? # rubocop:disable Development/ContextIsPassedCop -- AST-related - return NO_ARGUMENTS - end - args_hash = {} - ast_arg_or_hash_or_value.arguments.each do |arg| # rubocop:disable Development/ContextIsPassedCop -- AST-related - v = prepare_args_hash(query, arg.value) - if v != NO_VALUE_GIVEN - args_hash[arg.name] = v - end - end - args_hash - when GraphQL::Language::Nodes::VariableIdentifier - if query.variables.key?(ast_arg_or_hash_or_value.name) - variable_value = query.variables[ast_arg_or_hash_or_value.name] - prepare_args_hash(query, variable_value) - else - NO_VALUE_GIVEN - end - when GraphQL::Language::Nodes::Enum - ast_arg_or_hash_or_value.name - when GraphQL::Language::Nodes::NullValue - nil - else - ast_arg_or_hash_or_value - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/execution_errors.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/execution_errors.rb deleted file mode 100644 index e85041f8bc9..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/execution_errors.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - class ExecutionErrors - def initialize(ctx, ast_node, path) - @context = ctx - @ast_node = ast_node - @path = path - end - - def add(err_or_msg) - err = case err_or_msg - when String - GraphQL::ExecutionError.new(err_or_msg) - when GraphQL::ExecutionError - err_or_msg - else - raise ArgumentError, "expected String or GraphQL::ExecutionError, not #{err_or_msg.class} (#{err_or_msg.inspect})" - end - err.ast_node ||= @ast_node - err.path ||= @path - @context.add_error(err) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/handles_raw_value.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/handles_raw_value.rb deleted file mode 100644 index 21f62eba964..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/handles_raw_value.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - # Wrapper for raw values - class RawValue - def initialize(obj = nil) - @object = obj - end - - def resolve - @object - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/resolve.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/resolve.rb deleted file mode 100644 index 99bb6748352..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/resolve.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - module Resolve - # Continue field results in `results` until there's nothing else to continue. - # @return [void] - def self.resolve_all(results, dataloader) - dataloader.append_job { resolve(results, dataloader) } - nil - end - - def self.resolve_each_depth(lazies_at_depth, dataloader) - smallest_depth = nil - lazies_at_depth.each_key do |depth_key| - smallest_depth ||= depth_key - if depth_key < smallest_depth - smallest_depth = depth_key - end - end - - if smallest_depth - lazies = lazies_at_depth.delete(smallest_depth) - if !lazies.empty? - dataloader.append_job { - lazies.each(&:value) # resolve these Lazy instances - } - # Run lazies _and_ dataloader, see if more are enqueued - dataloader.run - resolve_each_depth(lazies_at_depth, dataloader) - end - end - nil - end - - # After getting `results` back from an interpreter evaluation, - # continue it until you get a response-ready Ruby value. - # - # `results` is one level of _depth_ of a query or multiplex. - # - # Resolve all lazy values in that depth before moving on - # to the next level. - # - # It's assumed that the lazies will - # return {Lazy} instances if there's more work to be done, - # or return {Hash}/{Array} if the query should be continued. - # - # @return [void] - def self.resolve(results, dataloader) - # There might be pending jobs here that _will_ write lazies - # into the result hash. We should run them out, so we - # can be sure that all lazies will be present in the result hashes. - # A better implementation would somehow interleave (or unify) - # these approaches. - dataloader.run - next_results = [] - while !results.empty? - result_value = results.shift - if result_value.is_a?(Runtime::GraphQLResultHash) || result_value.is_a?(Hash) - results.concat(result_value.values) - next - elsif result_value.is_a?(Runtime::GraphQLResultArray) - results.concat(result_value.values) - next - elsif result_value.is_a?(Array) - results.concat(result_value) - next - elsif result_value.is_a?(Lazy) - loaded_value = result_value.value - if loaded_value.is_a?(Lazy) - # Since this field returned another lazy, - # add it to the same queue - results << loaded_value - elsif loaded_value.is_a?(Runtime::GraphQLResultHash) || loaded_value.is_a?(Runtime::GraphQLResultArray) || - loaded_value.is_a?(Hash) || loaded_value.is_a?(Array) - # Add these values in wholesale -- - # they might be modified by later work in the dataloader. - next_results << loaded_value - end - end - end - - if !next_results.empty? - # Any pending data loader jobs may populate the - # resutl arrays or result hashes accumulated in - # `next_results``. Run those **to completion** - # before continuing to resolve `next_results`. - # (Just `.append_job` doesn't work if any pending - # jobs require multiple passes.) - dataloader.run - dataloader.append_job { resolve(next_results, dataloader) } - end - - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/runtime.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/runtime.rb deleted file mode 100644 index d2dcf98ba73..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/runtime.rb +++ /dev/null @@ -1,888 +0,0 @@ -# frozen_string_literal: true -require "graphql/execution/interpreter/runtime/graphql_result" - -module GraphQL - module Execution - class Interpreter - # I think it would be even better if we could somehow make - # `continue_field` not recursive. "Trampolining" it somehow. - # - # @api private - class Runtime - class CurrentState - def initialize - @current_field = nil - @current_arguments = nil - @current_result_name = nil - @current_result = nil - @was_authorized_by_scope_items = nil - end - - def current_object - @current_result.graphql_application_value - end - - attr_accessor :current_result, :current_result_name, - :current_arguments, :current_field, :was_authorized_by_scope_items - end - - # @return [GraphQL::Query] - attr_reader :query - - # @return [Class] - attr_reader :schema - - # @return [GraphQL::Query::Context] - attr_reader :context - - def initialize(query:, lazies_at_depth:) - @query = query - @current_trace = query.current_trace - @dataloader = query.multiplex.dataloader - @lazies_at_depth = lazies_at_depth - @schema = query.schema - @context = query.context - @response = nil - # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve` - @runtime_directive_names = [] - noop_resolve_owner = GraphQL::Schema::Directive.singleton_class - @schema_directives = schema.directives - @schema_directives.each do |name, dir_defn| - if dir_defn.method(:resolve).owner != noop_resolve_owner - @runtime_directive_names << name - end - end - # { Class => Boolean } - @lazy_cache = {}.compare_by_identity - end - - def final_result - @response && @response.graphql_result_data - end - - def inspect - "#<#{self.class.name} response=#{@response.inspect}>" - end - - # This _begins_ the execution. Some deferred work - # might be stored up in lazies. - # @return [void] - def run_eager - root_operation = query.selected_operation - root_op_type = root_operation.operation_type || "query" - root_type = schema.root_type_for_operation(root_op_type) - runtime_object = root_type.wrap(query.root_value, context) - runtime_object = schema.sync_lazy(runtime_object) - is_eager = root_op_type == "mutation" - @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil) - st = get_current_runtime_state - st.current_result = @response - - if runtime_object.nil? - # Root .authorized? returned false. - @response = nil - else - call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives - each_gathered_selections(@response) do |selections, is_selection_array| - if is_selection_array - selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil) - final_response = @response - else - selection_response = @response - final_response = nil - end - - @dataloader.append_job { - evaluate_selections( - selections, - selection_response, - final_response, - nil, - ) - } - end - end - end - nil - end - - def each_gathered_selections(response_hash) - gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections) - if gathered_selections.is_a?(Array) - gathered_selections.each do |item| - yield(item, true) - end - else - yield(gathered_selections, false) - end - end - - def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {}) - selections.each do |node| - # Skip gathering this if the directive says so - if !directives_include?(node, owner_object, owner_type) - next - end - - if node.is_a?(GraphQL::Language::Nodes::Field) - response_key = node.alias || node.name - selections = selections_by_name[response_key] - # if there was already a selection of this field, - # use an array to hold all selections, - # otherwise, use the single node to represent the selection - if selections - # This field was already selected at least once, - # add this node to the list of selections - s = Array(selections) - s << node - selections_by_name[response_key] = s - else - # No selection was found for this field yet - selections_by_name[response_key] = node - end - else - # This is an InlineFragment or a FragmentSpread - if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) } - next_selections = {} - next_selections[:graphql_directives] = node.directives - if selections_to_run - selections_to_run << next_selections - else - selections_to_run = [] - selections_to_run << selections_by_name - selections_to_run << next_selections - end - else - next_selections = selections_by_name - end - - case node - when GraphQL::Language::Nodes::InlineFragment - if node.type - type_defn = query.types.type(node.type.name) - - if query.types.possible_types(type_defn).include?(owner_type) - result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections) - if !result.equal?(next_selections) - selections_to_run = result - end - end - else - # it's an untyped fragment, definitely continue - result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections) - if !result.equal?(next_selections) - selections_to_run = result - end - end - when GraphQL::Language::Nodes::FragmentSpread - fragment_def = query.fragments[node.name] - type_defn = query.types.type(fragment_def.type.name) - if query.types.possible_types(type_defn).include?(owner_type) - result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections) - if !result.equal?(next_selections) - selections_to_run = result - end - end - else - raise "Invariant: unexpected selection class: #{node.class}" - end - end - end - selections_to_run || selections_by_name - end - - NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH - - # @return [void] - def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists - runtime_state ||= get_current_runtime_state - runtime_state.current_result_name = nil - runtime_state.current_result = selections_result - # This is a less-frequent case; use a fast check since it's often not there. - if (directives = gathered_selections[:graphql_directives]) - gathered_selections.delete(:graphql_directives) - end - - call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do - finished_jobs = 0 - enqueued_jobs = gathered_selections.size - gathered_selections.each do |result_name, field_ast_nodes_or_ast_node| - - # Field resolution may pause the fiber, - # so it wouldn't get to the `Resolve` call that happens below. - # So instead trigger a run from this outer context. - if selections_result.graphql_is_eager - @dataloader.clear_cache - @dataloader.run_isolated { - evaluate_selection( - result_name, field_ast_nodes_or_ast_node, selections_result - ) - finished_jobs += 1 - if finished_jobs == enqueued_jobs - if target_result - selections_result.merge_into(target_result) - end - end - @dataloader.clear_cache - } - else - @dataloader.append_job { - evaluate_selection( - result_name, field_ast_nodes_or_ast_node, selections_result - ) - finished_jobs += 1 - if finished_jobs == enqueued_jobs - if target_result - selections_result.merge_into(target_result) - end - end - } - end - end - selections_result - end - end - - # @return [void] - def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists - return if selections_result.graphql_dead - # As a performance optimization, the hash key will be a `Node` if - # there's only one selection of the field. But if there are multiple - # selections of the field, it will be an Array of nodes - if field_ast_nodes_or_ast_node.is_a?(Array) - field_ast_nodes = field_ast_nodes_or_ast_node - ast_node = field_ast_nodes.first - else - field_ast_nodes = nil - ast_node = field_ast_nodes_or_ast_node - end - field_name = ast_node.name - owner_type = selections_result.graphql_result_type - field_defn = query.types.field(owner_type, field_name) - - # Set this before calling `run_with_directives`, so that the directive can have the latest path - runtime_state = get_current_runtime_state - runtime_state.current_field = field_defn - runtime_state.current_result = selections_result - runtime_state.current_result_name = result_name - - owner_object = selections_result.graphql_application_value - if field_defn.dynamic_introspection - owner_object = field_defn.owner.wrap(owner_object, context) - end - - if !field_defn.any_arguments? - resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY - if field_defn.extras.size == 0 - evaluate_selection_with_resolved_keyword_args( - NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state - ) - else - evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state) - end - else - @query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments| - runtime_state = get_current_runtime_state # This might be in a different fiber - evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state) - end - end - end - - def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists - after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state| - if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError) - return_type_non_null = field_defn.type.non_null? - continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result) - next - end - - kwarg_arguments = if field_defn.extras.empty? - if resolved_arguments.empty? - # We can avoid allocating the `{ Symbol => Object }` hash in this case - NO_ARGS - else - resolved_arguments.keyword_arguments - end - else - # Bundle up the extras, then make a new arguments instance - # that includes the extras, too. - extra_args = {} - field_defn.extras.each do |extra| - case extra - when :ast_node - extra_args[:ast_node] = ast_node - when :execution_errors - extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, current_path) - when :path - extra_args[:path] = current_path - when :lookahead - if !field_ast_nodes - field_ast_nodes = [ast_node] - end - - extra_args[:lookahead] = Execution::Lookahead.new( - query: query, - ast_nodes: field_ast_nodes, - field: field_defn, - ) - when :argument_details - # Use this flag to tell Interpreter::Arguments to add itself - # to the keyword args hash _before_ freezing everything. - extra_args[:argument_details] = :__arguments_add_self - when :parent - parent_result = selection_result.graphql_parent - extra_args[:parent] = parent_result&.graphql_application_value&.object - else - extra_args[extra] = field_defn.fetch_extra(extra, context) - end - end - if !extra_args.empty? - resolved_arguments = resolved_arguments.merge_extras(extra_args) - end - resolved_arguments.keyword_arguments - end - - evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) - end - end - - def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists - runtime_state.current_field = field_defn - runtime_state.current_arguments = resolved_arguments - runtime_state.current_result_name = result_name - runtime_state.current_result = selection_result - # Optimize for the case that field is selected only once - if field_ast_nodes.nil? || field_ast_nodes.size == 1 - next_selections = ast_node.selections - directives = ast_node.directives - else - next_selections = [] - directives = [] - field_ast_nodes.each { |f| - next_selections.concat(f.selections) - directives.concat(f.directives) - } - end - - field_result = call_method_on_directives(:resolve, object, directives) do - if !directives.empty? - # This might be executed in a different context; reset this info - runtime_state = get_current_runtime_state - runtime_state.current_field = field_defn - runtime_state.current_arguments = resolved_arguments - runtime_state.current_result_name = result_name - runtime_state.current_result = selection_result - end - # Actually call the field resolver and capture the result - app_result = begin - @current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query) - @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do - field_defn.resolve(object, kwarg_arguments, context) - end - rescue GraphQL::ExecutionError => err - err - rescue StandardError => err - begin - query.handle_or_reraise(err) - rescue GraphQL::ExecutionError => ex_err - ex_err - end - end - @current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result) - after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state| - owner_type = selection_result.graphql_result_type - return_type = field_defn.type - continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result) - if HALT != continue_value - was_scoped = runtime_state.was_authorized_by_scope_items - runtime_state.was_authorized_by_scope_items = nil - continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state) - else - nil - end - end - end - # If this field is a root mutation field, immediately resolve - # all of its child fields before moving on to the next root mutation field. - # (Subselections of this mutation will still be resolved level-by-level.) - if selection_result.graphql_is_eager - Interpreter::Resolve.resolve_all([field_result], @dataloader) - end - end - - def set_result(selection_result, result_name, value, is_child_result, is_non_null) - if !selection_result.graphql_dead - if value.nil? && is_non_null - # This is an invalid nil that should be propagated - # One caller of this method passes a block, - # namely when application code returns a `nil` to GraphQL and it doesn't belong there. - # The other possibility for reaching here is when a field returns an ExecutionError, so we write - # `nil` to the response, not knowing whether it's an invalid `nil` or not. - # (And in that case, we don't have to call the schema's handler, since it's not a bug in the application.) - # TODO the code is trying to tell me something. - yield if block_given? - parent = selection_result.graphql_parent - if parent.nil? # This is a top-level result hash - @response = nil - else - name_in_parent = selection_result.graphql_result_name - is_non_null_in_parent = selection_result.graphql_is_non_null_in_parent - set_result(parent, name_in_parent, nil, false, is_non_null_in_parent) - set_graphql_dead(selection_result) - end - elsif is_child_result - selection_result.set_child_result(result_name, value) - else - selection_result.set_leaf(result_name, value) - end - end - end - - # Mark this node and any already-registered children as dead, - # so that it accepts no more writes. - def set_graphql_dead(selection_result) - case selection_result - when GraphQLResultArray - selection_result.graphql_dead = true - selection_result.values.each { |v| set_graphql_dead(v) } - when GraphQLResultHash - selection_result.graphql_dead = true - selection_result.each { |k, v| set_graphql_dead(v) } - else - # It's a scalar, no way to mark it dead. - end - end - - def current_path - st = get_current_runtime_state - result = st.current_result - path = result && result.path - if path && (rn = st.current_result_name) - path = path.dup - path.push(rn) - end - path - end - - HALT = Object.new - def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists - case value - when nil - if is_non_null - set_result(selection_result, result_name, nil, false, is_non_null) do - # When this comes from a list item, use the parent object: - parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type - # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.) - err = parent_type::InvalidNullError.new(parent_type, field, value, ast_node) - schema.type_error(err, context) - end - else - set_result(selection_result, result_name, nil, false, is_non_null) - end - HALT - when GraphQL::Error - # Handle these cases inside a single `when` - # to avoid the overhead of checking three different classes - # every time. - if value.is_a?(GraphQL::ExecutionError) - if selection_result.nil? || !selection_result.graphql_dead - value.path ||= current_path - value.ast_node ||= ast_node - context.errors << value - if selection_result - set_result(selection_result, result_name, nil, false, is_non_null) - end - end - HALT - elsif value.is_a?(GraphQL::UnauthorizedFieldError) - value.field ||= field - # this hook might raise & crash, or it might return - # a replacement value - next_value = begin - schema.unauthorized_field(value) - rescue GraphQL::ExecutionError => err - err - end - continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result) - elsif value.is_a?(GraphQL::UnauthorizedError) - # this hook might raise & crash, or it might return - # a replacement value - next_value = begin - schema.unauthorized_object(value) - rescue GraphQL::ExecutionError => err - err - end - continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result) - elsif GraphQL::Execution::SKIP == value - # It's possible a lazy was already written here - case selection_result - when GraphQLResultHash - selection_result.delete(result_name) - when GraphQLResultArray - selection_result.graphql_skip_at(result_name) - when nil - # this can happen with directives - else - raise "Invariant: unexpected result class #{selection_result.class} (#{selection_result.inspect})" - end - HALT - else - # What could this actually _be_? Anyhow, - # preserve the default behavior of doing nothing with it. - value - end - when Array - # It's an array full of execution errors; add them all. - if !value.empty? && value.all?(GraphQL::ExecutionError) - list_type_at_all = (field && (field.type.list?)) - if selection_result.nil? || !selection_result.graphql_dead - value.each_with_index do |error, index| - error.ast_node ||= ast_node - error.path ||= current_path + (list_type_at_all ? [index] : []) - context.errors << error - end - if selection_result - if list_type_at_all - result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v } - set_result(selection_result, result_name, result_without_errors, false, is_non_null) - else - set_result(selection_result, result_name, nil, false, is_non_null) - end - end - end - HALT - else - value - end - when GraphQL::Execution::Interpreter::RawValue - # Write raw value directly to the response without resolving nested objects - set_result(selection_result, result_name, value.resolve, false, is_non_null) - HALT - else - value - end - end - - # The resolver for `field` returned `value`. Continue to execute the query, - # treating `value` as `type` (probably the return type of the field). - # - # Use `next_selections` to resolve object fields, if there are any. - # - # Location information from `path` and `ast_node`. - # - # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later - def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists - if current_type.non_null? - current_type = current_type.of_type - is_non_null = true - end - - case current_type.kind.name - when "SCALAR", "ENUM" - r = begin - current_type.coerce_result(value, context) - rescue StandardError => err - query.handle_or_reraise(err) - end - set_result(selection_result, result_name, r, false, is_non_null) - r - when "UNION", "INTERFACE" - resolved_type_or_lazy = resolve_type(current_type, value) - after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state| - if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2 - resolved_type, resolved_value = resolved_type_result - else - resolved_type = resolved_type_result - resolved_value = value - end - - possible_types = query.types.possible_types(current_type) - if !possible_types.include?(resolved_type) - parent_type = field.owner_type - err_class = current_type::UnresolvedTypeError - type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types) - schema.type_error(type_error, context) - set_result(selection_result, result_name, nil, false, is_non_null) - nil - else - continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state) - end - end - when "OBJECT" - object_proxy = begin - was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context) - rescue GraphQL::ExecutionError => err - err - end - after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state| - continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result) - if HALT != continue_value - response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field) - set_result(selection_result, result_name, response_hash, true, is_non_null) - each_gathered_selections(response_hash) do |selections, is_selection_array| - if is_selection_array - this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field) - final_result = response_hash - else - this_result = response_hash - final_result = nil - end - - evaluate_selections( - selections, - this_result, - final_result, - runtime_state, - ) - end - end - end - when "LIST" - inner_type = current_type.of_type - # This is true for objects, unions, and interfaces - use_dataloader_job = !inner_type.unwrap.kind.input? - inner_type_non_null = inner_type.non_null? - response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field) - set_result(selection_result, result_name, response_list, true, is_non_null) - idx = nil - list_value = begin - begin - value.each do |inner_value| - idx ||= 0 - this_idx = idx - idx += 1 - if use_dataloader_job - @dataloader.append_job do - resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) - end - else - resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) - end - end - - response_list - rescue NoMethodError => err - # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.) - if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true) - # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug. - raise ListResultFailedError.new(value: value, field: field, path: current_path) - else - # This was some other NoMethodError -- let it bubble to reveal the real error. - raise - end - rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err - ex_err - rescue StandardError => err - begin - query.handle_or_reraise(err) - rescue GraphQL::ExecutionError => ex_err - ex_err - end - end - rescue StandardError => err - begin - query.handle_or_reraise(err) - rescue GraphQL::ExecutionError => ex_err - ex_err - end - end - # Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set) - error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null? - continue_value(list_value, field, error_is_non_null, ast_node, result_name, selection_result) - else - raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})" - end - end - - def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists - runtime_state.current_result_name = this_idx - runtime_state.current_result = response_list - call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do - # This will update `response_list` with the lazy - after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state| - continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list) - if HALT != continue_value - continue_field(continue_value, owner_type, field, inner_type, ast_node, response_list.graphql_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state) - end - end - end - end - - def call_method_on_directives(method_name, object, directives, &block) - return yield if directives.nil? || directives.empty? - run_directive(method_name, object, directives, 0, &block) - end - - def run_directive(method_name, object, directives, idx, &block) - dir_node = directives[idx] - if !dir_node - yield - else - dir_defn = @schema_directives.fetch(dir_node.name) - raw_dir_args = arguments(nil, dir_defn, dir_node) - dir_args = continue_value( - raw_dir_args, # value - nil, # field - false, # is_non_null - dir_node, # ast_node - nil, # result_name - nil, # selection_result - ) - - if dir_args == HALT - nil - else - dir_defn.public_send(method_name, object, dir_args, context) do - run_directive(method_name, object, directives, idx + 1, &block) - end - end - end - end - - # Check {Schema::Directive.include?} for each directive that's present - def directives_include?(node, graphql_object, parent_type) - node.directives.each do |dir_node| - dir_defn = @schema_directives.fetch(dir_node.name) - args = arguments(graphql_object, dir_defn, dir_node) - if !dir_defn.include?(graphql_object, args, context) - return false - end - end - true - end - - def get_current_runtime_state - current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity - current_state[@query] ||= CurrentState.new - end - - def minimal_after_lazy(value, &block) - if lazy?(value) - GraphQL::Execution::Lazy.new do - result = @schema.sync_lazy(value) - # The returned result might also be lazy, so check it, too - minimal_after_lazy(result, &block) - end - else - yield(value) - end - end - - # @param obj [Object] Some user-returned value that may want to be batched - # @param field [GraphQL::Schema::Field] - # @param eager [Boolean] Set to `true` for mutation root fields only - # @param trace [Boolean] If `false`, don't wrap this with field tracing - # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it. - def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block) - if lazy?(lazy_obj) - orig_result = result - was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items - lazy = GraphQL::Execution::Lazy.new(field: field) do - # This block might be called in a new fiber; - # In that case, this will initialize a new state - # to avoid conflicting with the parent fiber. - runtime_state = get_current_runtime_state - runtime_state.current_field = field - runtime_state.current_arguments = arguments - runtime_state.current_result_name = result_name - runtime_state.current_result = orig_result - runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items - # Wrap the execution of _this_ method with tracing, - # but don't wrap the continuation below - result = nil - inner_obj = begin - result = if trace - @current_trace.begin_execute_field(field, owner_object, arguments, query) - @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do - schema.sync_lazy(lazy_obj) - end - else - schema.sync_lazy(lazy_obj) - end - rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err - ex_err - rescue StandardError => err - begin - query.handle_or_reraise(err) - rescue GraphQL::ExecutionError => ex_err - ex_err - end - ensure - if trace - @current_trace.end_execute_field(field, owner_object, arguments, query, result) - end - end - yield(inner_obj, runtime_state) - end - - if eager - lazy.value - else - set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here - current_depth = 0 - while result - current_depth += 1 - result = result.graphql_parent - end - @lazies_at_depth[current_depth] << lazy - lazy - end - else - # Don't need to reset state here because it _wasn't_ lazy. - yield(lazy_obj, runtime_state) - end - end - - def arguments(graphql_object, arg_owner, ast_node) - if arg_owner.arguments_statically_coercible? - query.arguments_for(ast_node, arg_owner) - else - # The arguments must be prepared in the context of the given object - query.arguments_for(ast_node, arg_owner, parent_object: graphql_object) - end - end - - def delete_all_interpreter_context - per_query_state = Fiber[:__graphql_runtime_info] - if per_query_state - per_query_state.delete(@query) - if per_query_state.size == 0 - Fiber[:__graphql_runtime_info] = nil - end - end - nil - end - - def resolve_type(type, value) - @current_trace.begin_resolve_type(type, value, context) - resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do - query.resolve_type(type, value) - end - @current_trace.end_resolve_type(type, value, context, resolved_type) - - if lazy?(resolved_type) - GraphQL::Execution::Lazy.new do - @current_trace.begin_resolve_type(type, value, context) - @current_trace.resolve_type_lazy(query: query, type: type, object: value) do - rt = schema.sync_lazy(resolved_type) - @current_trace.end_resolve_type(type, value, context, rt) - rt - end - end - else - [resolved_type, resolved_value] - end - end - - def lazy?(object) - obj_class = object.class - is_lazy = @lazy_cache[obj_class] - if is_lazy.nil? - is_lazy = @lazy_cache[obj_class] = @schema.lazy?(object) - end - is_lazy - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/interpreter/runtime/graphql_result.rb b/vendor/gems/graphql/lib/graphql/execution/interpreter/runtime/graphql_result.rb deleted file mode 100644 index d6fa4d8f175..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/interpreter/runtime/graphql_result.rb +++ /dev/null @@ -1,182 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Execution - class Interpreter - class Runtime - module GraphQLResult - def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager, ast_node, graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists - @ast_node = ast_node - @graphql_arguments = graphql_arguments - @graphql_field = graphql_field - @graphql_parent = parent_result - @graphql_application_value = application_value - @graphql_result_type = result_type - if parent_result && parent_result.graphql_dead - @graphql_dead = true - end - @graphql_result_name = result_name - @graphql_is_non_null_in_parent = is_non_null_in_parent - # Jump through some hoops to avoid creating this duplicate storage if at all possible. - @graphql_metadata = nil - @graphql_selections = selections - @graphql_is_eager = is_eager - end - - def path - @path ||= build_path([]) - end - - def build_path(path_array) - graphql_result_name && path_array.unshift(graphql_result_name) - @graphql_parent ? @graphql_parent.build_path(path_array) : path_array - end - - attr_accessor :graphql_dead - attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent, - :graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field - - # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects) - attr_accessor :graphql_result_data - end - - class GraphQLResultHash - def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists - super - @graphql_result_data = {} - end - - include GraphQLResult - - attr_accessor :graphql_merged_into - - def set_leaf(key, value) - # This is a hack. - # Basically, this object is merged into the root-level result at some point. - # But the problem is, some lazies are created whose closures retain reference to _this_ - # object. When those lazies are resolved, they cause an update to this object. - # - # In order to return a proper top-level result, we have to update that top-level result object. - # In order to return a proper partial result (eg, for a directive), we have to update this object, too. - # Yowza. - if (t = @graphql_merged_into) - t.set_leaf(key, value) - end - - @graphql_result_data[key] = value - # keep this up-to-date if it's been initialized - @graphql_metadata && @graphql_metadata[key] = value - - value - end - - def set_child_result(key, value) - if (t = @graphql_merged_into) - t.set_child_result(key, value) - end - @graphql_result_data[key] = value.graphql_result_data - # If we encounter some part of this response that requires metadata tracking, - # then create the metadata hash if necessary. It will be kept up-to-date after this. - (@graphql_metadata ||= @graphql_result_data.dup)[key] = value - value - end - - def delete(key) - @graphql_metadata && @graphql_metadata.delete(key) - @graphql_result_data.delete(key) - end - - def each - (@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) } - end - - def values - (@graphql_metadata || @graphql_result_data).values - end - - def key?(k) - @graphql_result_data.key?(k) - end - - def [](k) - (@graphql_metadata || @graphql_result_data)[k] - end - - def merge_into(into_result) - self.each do |key, value| - case value - when GraphQLResultHash - next_into = into_result[key] - if next_into - value.merge_into(next_into) - else - into_result.set_child_result(key, value) - end - when GraphQLResultArray - # There's no special handling of arrays because currently, there's no way to split the execution - # of a list over several concurrent flows. - into_result.set_child_result(key, value) - else - # We have to assume that, since this passed the `fields_will_merge` selection, - # that the old and new values are the same. - into_result.set_leaf(key, value) - end - end - @graphql_merged_into = into_result - end - end - - class GraphQLResultArray - include GraphQLResult - - def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists - super - @graphql_result_data = [] - end - - def graphql_skip_at(index) - # Mark this index as dead. It's tricky because some indices may already be storing - # `Lazy`s. So the runtime is still holding indexes _before_ skipping, - # this object has to coordinate incoming writes to account for any already-skipped indices. - @skip_indices ||= [] - @skip_indices << index - offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index} - delete_at_index = index - offset_by - @graphql_metadata && @graphql_metadata.delete_at(delete_at_index) - @graphql_result_data.delete_at(delete_at_index) - end - - def set_leaf(idx, value) - if @skip_indices - offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx } - idx -= offset_by - end - @graphql_result_data[idx] = value - @graphql_metadata && @graphql_metadata[idx] = value - value - end - - def set_child_result(idx, value) - if @skip_indices - offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx } - idx -= offset_by - end - @graphql_result_data[idx] = value.graphql_result_data - # If we encounter some part of this response that requires metadata tracking, - # then create the metadata hash if necessary. It will be kept up-to-date after this. - (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value - value - end - - def values - (@graphql_metadata || @graphql_result_data) - end - - def [](idx) - (@graphql_metadata || @graphql_result_data)[idx] - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/lazy.rb b/vendor/gems/graphql/lib/graphql/execution/lazy.rb deleted file mode 100644 index 41849f5f503..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/lazy.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true -require "graphql/execution/lazy/lazy_method_map" - -module GraphQL - module Execution - # This wraps a value which is available, but not yet calculated, like a promise or future. - # - # Calling `#value` will trigger calculation & return the "lazy" value. - # - # This is an itty-bitty promise-like object, with key differences: - # - It has only two states, not-resolved and resolved - # - It has no error-catching functionality - # @api private - class Lazy - attr_reader :field - - # Create a {Lazy} which will get its inner value by calling the block - # @param field [GraphQL::Schema::Field] - # @param get_value_func [Proc] a block to get the inner value (later) - def initialize(field: nil, &get_value_func) - @get_value_func = get_value_func - @resolved = false - @field = field - end - - # @return [Object] The wrapped value, calling the lazy block if necessary - def value - if !@resolved - @resolved = true - v = @get_value_func.call - if v.is_a?(Lazy) - v = v.value - end - @value = v - end - - # `SKIP` was made into a subclass of `GraphQL::Error` to improve runtime performance - # (fewer clauses in a hot `case` block), but now it requires special handling here. - # I think it's still worth it for the performance win, but if the number of special - # cases grows, then maybe it's worth rethinking somehow. - if @value.is_a?(StandardError) && @value != GraphQL::Execution::SKIP - raise @value - else - @value - end - end - - # @return [Lazy] A {Lazy} whose value depends on another {Lazy}, plus any transformations in `block` - def then - self.class.new { - yield(value) - } - end - - # @param lazies [Array] Maybe-lazy objects - # @return [Lazy] A lazy which will sync all of `lazies` - def self.all(lazies) - self.new { - lazies.map { |l| l.is_a?(Lazy) ? l.value : l } - } - end - - # This can be used for fields which _had no_ lazy results - # @api private - NullResult = Lazy.new(){} - NullResult.value - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/lazy/lazy_method_map.rb b/vendor/gems/graphql/lib/graphql/execution/lazy/lazy_method_map.rb deleted file mode 100644 index ffc9b3c882d..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/lazy/lazy_method_map.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true -require 'thread' -begin - require 'concurrent' -rescue LoadError - # no problem, we'll fallback to our own map -end - -module GraphQL - module Execution - class Lazy - # {GraphQL::Schema} uses this to match returned values to lazy resolution methods. - # Methods may be registered for classes, they apply to its subclasses also. - # The result of this lookup is cached for future resolutions. - # Instances of this class are thread-safe. - # @api private - # @see {Schema#lazy?} looks up values from this map - class LazyMethodMap - def initialize(use_concurrent: defined?(Concurrent::Map)) - @storage = use_concurrent ? Concurrent::Map.new : ConcurrentishMap.new - end - - def initialize_copy(other) - @storage = other.storage.dup - end - - # @param lazy_class [Class] A class which represents a lazy value (subclasses may also be used) - # @param lazy_value_method [Symbol] The method to call on this class to get its value - def set(lazy_class, lazy_value_method) - @storage[lazy_class] = lazy_value_method - end - - # @param value [Object] an object which may have a `lazy_value_method` registered for its class or superclasses - # @return [Symbol, nil] The `lazy_value_method` for this object, or nil - def get(value) - @storage.compute_if_absent(value.class) { find_superclass_method(value.class) } - end - - def each - @storage.each_pair { |k, v| yield(k, v) } - end - - protected - - attr_reader :storage - - private - - def find_superclass_method(value_class) - @storage.each_pair { |lazy_class, lazy_value_method| - return lazy_value_method if value_class < lazy_class - } - nil - end - - # Mock the Concurrent::Map API - class ConcurrentishMap - extend Forwardable - # Technically this should be under the mutex too, - # but I know it's only used when the lock is already acquired. - def_delegators :@storage, :each_pair, :size - - def initialize - @semaphore = Mutex.new - # Access to this hash must always be managed by the mutex - # since it may be modified at runtime - @storage = {} - end - - def []=(key, value) - @semaphore.synchronize { - @storage[key] = value - } - end - - def compute_if_absent(key) - @semaphore.synchronize { - @storage.fetch(key) { @storage[key] = yield } - } - end - - def initialize_copy(other) - @semaphore = Mutex.new - @storage = other.copy_storage - end - - protected - - def copy_storage - @semaphore.synchronize { - @storage.dup - } - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/lookahead.rb b/vendor/gems/graphql/lib/graphql/execution/lookahead.rb deleted file mode 100644 index 61aa94aae5e..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/lookahead.rb +++ /dev/null @@ -1,385 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Execution - # Lookahead creates a uniform interface to inspect the forthcoming selections. - # - # It assumes that the AST it's working with is valid. (So, it's safe to use - # during execution, but if you're using it directly, be sure to validate first.) - # - # A field may get access to its lookahead by adding `extras: [:lookahead]` - # to its configuration. - # - # @example looking ahead in a field - # field :articles, [Types::Article], null: false, - # extras: [:lookahead] - # - # # For example, imagine a faster database call - # # may be issued when only some fields are requested. - # # - # # Imagine that _full_ fetch must be made to satisfy `fullContent`, - # # we can look ahead to see if we need that field. If we do, - # # we make the expensive database call instead of the cheap one. - # def articles(lookahead:) - # if lookahead.selects?(:full_content) - # fetch_full_articles(object) - # else - # fetch_preview_articles(object) - # end - # end - class Lookahead - # @param query [GraphQL::Query] - # @param ast_nodes [Array, Array] - # @param field [GraphQL::Schema::Field] if `ast_nodes` are fields, this is the field definition matching those nodes - # @param root_type [Class] if `ast_nodes` are operation definition, this is the root type for that operation - def initialize(query:, ast_nodes:, field: nil, root_type: nil, owner_type: nil) - @ast_nodes = ast_nodes.freeze - @field = field - @root_type = root_type - @query = query - @selected_type = @field ? @field.type.unwrap : root_type - @owner_type = owner_type - end - - # @return [Array] - attr_reader :ast_nodes - - # @return [GraphQL::Schema::Field] - attr_reader :field - - # @return [GraphQL::Schema::Object, GraphQL::Schema::Union, GraphQL::Schema::Interface] - attr_reader :owner_type - - # @return [Hash] - def arguments - if defined?(@arguments) - @arguments - else - @arguments = if @field - @query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args| - case args - when Execution::Interpreter::Arguments - args.keyword_arguments - when GraphQL::ExecutionError - EmptyObjects::EMPTY_HASH - else - args - end - end - else - nil - end - end - end - - # True if this node has a selection on `field_name`. - # If `field_name` is a String, it is treated as a GraphQL-style (camelized) - # field name and used verbatim. If `field_name` is a Symbol, it is - # treated as a Ruby-style (underscored) name and camelized before comparing. - # - # If `arguments:` is provided, each provided key/value will be matched - # against the arguments in the next selection. This method will return false - # if any of the given `arguments:` are not present and matching in the next selection. - # (But, the next selection may contain _more_ than the given arguments.) - # @param field_name [String, Symbol] - # @param arguments [Hash] Arguments which must match in the selection - # @return [Boolean] - def selects?(field_name, selected_type: @selected_type, arguments: nil) - selection(field_name, selected_type: selected_type, arguments: arguments).selected? - end - - # True if this node has a selection with alias matching `alias_name`. - # If `alias_name` is a String, it is treated as a GraphQL-style (camelized) - # field name and used verbatim. If `alias_name` is a Symbol, it is - # treated as a Ruby-style (underscored) name and camelized before comparing. - # - # If `arguments:` is provided, each provided key/value will be matched - # against the arguments in the next selection. This method will return false - # if any of the given `arguments:` are not present and matching in the next selection. - # (But, the next selection may contain _more_ than the given arguments.) - # @param alias_name [String, Symbol] - # @param arguments [Hash] Arguments which must match in the selection - # @return [Boolean] - def selects_alias?(alias_name, arguments: nil) - alias_selection(alias_name, arguments: arguments).selected? - end - - # @return [Boolean] True if this lookahead represents a field that was requested - def selected? - true - end - - # Like {#selects?}, but can be used for chaining. - # It returns a null object (check with {#selected?}) - # @param field_name [String, Symbol] - # @return [GraphQL::Execution::Lookahead] - def selection(field_name, selected_type: @selected_type, arguments: nil) - next_field_defn = case field_name - when String - @query.types.field(selected_type, field_name) - when Symbol - # Try to avoid the `.to_s` below, if possible - all_fields = if selected_type.kind.fields? - @query.types.fields(selected_type) - else - # Handle unions by checking possible - @query.types - .possible_types(selected_type) - .map { |t| @query.types.fields(t) } - .tap(&:flatten!) - end - - - if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name }) - match_by_orig_name - else - # Symbol#name is only present on 3.0+ - sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s - guessed_name = Schema::Member::BuildType.camelize(sym_s) - @query.types.field(selected_type, guessed_name) - end - end - lookahead_for_selection(next_field_defn, selected_type, arguments) - end - - # Like {#selection}, but for aliases. - # It returns a null object (check with {#selected?}) - # @return [GraphQL::Execution::Lookahead] - def alias_selection(alias_name, selected_type: @selected_type, arguments: nil) - alias_cache_key = [alias_name, arguments] - return alias_selections[key] if alias_selections.key?(alias_name) - - alias_node = lookup_alias_node(ast_nodes, alias_name) - return NULL_LOOKAHEAD unless alias_node - - next_field_defn = @query.types.field(selected_type, alias_node.name) - - alias_arguments = @query.arguments_for(alias_node, next_field_defn) - if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments) - alias_arguments = alias_arguments.keyword_arguments - end - - return NULL_LOOKAHEAD if arguments && arguments != alias_arguments - - alias_selections[alias_cache_key] = lookahead_for_selection(next_field_defn, selected_type, alias_arguments, alias_name) - end - - # Like {#selection}, but for all nodes. - # It returns a list of Lookaheads for all Selections - # - # If `arguments:` is provided, each provided key/value will be matched - # against the arguments in each selection. This method will filter the selections - # if any of the given `arguments:` do not match the given selection. - # - # @example getting the name of a selection - # def articles(lookahead:) - # next_lookaheads = lookahead.selections # => [#, ...] - # next_lookaheads.map(&:name) #=> [:full_content, :title] - # end - # - # @param arguments [Hash] Arguments which must match in the selection - # @return [Array] - def selections(arguments: nil) - subselections_by_type = {} - subselections_on_type = subselections_by_type[@selected_type] = {} - - @ast_nodes.each do |node| - find_selections(subselections_by_type, subselections_on_type, @selected_type, node.selections, arguments) - end - - subselections = [] - - subselections_by_type.each do |type, ast_nodes_by_response_key| - ast_nodes_by_response_key.each do |response_key, ast_nodes| - field_defn = @query.types.field(type, ast_nodes.first.name) - lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type) - subselections.push(lookahead) - end - end - - subselections - end - - # The method name of the field. - # It returns the method_sym of the Lookahead's field. - # - # @example getting the name of a selection - # def articles(lookahead:) - # article.selection(:full_content).name # => :full_content - # # ... - # end - # - # @return [Symbol] - def name - @field && @field.original_name - end - - def inspect - "#" - end - - # This is returned for {Lookahead#selection} when a non-existent field is passed - class NullLookahead < Lookahead - # No inputs required here. - def initialize - end - - def selected? - false - end - - def selects?(*) - false - end - - def selection(*) - NULL_LOOKAHEAD - end - - def selections(*) - [] - end - - def inspect - "#" - end - end - - # A singleton, so that misses don't come with overhead. - NULL_LOOKAHEAD = NullLookahead.new - - private - - def skipped_by_directive?(ast_selection) - ast_selection.directives.each do |directive| - dir_defn = @query.schema.directives.fetch(directive.name) - directive_class = dir_defn - if directive_class - dir_args = @query.arguments_for(directive, dir_defn) - return true unless directive_class.static_include?(dir_args, @query.context) - end - end - false - end - - def find_selections(subselections_by_type, selections_on_type, selected_type, ast_selections, arguments) - ast_selections.each do |ast_selection| - next if skipped_by_directive?(ast_selection) - - case ast_selection - when GraphQL::Language::Nodes::Field - response_key = ast_selection.alias || ast_selection.name - if selections_on_type.key?(response_key) - selections_on_type[response_key] << ast_selection - elsif arguments.nil? || arguments.empty? - selections_on_type[response_key] = [ast_selection] - else - field_defn = @query.types.field(selected_type, ast_selection.name) - if arguments_match?(arguments, field_defn, ast_selection) - selections_on_type[response_key] = [ast_selection] - end - end - when GraphQL::Language::Nodes::InlineFragment - on_type = selected_type - subselections_on_type = selections_on_type - if (t = ast_selection.type) - # Assuming this is valid, that `t` will be found. - on_type = @query.types.type(t.name) - subselections_on_type = subselections_by_type[on_type] ||= {} - end - find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments) - when GraphQL::Language::Nodes::FragmentSpread - frag_defn = lookup_fragment(ast_selection) - # Again, assuming a valid AST - on_type = @query.types.type(frag_defn.type.name) - subselections_on_type = subselections_by_type[on_type] ||= {} - find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments) - else - raise "Invariant: Unexpected selection type: #{ast_selection.class}" - end - end - end - - # If a selection on `node` matches `field_name` (which is backed by `field_defn`) - # and matches the `arguments:` constraints, then add that node to `matches` - def find_selected_nodes(node, field_name, field_defn, arguments:, matches:, alias_name: NOT_CONFIGURED) - return if skipped_by_directive?(node) - case node - when GraphQL::Language::Nodes::Field - if node.name == field_name && (NOT_CONFIGURED.equal?(alias_name) || node.alias == alias_name) - if arguments.nil? || arguments.empty? - # No constraint applied - matches << node - elsif arguments_match?(arguments, field_defn, node) - matches << node - end - end - when GraphQL::Language::Nodes::InlineFragment - node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) } - when GraphQL::Language::Nodes::FragmentSpread - frag_defn = lookup_fragment(node) - frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) } - else - raise "Unexpected selection comparison on #{node.class.name} (#{node})" - end - end - - def arguments_match?(arguments, field_defn, field_node) - query_kwargs = @query.arguments_for(field_node, field_defn) - arguments.all? do |arg_name, arg_value| - arg_name_sym = if arg_name.is_a?(String) - Schema::Member::BuildType.underscore(arg_name).to_sym - else - arg_name - end - - # Make sure the constraint is present with a matching value - query_kwargs.key?(arg_name_sym) && query_kwargs[arg_name_sym] == arg_value - end - end - - def lookahead_for_selection(field_defn, selected_type, arguments, alias_name = NOT_CONFIGURED) - return NULL_LOOKAHEAD unless field_defn - - next_nodes = [] - field_name = field_defn.name - @ast_nodes.each do |ast_node| - ast_node.selections.each do |selection| - find_selected_nodes(selection, field_name, field_defn, arguments: arguments, matches: next_nodes, alias_name: alias_name) - end - end - - return NULL_LOOKAHEAD if next_nodes.empty? - - Lookahead.new(query: @query, ast_nodes: next_nodes, field: field_defn, owner_type: selected_type) - end - - def alias_selections - return @alias_selections if defined?(@alias_selections) - @alias_selections ||= {} - end - - def lookup_alias_node(nodes, name) - return if nodes.empty? - - nodes.flat_map(&:children) - .flat_map { |child| unwrap_fragments(child) } - .find { |child| child.is_a?(GraphQL::Language::Nodes::Field) && child.alias == name } - end - - def unwrap_fragments(node) - case node - when GraphQL::Language::Nodes::InlineFragment - node.children - when GraphQL::Language::Nodes::FragmentSpread - lookup_fragment(node).children - else - [node] - end - end - - def lookup_fragment(ast_selection) - @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})") - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution/multiplex.rb b/vendor/gems/graphql/lib/graphql/execution/multiplex.rb deleted file mode 100644 index 582d379f1f3..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution/multiplex.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Execution - # Execute multiple queries under the same multiplex "umbrella". - # They can share a batching context and reduce redundant database hits. - # - # The flow is: - # - # - Multiplex instrumentation setup - # - Query instrumentation setup - # - Analyze the multiplex + each query - # - Begin each query - # - Resolve lazy values, breadth-first across all queries - # - Finish each query (eg, get errors) - # - Query instrumentation teardown - # - Multiplex instrumentation teardown - # - # If one query raises an application error, all queries will be in undefined states. - # - # Validation errors and {GraphQL::ExecutionError}s are handled in isolation: - # one of these errors in one query will not affect the other queries. - # - # @see {Schema#multiplex} for public API - # @api private - class Multiplex - include Tracing::Traceable - - attr_reader :context, :queries, :schema, :max_complexity, :dataloader, :current_trace - - def initialize(schema:, queries:, context:, max_complexity:) - @schema = schema - @queries = queries - @queries.each { |q| q.multiplex = self } - @context = context - @current_trace = @context[:trace] || schema.new_trace(multiplex: self) - @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new - @tracers = schema.tracers + (context[:tracers] || []) - @max_complexity = max_complexity - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/execution_error.rb b/vendor/gems/graphql/lib/graphql/execution_error.rb deleted file mode 100644 index 96a123bf6fe..00000000000 --- a/vendor/gems/graphql/lib/graphql/execution_error.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # If a field's resolve function returns a {ExecutionError}, - # the error will be inserted into the response's `"errors"` key - # and the field will resolve to `nil`. - class ExecutionError < GraphQL::Error - # @return [GraphQL::Language::Nodes::Field] the field where the error occurred - attr_accessor :ast_node - - # @return [String] an array describing the JSON-path into the execution - # response which corresponds to this error. - attr_accessor :path - - # @return [Hash] Optional data for error objects - # @deprecated Use `extensions` instead of `options`. The GraphQL spec - # recommends that any custom entries in an error be under the - # `extensions` key. - attr_accessor :options - - # @return [Hash] Optional custom data for error objects which will be added - # under the `extensions` key. - attr_accessor :extensions - - def initialize(message, ast_node: nil, options: nil, extensions: nil) - @ast_node = ast_node - @options = options - @extensions = extensions - super(message) - end - - # @return [Hash] An entry for the response's "errors" key - def to_h - hash = { - "message" => message, - } - if ast_node - hash["locations"] = [ - { - "line" => ast_node.line, - "column" => ast_node.col, - } - ] - end - if path - hash["path"] = path - end - if options - hash.merge!(options) - end - if extensions - hash["extensions"] = extensions.each_with_object({}) { |(key, value), ext| - ext[key.to_s] = value - } - end - hash - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/integer_decoding_error.rb b/vendor/gems/graphql/lib/graphql/integer_decoding_error.rb deleted file mode 100644 index 30a5ae823c4..00000000000 --- a/vendor/gems/graphql/lib/graphql/integer_decoding_error.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # This error is raised when `Types::Int` is given an input value outside of 32-bit integer range. - # - # For really big integer values, consider `GraphQL::Types::BigInt` - # - # @see GraphQL::Types::Int which raises this error - class IntegerDecodingError < GraphQL::RuntimeTypeError - # The value which couldn't be decoded - attr_reader :integer_value - - def initialize(value) - @integer_value = value - super("Integer out of bounds: #{value}. \nConsider using GraphQL::Types::BigInt instead.") - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/integer_encoding_error.rb b/vendor/gems/graphql/lib/graphql/integer_encoding_error.rb deleted file mode 100644 index 5de3b52b34f..00000000000 --- a/vendor/gems/graphql/lib/graphql/integer_encoding_error.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # This error is raised when `Types::Int` is asked to return a value outside of 32-bit integer range. - # - # For values outside that range, consider: - # - # - `ID` for database primary keys or other identifiers - # - `GraphQL::Types::BigInt` for really big integer values - # - # @see GraphQL::Types::Int which raises this error - class IntegerEncodingError < GraphQL::RuntimeTypeError - # The value which couldn't be encoded - attr_reader :integer_value - - # @return [GraphQL::Schema::Field] The field that returned a too-big integer - attr_reader :field - - # @return [Array] Where the field appeared in the GraphQL response - attr_reader :path - - def initialize(value, context:) - @integer_value = value - @field = context[:current_field] - @path = context[:current_path] - message = "Integer out of bounds: #{value}".dup - if @path - message << " @ #{@path.join(".")}" - end - if @field - message << " (#{@field.path})" - end - message << ". Consider using ID or GraphQL::Types::BigInt instead." - super(message) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection.rb b/vendor/gems/graphql/lib/graphql/introspection.rb deleted file mode 100644 index 54d64d676ff..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false) - # The introspection query to end all introspection queries, copied from - # https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js - <<-QUERY.gsub(/\n{2,}/, "\n") -query IntrospectionQuery { - __schema { - #{include_schema_description ? "description" : ""} - queryType { name } - mutationType { name } - subscriptionType { name } - types { - ...FullType - } - directives { - name - description - locations - #{include_is_repeatable ? "isRepeatable" : ""} - args#{include_deprecated_args ? '(includeDeprecated: true)' : ''} { - ...InputValue - } - } - } -} -fragment FullType on __Type { - kind - name - description - #{include_specified_by_url ? "specifiedByURL" : ""} - #{include_is_one_of ? "isOneOf" : ""} - fields(includeDeprecated: true) { - name - description - args#{include_deprecated_args ? '(includeDeprecated: true)' : ''} { - ...InputValue - } - type { - ...TypeRef - } - isDeprecated - deprecationReason - } - inputFields#{include_deprecated_args ? '(includeDeprecated: true)' : ''} { - ...InputValue - } - interfaces { - ...TypeRef - } - enumValues(includeDeprecated: true) { - name - description - isDeprecated - deprecationReason - } - possibleTypes { - ...TypeRef - } -} -fragment InputValue on __InputValue { - name - description - type { ...TypeRef } - defaultValue - #{include_deprecated_args ? 'isDeprecated' : ''} - #{include_deprecated_args ? 'deprecationReason' : ''} -} -fragment TypeRef on __Type { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - } - } - } - } - } - } - } -} - QUERY - end - end -end - -require "graphql/introspection/base_object" -require "graphql/introspection/input_value_type" -require "graphql/introspection/enum_value_type" -require "graphql/introspection/type_kind_enum" -require "graphql/introspection/type_type" -require "graphql/introspection/field_type" -require "graphql/introspection/directive_location_enum" -require "graphql/introspection/directive_type" -require "graphql/introspection/schema_type" -require "graphql/introspection/introspection_query" -require "graphql/introspection/dynamic_fields" -require "graphql/introspection/entry_points" diff --git a/vendor/gems/graphql/lib/graphql/introspection/base_object.rb b/vendor/gems/graphql/lib/graphql/introspection/base_object.rb deleted file mode 100644 index e06d2e45710..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/base_object.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class BaseObject < GraphQL::Schema::Object - introspection(true) - - def self.field(*args, **kwargs, &block) - kwargs[:introspection] = true - super(*args, **kwargs, &block) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/directive_location_enum.rb b/vendor/gems/graphql/lib/graphql/introspection/directive_location_enum.rb deleted file mode 100644 index 4fe69f53cad..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/directive_location_enum.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class DirectiveLocationEnum < GraphQL::Schema::Enum - graphql_name "__DirectiveLocation" - description "A Directive can be adjacent to many parts of the GraphQL language, "\ - "a __DirectiveLocation describes one such possible adjacencies." - - GraphQL::Schema::Directive::LOCATIONS.each do |location| - value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false) - end - introspection true - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/directive_type.rb b/vendor/gems/graphql/lib/graphql/introspection/directive_type.rb deleted file mode 100644 index 72ee132aa8a..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/directive_type.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class DirectiveType < Introspection::BaseObject - graphql_name "__Directive" - description "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document."\ - "\n\n"\ - "In some cases, you need to provide options to alter GraphQL's execution behavior "\ - "in ways field arguments will not suffice, such as conditionally including or "\ - "skipping a field. Directives provide this by describing additional information "\ - "to the executor." - field :name, String, null: false, method: :graphql_name - field :description, String - field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false, scope: false - field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do - argument :include_deprecated, Boolean, required: false, default_value: false - end - field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation? - field :on_fragment, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_fragment? - field :on_field, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_field? - - field :is_repeatable, Boolean, method: :repeatable? - - def args(include_deprecated:) - args = @context.types.arguments(@object) - args = args.reject(&:deprecation_reason) unless include_deprecated - args - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/dynamic_fields.rb b/vendor/gems/graphql/lib/graphql/introspection/dynamic_fields.rb deleted file mode 100644 index 9d788cc73fe..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/dynamic_fields.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class DynamicFields < Introspection::BaseObject - field :__typename, String, "The name of this type", null: false, dynamic_introspection: true - - def __typename - object.class.graphql_name - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/entry_points.rb b/vendor/gems/graphql/lib/graphql/introspection/entry_points.rb deleted file mode 100644 index 866e88cb37a..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/entry_points.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class EntryPoints < Introspection::BaseObject - field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true - field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do - argument :name, String - end - - def __schema - # Apply wrapping manually since this field isn't wrapped by instrumentation - schema = context.schema - schema_type = schema.introspection_system.types["__Schema"] - schema_type.wrap(schema, context) - end - - def __type(name:) - if context.types.reachable_type?(name) && (type = context.types.type(name)) - type - elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name }) - type - else - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/enum_value_type.rb b/vendor/gems/graphql/lib/graphql/introspection/enum_value_type.rb deleted file mode 100644 index 5716fe18857..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/enum_value_type.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class EnumValueType < Introspection::BaseObject - graphql_name "__EnumValue" - description "One possible value for a given Enum. Enum values are unique values, not a "\ - "placeholder for a string or numeric value. However an Enum value is returned in "\ - "a JSON response as a string." - field :name, String, null: false - field :description, String - field :is_deprecated, Boolean, null: false - field :deprecation_reason, String - - def name - object.graphql_name - end - - def is_deprecated - !!@object.deprecation_reason - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/field_type.rb b/vendor/gems/graphql/lib/graphql/introspection/field_type.rb deleted file mode 100644 index 681bb3a7057..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/field_type.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class FieldType < Introspection::BaseObject - graphql_name "__Field" - description "Object and Interface types are described by a list of Fields, each of which has "\ - "a name, potentially a list of arguments, and a return type." - field :name, String, null: false - field :description, String - field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do - argument :include_deprecated, Boolean, required: false, default_value: false - end - field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false - field :is_deprecated, Boolean, null: false - field :deprecation_reason, String - - def is_deprecated - !!@object.deprecation_reason - end - - def args(include_deprecated:) - args = @context.types.arguments(@object) - args = args.reject(&:deprecation_reason) unless include_deprecated - args - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/input_value_type.rb b/vendor/gems/graphql/lib/graphql/introspection/input_value_type.rb deleted file mode 100644 index a7db0c172f1..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/input_value_type.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class InputValueType < Introspection::BaseObject - graphql_name "__InputValue" - description "Arguments provided to Fields or Directives and the input fields of an "\ - "InputObject are represented as Input Values which describe their type and "\ - "optionally a default value." - field :name, String, null: false - field :description, String - field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false - field :default_value, String, "A GraphQL-formatted string representing the default value for this input value." - field :is_deprecated, Boolean, null: false - field :deprecation_reason, String - - def is_deprecated - !!@object.deprecation_reason - end - - def default_value - if @object.default_value? - value = @object.default_value - if value.nil? - 'null' - else - if (@object.type.kind.list? || (@object.type.kind.non_null? && @object.type.of_type.kind.list?)) && !value.respond_to?(:map) - # This is a bit odd -- we expect the default value to be an application-style value, so we use coerce result below. - # But coerce_result doesn't wrap single-item lists, which are valid inputs to list types. - # So, apply that wrapper here if needed. - value = [value] - end - coerced_default_value = @object.type.coerce_result(value, @context) - serialize_default_value(coerced_default_value, @object.type) - end - else - nil - end - end - - - private - - # Recursively serialize, taking care not to add quotes to enum values - def serialize_default_value(value, type) - if value.nil? - 'null' - elsif type.kind.list? - inner_type = type.of_type - "[" + value.map { |v| serialize_default_value(v, inner_type) }.join(", ") + "]" - elsif type.kind.non_null? - serialize_default_value(value, type.of_type) - elsif type.kind.enum? - value - elsif type.kind.input_object? - "{" + - value.map do |k, v| - arg_defn = type.get_argument(k, context) - "#{k}: #{serialize_default_value(v, arg_defn.type)}" - end.join(", ") + - "}" - else - GraphQL::Language.serialize(value) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/introspection_query.rb b/vendor/gems/graphql/lib/graphql/introspection/introspection_query.rb deleted file mode 100644 index acb30beeeb6..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/introspection_query.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -# This query is used by graphql-client so don't add the includeDeprecated -# argument for inputFields since the server may not support it. Two stage -# introspection queries will be required to handle this in clients. -GraphQL::Introspection::INTROSPECTION_QUERY = GraphQL::Introspection.query - diff --git a/vendor/gems/graphql/lib/graphql/introspection/schema_type.rb b/vendor/gems/graphql/lib/graphql/introspection/schema_type.rb deleted file mode 100644 index cc7b1eb0417..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/schema_type.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Introspection - class SchemaType < Introspection::BaseObject - graphql_name "__Schema" - description "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all "\ - "available types and directives on the server, as well as the entry points for "\ - "query, mutation, and subscription operations." - - field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false, scope: false - field :query_type, GraphQL::Schema::LateBoundType.new("__Type"), "The type that query operations will be rooted at.", null: false - field :mutation_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server supports mutation, the type that mutation operations will be rooted at." - field :subscription_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server support subscription, the type that subscription operations will be rooted at." - field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false, scope: false - field :description, String, resolver_method: :schema_description - - def schema_description - context.schema.description - end - - def types - query_types = context.types.all_types - types = query_types + context.schema.extra_types - types.sort_by!(&:graphql_name) - types - end - - def query_type - @context.types.query_root - end - - def mutation_type - @context.types.mutation_root - end - - def subscription_type - @context.types.subscription_root - end - - def directives - @context.types.directives.sort_by(&:graphql_name) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/type_kind_enum.rb b/vendor/gems/graphql/lib/graphql/introspection/type_kind_enum.rb deleted file mode 100644 index efafe016afe..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/type_kind_enum.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class TypeKindEnum < GraphQL::Schema::Enum - graphql_name "__TypeKind" - description "An enum describing what kind of type a given `__Type` is." - GraphQL::TypeKinds::TYPE_KINDS.each do |type_kind| - value(type_kind.name, type_kind.description) - end - introspection true - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/introspection/type_type.rb b/vendor/gems/graphql/lib/graphql/introspection/type_type.rb deleted file mode 100644 index 9e2ede5948c..00000000000 --- a/vendor/gems/graphql/lib/graphql/introspection/type_type.rb +++ /dev/null @@ -1,108 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Introspection - class TypeType < Introspection::BaseObject - graphql_name "__Type" - description "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in "\ - "GraphQL as represented by the `__TypeKind` enum.\n\n"\ - "Depending on the kind of a type, certain fields describe information about that type. "\ - "Scalar types provide no information beyond a name and description, while "\ - "Enum types provide their values. Object and Interface types provide the fields "\ - "they describe. Abstract types, Union and Interface, provide the Object types "\ - "possible at runtime. List and NonNull types compose other types." - - field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false - field :name, String, method: :graphql_name - field :description, String - field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false do - argument :include_deprecated, Boolean, required: false, default_value: false - end - field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false - field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false - field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false do - argument :include_deprecated, Boolean, required: false, default_value: false - end - field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false do - argument :include_deprecated, Boolean, required: false, default_value: false - end - field :of_type, GraphQL::Schema::LateBoundType.new("__Type") - - field :specifiedByURL, String, resolver_method: :specified_by_url - - field :is_one_of, Boolean, null: false - - def is_one_of - object.kind.input_object? && - object.directives.any? { |d| d.graphql_name == "oneOf" } - end - - def specified_by_url - if object.kind.scalar? - object.specified_by_url - else - nil - end - end - - def kind - @object.kind.name - end - - def enum_values(include_deprecated:) - if !@object.kind.enum? - nil - else - enum_values = @context.types.enum_values(@object) - - if !include_deprecated - enum_values = enum_values.select {|f| !f.deprecation_reason } - end - - enum_values - end - end - - def interfaces - if @object.kind.object? || @object.kind.interface? - @context.types.interfaces(@object).sort_by(&:graphql_name) - else - nil - end - end - - def input_fields(include_deprecated:) - if @object.kind.input_object? - args = @context.types.arguments(@object) - args = args.reject(&:deprecation_reason) unless include_deprecated - args - else - nil - end - end - - def possible_types - if @object.kind.abstract? - @context.types.possible_types(@object).sort_by(&:graphql_name) - else - nil - end - end - - def fields(include_deprecated:) - if !@object.kind.fields? - nil - else - fields = @context.types.fields(@object) - if !include_deprecated - fields = fields.select {|f| !f.deprecation_reason } - end - fields.sort_by(&:name) - end - end - - def of_type - @object.kind.wraps? ? @object.of_type : nil - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/invalid_name_error.rb b/vendor/gems/graphql/lib/graphql/invalid_name_error.rb deleted file mode 100644 index 30f19cf5192..00000000000 --- a/vendor/gems/graphql/lib/graphql/invalid_name_error.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class InvalidNameError < GraphQL::Error - attr_reader :name, :valid_regex - def initialize(name, valid_regex) - @name = name - @valid_regex = valid_regex - super("Names must match #{@valid_regex.inspect} but '#{@name}' does not") - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/invalid_null_error.rb b/vendor/gems/graphql/lib/graphql/invalid_null_error.rb deleted file mode 100644 index 6ff6586039b..00000000000 --- a/vendor/gems/graphql/lib/graphql/invalid_null_error.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # Raised automatically when a field's resolve function returns `nil` - # for a non-null field. - class InvalidNullError < GraphQL::Error - # @return [GraphQL::BaseType] The owner of {#field} - attr_reader :parent_type - - # @return [GraphQL::Field] The field which failed to return a value - attr_reader :field - - # @return [nil, GraphQL::ExecutionError] The invalid value for this field - attr_reader :value - - # @return [GraphQL::Language::Nodes::Field] the field where the error occurred - attr_reader :ast_node - - def initialize(parent_type, field, value, ast_node) - @parent_type = parent_type - @field = field - @value = value - @ast_node = ast_node - super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}") - end - - class << self - attr_accessor :parent_class - - def subclass_for(parent_class) - subclass = Class.new(self) - subclass.parent_class = parent_class - subclass - end - - def inspect - if (name.nil? || parent_class&.name.nil?) && parent_class.respond_to?(:mutation) && (mutation = parent_class.mutation) - "#{mutation.inspect}::#{parent_class.graphql_name}::InvalidNullError" - else - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language.rb b/vendor/gems/graphql/lib/graphql/language.rb deleted file mode 100644 index b230d340ab5..00000000000 --- a/vendor/gems/graphql/lib/graphql/language.rb +++ /dev/null @@ -1,97 +0,0 @@ -# frozen_string_literal: true -require "graphql/language/block_string" -require "graphql/language/comment" -require "graphql/language/printer" -require "graphql/language/sanitized_printer" -require "graphql/language/document_from_schema_definition" -require "graphql/language/generation" -require "graphql/language/lexer" -require "graphql/language/nodes" -require "graphql/language/cache" -require "graphql/language/parser" -require "graphql/language/static_visitor" -require "graphql/language/visitor" -require "graphql/language/definition_slice" -require "strscan" - -module GraphQL - module Language - # @api private - def self.serialize(value) - if value.is_a?(Hash) - serialized_hash = value.map do |k, v| - "#{k}:#{serialize v}" - end.join(",") - - "{#{serialized_hash}}" - elsif value.is_a?(Array) - serialized_array = value.map do |v| - serialize v - end.join(",") - - "[#{serialized_array}]" - else - JSON.generate(value, quirks_mode: true) - end - rescue JSON::GeneratorError - if Float::INFINITY == value - "Infinity" - else - raise - end - end - - # Returns a new string if any single-quoted newlines were escaped. - # Otherwise, returns `query_str` unchanged. - # @return [String] - def self.escape_single_quoted_newlines(query_str) - scanner = StringScanner.new(query_str) - inside_single_quoted_string = false - new_query_str = nil - while !scanner.eos? - if scanner.skip(/(?:\\"|[^"\n\r]|""")+/m) - new_query_str && (new_query_str << scanner.matched) - elsif scanner.skip('"') - new_query_str && (new_query_str << '"') - inside_single_quoted_string = !inside_single_quoted_string - elsif scanner.skip("\n") - if inside_single_quoted_string - new_query_str ||= query_str[0, scanner.pos - 1] - new_query_str << '\\n' - else - new_query_str && (new_query_str << "\n") - end - elsif scanner.skip("\r") - if inside_single_quoted_string - new_query_str ||= query_str[0, scanner.pos - 1] - new_query_str << '\\r' - else - new_query_str && (new_query_str << "\r") - end - elsif scanner.eos? - break - else - raise ArgumentError, "Unmatchable string scanner segment: #{scanner.rest.inspect}" - end - end - new_query_str || query_str - end - - INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP = %r{ - ( - ((?#{Lexer::INT_REGEXP}(#{Lexer::FLOAT_EXP_REGEXP})?)(?#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:) - | - ((?#{Lexer::INT_REGEXP}#{Lexer::FLOAT_DECIMAL_REGEXP}#{Lexer::FLOAT_EXP_REGEXP})(?#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:) - | - ((?#{Lexer::INT_REGEXP}#{Lexer::FLOAT_DECIMAL_REGEXP})(?#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:) - )}x - - def self.add_space_between_numbers_and_names(query_str) - if query_str.match?(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP) - query_str.gsub(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP, "\\k \\k:") - else - query_str - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/block_string.rb b/vendor/gems/graphql/lib/graphql/language/block_string.rb deleted file mode 100644 index 8b7a97d8692..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/block_string.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - module BlockString - # Remove leading and trailing whitespace from a block string. - # See "Block Strings" in https://github.com/facebook/graphql/blob/master/spec/Section%202%20--%20Language.md - def self.trim_whitespace(str) - # Early return for the most common cases: - if str == "" - return "".dup - elsif !(has_newline = str.include?("\n")) && !(str.start_with?(" ")) - return str - end - - lines = has_newline ? str.split("\n") : [str] - common_indent = nil - - # find the common whitespace - lines.each_with_index do |line, idx| - if idx == 0 - next - end - line_length = line.size - line_indent = if line.match?(/\A [^ ]/) - 2 - elsif line.match?(/\A [^ ]/) - 4 - elsif line.match?(/\A[^ ]/) - 0 - else - line[/\A */].size - end - if line_indent < line_length && (common_indent.nil? || line_indent < common_indent) - common_indent = line_indent - end - end - - # Remove the common whitespace - if common_indent && common_indent > 0 - lines.each_with_index do |line, idx| - if idx == 0 - next - else - line.slice!(0, common_indent) - end - end - end - - # Remove leading & trailing blank lines - while lines.size > 0 && contains_only_whitespace?(lines.first) - lines.shift - end - while lines.size > 0 && contains_only_whitespace?(lines.last) - lines.pop - end - - # Rebuild the string - lines.size > 1 ? lines.join("\n") : (lines.first || "".dup) - end - - def self.print(str, indent: '') - line_length = 120 - indent.length - block_str = "".dup - triple_quotes = "\"\"\"\n" - block_str << indent - block_str << triple_quotes - - if str.include?("\n") - str.split("\n") do |line| - if line == '' - block_str << "\n" - else - break_line(line, line_length) do |subline| - block_str << indent - block_str << subline - block_str << "\n" - end - end - end - else - break_line(str, line_length) do |subline| - block_str << indent - block_str << subline - block_str << "\n" - end - end - - block_str << indent - block_str << triple_quotes - end - - private - - def self.break_line(line, length) - return yield(line) if line.length < length + 5 - - parts = line.split(Regexp.new("((?: |^).{15,#{length - 40}}(?= |$))")) - return yield(line) if parts.length < 4 - - yield(parts.slice!(0, 3).join) - - parts.each_with_index do |part, i| - next if i % 2 == 1 - yield "#{part[1..-1]}#{parts[i + 1]}" - end - - nil - end - - def self.contains_only_whitespace?(line) - line.match?(/^\s*$/) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/cache.rb b/vendor/gems/graphql/lib/graphql/language/cache.rb deleted file mode 100644 index 5f30ad2f905..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/cache.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -require 'graphql/version' -require 'digest/sha2' - -module GraphQL - module Language - # This cache is used by {GraphQL::Language::Parser.parse_file} when it's enabled. - # - # With Rails, parser caching may enabled by setting `config.graphql.parser_cache = true` in your Rails application. - # - # The cache may be manually built by assigning `GraphQL::Language::Parser.cache = GraphQL::Language::Cache.new("some_dir")`. - # This will create a directory (`tmp/cache/graphql` by default) that stores a cache of parsed files. - # - # Much like [bootsnap](https://github.com/Shopify/bootsnap), the parser cache needs to be cleaned up manually. - # You will need to clear the cache directory for each new deployment of your application. - # Also note that the parser cache will grow as your schema is loaded, so the cache directory must be writable. - # - # @see GraphQL::Railtie for simple Rails integration - class Cache - def initialize(path) - @path = path - end - - DIGEST = Digest::SHA256.new << GraphQL::VERSION - - def fetch(filename) - hash = DIGEST.dup << filename - begin - hash << File.mtime(filename).to_i.to_s - rescue SystemCallError - return yield - end - cache_path = @path.join(hash.to_s) - - if cache_path.exist? - Marshal.load(cache_path.read) - else - payload = yield - tmp_path = "#{cache_path}.#{rand}" - - @path.mkpath - File.binwrite(tmp_path, Marshal.dump(payload)) - File.rename(tmp_path, cache_path.to_s) - payload - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/comment.rb b/vendor/gems/graphql/lib/graphql/language/comment.rb deleted file mode 100644 index a1064399bfa..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/comment.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - module Comment - def self.print(str, indent: '') - lines = str.split("\n").map do |line| - comment_str = "".dup - comment_str << indent - comment_str << "# " - comment_str << line - comment_str.rstrip - end - - lines.join("\n") + "\n" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/definition_slice.rb b/vendor/gems/graphql/lib/graphql/language/definition_slice.rb deleted file mode 100644 index 71033accb91..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/definition_slice.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - module DefinitionSlice - extend self - - def slice(document, name) - definitions = {} - document.definitions.each { |d| definitions[d.name] = d } - names = Set.new - DependencyVisitor.find_definition_dependencies(definitions, name, names) - definitions = document.definitions.select { |d| names.include?(d.name) } - Nodes::Document.new(definitions: definitions) - end - - private - - class DependencyVisitor < GraphQL::Language::StaticVisitor - def initialize(doc, definitions, names) - @names = names - @definitions = definitions - super(doc) - end - - def on_fragment_spread(node, parent) - if fragment = @definitions[node.name] - self.class.find_definition_dependencies(@definitions, fragment.name, @names) - end - super - end - - def self.find_definition_dependencies(definitions, name, names) - names.add(name) - visitor = self.new(definitions[name], definitions, names) - visitor.visit - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/document_from_schema_definition.rb b/vendor/gems/graphql/lib/graphql/language/document_from_schema_definition.rb deleted file mode 100644 index a40cfdc9583..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/document_from_schema_definition.rb +++ /dev/null @@ -1,386 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - # @api private - # - # {GraphQL::Language::DocumentFromSchemaDefinition} is used to convert a {GraphQL::Schema} object - # To a {GraphQL::Language::Document} AST node. - # - # @param context [Hash] - # @param only [<#call(member, ctx)>] - # @param except [<#call(member, ctx)>] - # @param include_introspection_types [Boolean] Whether or not to include introspection types in the AST - # @param include_built_in_scalars [Boolean] Whether or not to include built in scalars in the AST - # @param include_built_in_directives [Boolean] Whether or not to include built in directives in the AST - class DocumentFromSchemaDefinition - def initialize( - schema, context: nil, include_introspection_types: false, - include_built_in_directives: false, include_built_in_scalars: false, always_include_schema: false - ) - @schema = schema - @context = context - @always_include_schema = always_include_schema - @include_introspection_types = include_introspection_types - @include_built_in_scalars = include_built_in_scalars - @include_built_in_directives = include_built_in_directives - @include_one_of = false - - dummy_query = @schema.query_class.new(@schema, "{ __typename }", validate: false, context: context) - @types = dummy_query.types # rubocop:disable Development/ContextIsPassedCop - end - - def document - GraphQL::Language::Nodes::Document.new( - definitions: build_definition_nodes - ) - end - - def build_schema_node - if !schema_respects_root_name_conventions?(@schema) - GraphQL::Language::Nodes::SchemaDefinition.new( - query: @types.query_root&.graphql_name, - mutation: @types.mutation_root&.graphql_name, - subscription: @types.subscription_root&.graphql_name, - directives: definition_directives(@schema, :schema_directives) - ) - else - # A plain `schema ...` _must_ include root type definitions. - # If the only difference is directives, then you have to use `extend schema` - GraphQL::Language::Nodes::SchemaExtension.new(directives: definition_directives(@schema, :schema_directives)) - end - end - - def build_object_type_node(object_type) - ints = @types.interfaces(object_type) - if !ints.empty? - ints.sort_by!(&:graphql_name) - ints.map! { |iface| build_type_name_node(iface) } - end - - GraphQL::Language::Nodes::ObjectTypeDefinition.new( - name: object_type.graphql_name, - comment: object_type.comment, - interfaces: ints, - fields: build_field_nodes(@types.fields(object_type)), - description: object_type.description, - directives: directives(object_type), - ) - end - - def build_field_node(field) - GraphQL::Language::Nodes::FieldDefinition.new( - name: field.graphql_name, - comment: field.comment, - arguments: build_argument_nodes(@types.arguments(field)), - type: build_type_name_node(field.type), - description: field.description, - directives: directives(field), - ) - end - - def build_union_type_node(union_type) - GraphQL::Language::Nodes::UnionTypeDefinition.new( - name: union_type.graphql_name, - comment: union_type.comment, - description: union_type.description, - types: @types.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) }, - directives: directives(union_type), - ) - end - - def build_interface_type_node(interface_type) - GraphQL::Language::Nodes::InterfaceTypeDefinition.new( - name: interface_type.graphql_name, - comment: interface_type.comment, - interfaces: @types.interfaces(interface_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) }, - description: interface_type.description, - fields: build_field_nodes(@types.fields(interface_type)), - directives: directives(interface_type), - ) - end - - def build_enum_type_node(enum_type) - GraphQL::Language::Nodes::EnumTypeDefinition.new( - name: enum_type.graphql_name, - comment: enum_type.comment, - values: @types.enum_values(enum_type).sort_by(&:graphql_name).map do |enum_value| - build_enum_value_node(enum_value) - end, - description: enum_type.description, - directives: directives(enum_type), - ) - end - - def build_enum_value_node(enum_value) - GraphQL::Language::Nodes::EnumValueDefinition.new( - name: enum_value.graphql_name, - comment: enum_value.comment, - description: enum_value.description, - directives: directives(enum_value), - ) - end - - def build_scalar_type_node(scalar_type) - GraphQL::Language::Nodes::ScalarTypeDefinition.new( - name: scalar_type.graphql_name, - comment: scalar_type.comment, - description: scalar_type.description, - directives: directives(scalar_type), - ) - end - - def build_argument_node(argument) - if argument.default_value? - default_value = build_default_value(argument.default_value, argument.type) - else - default_value = nil - end - - argument_node = GraphQL::Language::Nodes::InputValueDefinition.new( - name: argument.graphql_name, - comment: argument.comment, - description: argument.description, - type: build_type_name_node(argument.type), - default_value: default_value, - directives: directives(argument), - ) - - argument_node - end - - def build_input_object_node(input_object) - GraphQL::Language::Nodes::InputObjectTypeDefinition.new( - name: input_object.graphql_name, - comment: input_object.comment, - fields: build_argument_nodes(@types.arguments(input_object)), - description: input_object.description, - directives: directives(input_object), - ) - end - - def build_directive_node(directive) - GraphQL::Language::Nodes::DirectiveDefinition.new( - name: directive.graphql_name, - repeatable: directive.repeatable?, - arguments: build_argument_nodes(@types.arguments(directive)), - locations: build_directive_location_nodes(directive.locations), - description: directive.description, - ) - end - - def build_directive_location_nodes(locations) - locations.sort.map { |location| build_directive_location_node(location) } - end - - def build_directive_location_node(location) - GraphQL::Language::Nodes::DirectiveLocation.new( - name: location.to_s - ) - end - - def build_type_name_node(type) - case type.kind.name - when "LIST" - GraphQL::Language::Nodes::ListType.new( - of_type: build_type_name_node(type.of_type) - ) - when "NON_NULL" - GraphQL::Language::Nodes::NonNullType.new( - of_type: build_type_name_node(type.of_type) - ) - else - @cached_type_name_nodes ||= {} - @cached_type_name_nodes[type.graphql_name] ||= GraphQL::Language::Nodes::TypeName.new(name: type.graphql_name) - end - end - - def build_default_value(default_value, type) - if default_value.nil? - return GraphQL::Language::Nodes::NullValue.new(name: "null") - end - - case type.kind.name - when "SCALAR" - type.coerce_isolated_result(default_value) - when "ENUM" - GraphQL::Language::Nodes::Enum.new(name: type.coerce_isolated_result(default_value)) - when "INPUT_OBJECT" - GraphQL::Language::Nodes::InputObject.new( - arguments: default_value.to_h.map do |arg_name, arg_value| - args = @types.arguments(type) - arg = args.find { |a| a.keyword.to_s == arg_name.to_s } - if arg.nil? - raise ArgumentError, "No argument definition on #{type.graphql_name} for argument: #{arg_name.inspect} (expected one of: #{args.map(&:keyword)})" - end - GraphQL::Language::Nodes::Argument.new( - name: arg.graphql_name.to_s, - value: build_default_value(arg_value, arg.type) - ) - end - ) - when "NON_NULL" - build_default_value(default_value, type.of_type) - when "LIST" - default_value.to_a.map { |v| build_default_value(v, type.of_type) } - else - raise GraphQL::RequiredImplementationMissingError, "Unexpected default value type #{type.inspect}" - end - end - - def build_type_definition_node(type) - case type.kind.name - when "OBJECT" - build_object_type_node(type) - when "UNION" - build_union_type_node(type) - when "INTERFACE" - build_interface_type_node(type) - when "SCALAR" - build_scalar_type_node(type) - when "ENUM" - build_enum_type_node(type) - when "INPUT_OBJECT" - build_input_object_node(type) - else - raise TypeError - end - end - - def build_argument_nodes(arguments) - if !arguments.empty? - nodes = arguments.map { |arg| build_argument_node(arg) } - nodes.sort_by!(&:name) - nodes - else - arguments - end - end - - def build_directive_nodes(directives) - directives - .map { |directive| build_directive_node(directive) } - .sort_by(&:name) - end - - def build_definition_nodes - dirs_to_build = @types.directives - if !include_built_in_directives - dirs_to_build = dirs_to_build.reject { |directive| directive.default_directive? } - end - definitions = build_directive_nodes(dirs_to_build) - all_types = @types.all_types - type_nodes = build_type_definition_nodes(all_types) - - if !(ex_t = schema.extra_types).empty? - dummy_query = Class.new(GraphQL::Schema::Object) do - graphql_name "DummyQuery" - (all_types + ex_t).each_with_index do |type, idx| - if !type.kind.input_object? && !type.introspection? - field "f#{idx}", type - end - end - end - - extra_types_schema = Class.new(GraphQL::Schema) do - query(dummy_query) - end - - extra_types_types = GraphQL::Query.new(extra_types_schema, "{ __typename }", context: @context).types # rubocop:disable Development/ContextIsPassedCop - # Temporarily replace `@types` with something from this example schema. - # It'd be much nicer to pass this in, but that would be a big refactor :S - prev_types = @types - @types = extra_types_types - type_nodes += build_type_definition_nodes(ex_t) - @types = prev_types - end - - type_nodes.sort_by!(&:name) - - if @include_one_of - # This may have been set to true when iterating over all types - definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf])) - end - - definitions.concat(type_nodes) - if include_schema_node? - definitions.unshift(build_schema_node) - end - - definitions - end - - def build_type_definition_nodes(types) - if !include_introspection_types - types = types.reject { |type| type.introspection? } - end - - if !include_built_in_scalars - types = types.reject { |type| type.kind.scalar? && type.default_scalar? } - end - - types.map { |type| build_type_definition_node(type) } - end - - def build_field_nodes(fields) - f_nodes = fields.map { |field| build_field_node(field) } - f_nodes.sort_by!(&:name) - f_nodes - end - - private - - def include_schema_node? - always_include_schema || - !schema_respects_root_name_conventions?(schema) || - !schema.schema_directives.empty? - end - - def schema_respects_root_name_conventions?(schema) - (schema.query.nil? || schema.query.graphql_name == 'Query') && - (schema.mutation.nil? || schema.mutation.graphql_name == 'Mutation') && - (schema.subscription.nil? || schema.subscription.graphql_name == 'Subscription') - end - - def directives(member) - definition_directives(member, :directives) - end - - def definition_directives(member, directives_method) - if !member.respond_to?(directives_method) || member.directives.empty? - EmptyObjects::EMPTY_ARRAY - else - visible_directives = member.public_send(directives_method).select { |dir| @types.directive_exists?(dir.graphql_name) } - visible_directives.map! do |dir| - args = [] - dir.arguments.argument_values.each_value do |arg_value| # rubocop:disable Development/ContextIsPassedCop -- directive instance method - arg_defn = arg_value.definition - if arg_defn.default_value? && arg_value.value == arg_defn.default_value - next - else - value_node = build_default_value(arg_value.value, arg_value.definition.type) - args << GraphQL::Language::Nodes::Argument.new( - name: arg_value.definition.name, - value: value_node, - ) - end - end - - # If this schema uses this built-in directive definition, - # include it in the print-out since it's not part of the spec yet. - @include_one_of ||= dir.class == GraphQL::Schema::Directive::OneOf - - GraphQL::Language::Nodes::Directive.new( - name: dir.class.graphql_name, - arguments: args - ) - end - - visible_directives - end - end - - attr_reader :schema, :always_include_schema, - :include_introspection_types, :include_built_in_directives, :include_built_in_scalars - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/generation.rb b/vendor/gems/graphql/lib/graphql/language/generation.rb deleted file mode 100644 index 6059a264b18..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/generation.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - # Exposes {.generate}, which turns AST nodes back into query strings. - module Generation - extend self - - # Turn an AST node back into a string. - # - # @example Turning a document into a query - # document = GraphQL.parse(query_string) - # GraphQL::Language::Generation.generate(document) - # # => "{ ... }" - # - # @param node [GraphQL::Language::Nodes::AbstractNode] an AST node to recursively stringify - # @param indent [String] Whitespace to add to each printed node - # @param printer [GraphQL::Language::Printer] An optional custom printer for printing AST nodes. Defaults to GraphQL::Language::Printer - # @return [String] Valid GraphQL for `node` - def generate(node, indent: "", printer: GraphQL::Language::Printer.new) - printer.print(node, indent: indent) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/lexer.rb b/vendor/gems/graphql/lib/graphql/language/lexer.rb deleted file mode 100644 index 1aeb1da4c0e..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/lexer.rb +++ /dev/null @@ -1,367 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - - class Lexer - def initialize(graphql_str, filename: nil, max_tokens: nil) - if !(graphql_str.encoding == Encoding::UTF_8 || graphql_str.ascii_only?) - graphql_str = graphql_str.dup.force_encoding(Encoding::UTF_8) - end - @string = graphql_str - @filename = filename - @scanner = StringScanner.new(graphql_str) - @pos = nil - @max_tokens = max_tokens || Float::INFINITY - @tokens_count = 0 - end - - def eos? - @scanner.eos? - end - - attr_reader :pos, :tokens_count - - def advance - @scanner.skip(IGNORE_REGEXP) - return false if @scanner.eos? - @tokens_count += 1 - if @tokens_count > @max_tokens - raise_parse_error("This query is too large to execute.") - end - @pos = @scanner.pos - next_byte = @string.getbyte(@pos) - next_byte_is_for = FIRST_BYTES[next_byte] - case next_byte_is_for - when ByteFor::PUNCTUATION - @scanner.pos += 1 - PUNCTUATION_NAME_FOR_BYTE[next_byte] - when ByteFor::NAME - if len = @scanner.skip(KEYWORD_REGEXP) - case len - when 2 - :ON - when 12 - :SUBSCRIPTION - else - pos = @pos - - # Use bytes 2 and 3 as a unique identifier for this keyword - bytes = (@string.getbyte(pos + 2) << 8) | @string.getbyte(pos + 1) - KEYWORD_BY_TWO_BYTES[_hash(bytes)] - end - else - @scanner.skip(IDENTIFIER_REGEXP) - :IDENTIFIER - end - when ByteFor::IDENTIFIER - @scanner.skip(IDENTIFIER_REGEXP) - :IDENTIFIER - when ByteFor::NUMBER - if len = @scanner.skip(NUMERIC_REGEXP) - - if GraphQL.reject_numbers_followed_by_names - new_pos = @scanner.pos - peek_byte = @string.getbyte(new_pos) - next_first_byte = FIRST_BYTES[peek_byte] - if next_first_byte == ByteFor::NAME || next_first_byte == ByteFor::IDENTIFIER - number_part = token_value - name_part = @scanner.scan(IDENTIFIER_REGEXP) - raise_parse_error("Name after number is not allowed (in `#{number_part}#{name_part}`)") - end - end - # Check for a matched decimal: - @scanner[1] ? :FLOAT : :INT - else - # Attempt to find the part after the `-` - value = @scanner.scan(/-\s?[a-z0-9]*/i) - invalid_byte_for_number_error_message = "Expected type 'number', but it was malformed#{value.nil? ? "" : ": #{value.inspect}"}." - raise_parse_error(invalid_byte_for_number_error_message) - end - when ByteFor::ELLIPSIS - if @string.getbyte(@pos + 1) != 46 || @string.getbyte(@pos + 2) != 46 - raise_parse_error("Expected `...`, actual: #{@string[@pos..@pos + 2].inspect}") - end - @scanner.pos += 3 - :ELLIPSIS - when ByteFor::STRING - if @scanner.skip(BLOCK_STRING_REGEXP) || @scanner.skip(QUOTED_STRING_REGEXP) - :STRING - else - raise_parse_error("Expected string or block string, but it was malformed") - end - else - @scanner.pos += 1 - :UNKNOWN_CHAR - end - rescue ArgumentError => err - if err.message == "invalid byte sequence in UTF-8" - raise_parse_error("Parse error on bad Unicode escape sequence", nil, nil) - end - end - - def token_value - @string.byteslice(@scanner.pos - @scanner.matched_size, @scanner.matched_size) - rescue StandardError => err - raise GraphQL::Error, "(token_value failed: #{err.class}: #{err.message})" - end - - def debug_token_value(token_name) - if token_name && Lexer::Punctuation.const_defined?(token_name) - Lexer::Punctuation.const_get(token_name) - elsif token_name == :ELLIPSIS - "..." - elsif token_name == :STRING - string_value - elsif @scanner.matched_size.nil? - @scanner.peek(1) - else - token_value - end - end - - ESCAPES = /\\["\\\/bfnrt]/ - ESCAPES_REPLACE = { - '\\"' => '"', - "\\\\" => "\\", - "\\/" => '/', - "\\b" => "\b", - "\\f" => "\f", - "\\n" => "\n", - "\\r" => "\r", - "\\t" => "\t", - } - UTF_8 = /\\u(?:([\dAa-f]{4})|\{([\da-f]{4,})\})(?:\\u([\dAa-f]{4}))?/i - VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o - ESCAPED = /(?:#{ESCAPES}|#{UTF_8})/o - - def string_value - str = token_value - is_block = str.start_with?('"""') - if is_block - str.gsub!(/\A"""|"""\z/, '') - return Language::BlockString.trim_whitespace(str) - else - str.gsub!(/\A"|"\z/, '') - - if !str.valid_encoding? || !str.match?(VALID_STRING) - raise_parse_error("Bad unicode escape in #{str.inspect}") - else - Lexer.replace_escaped_characters_in_place(str) - - if !str.valid_encoding? - raise_parse_error("Bad unicode escape in #{str.inspect}") - else - str - end - end - end - end - - def line_number - @scanner.string[0..@pos].count("\n") + 1 - end - - def column_number - @scanner.string[0..@pos].split("\n").last.length - end - - def raise_parse_error(message, line = line_number, col = column_number) - raise GraphQL::ParseError.new(message, line, col, @string, filename: @filename) - end - - IGNORE_REGEXP = %r{ - (?: - [, \c\r\n\t]+ | - \#.*$ - )* - }x - IDENTIFIER_REGEXP = /[_A-Za-z][_0-9A-Za-z]*/ - INT_REGEXP = /-?(?:[0]|[1-9][0-9]*)/ - FLOAT_DECIMAL_REGEXP = /[.][0-9]+/ - FLOAT_EXP_REGEXP = /[eE][+-]?[0-9]+/ - # TODO: FLOAT_EXP_REGEXP should not be allowed to follow INT_REGEXP, integers are not allowed to have exponent parts. - NUMERIC_REGEXP = /#{INT_REGEXP}(#{FLOAT_DECIMAL_REGEXP}#{FLOAT_EXP_REGEXP}|#{FLOAT_DECIMAL_REGEXP}|#{FLOAT_EXP_REGEXP})?/ - - KEYWORDS = [ - "on", - "fragment", - "true", - "false", - "null", - "query", - "mutation", - "subscription", - "schema", - "scalar", - "type", - "extend", - "implements", - "interface", - "union", - "enum", - "input", - "directive", - "repeatable" - ].freeze - - KEYWORD_REGEXP = /#{Regexp.union(KEYWORDS.sort)}\b/ - KEYWORD_BY_TWO_BYTES = [ - :INTERFACE, - :MUTATION, - :EXTEND, - :FALSE, - :ENUM, - :TRUE, - :NULL, - nil, - nil, - nil, - nil, - nil, - nil, - nil, - :QUERY, - nil, - nil, - :REPEATABLE, - :IMPLEMENTS, - :INPUT, - :TYPE, - :SCHEMA, - nil, - nil, - nil, - :DIRECTIVE, - :UNION, - nil, - nil, - :SCALAR, - nil, - :FRAGMENT - ] - - # This produces a unique integer for bytes 2 and 3 of each keyword string - # See https://tenderlovemaking.com/2023/09/02/fast-tokenizers-with-stringscanner.html - def _hash key - (key * 18592990) >> 27 & 0x1f - end - - module Punctuation - LCURLY = '{' - RCURLY = '}' - LPAREN = '(' - RPAREN = ')' - LBRACKET = '[' - RBRACKET = ']' - COLON = ':' - VAR_SIGN = '$' - DIR_SIGN = '@' - EQUALS = '=' - BANG = '!' - PIPE = '|' - AMP = '&' - end - - # A sparse array mapping the bytes for each punctuation - # to a symbol name for that punctuation - PUNCTUATION_NAME_FOR_BYTE = Punctuation.constants.each_with_object([]) { |name, arr| - punct = Punctuation.const_get(name) - arr[punct.ord] = name - } - - QUOTE = '"' - UNICODE_DIGIT = /[0-9A-Za-z]/ - FOUR_DIGIT_UNICODE = /#{UNICODE_DIGIT}{4}/ - N_DIGIT_UNICODE = %r{#{Punctuation::LCURLY}#{UNICODE_DIGIT}{4,}#{Punctuation::RCURLY}}x - UNICODE_ESCAPE = %r{\\u(?:#{FOUR_DIGIT_UNICODE}|#{N_DIGIT_UNICODE})} - STRING_ESCAPE = %r{[\\][\\/bfnrt]} - BLOCK_QUOTE = '"""' - ESCAPED_QUOTE = /\\"/; - STRING_CHAR = /#{ESCAPED_QUOTE}|[^"\\\n\r]|#{UNICODE_ESCAPE}|#{STRING_ESCAPE}/ - QUOTED_STRING_REGEXP = %r{#{QUOTE} (?:#{STRING_CHAR})* #{QUOTE}}x - BLOCK_STRING_REGEXP = %r{ - #{BLOCK_QUOTE} - (?: [^"\\] | # Any characters that aren't a quote or slash - (?= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate - (codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate - # A surrogate pair - combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000 - [combined].pack('U'.freeze) - else - # Two separate code points - [codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze) - end - else - [codepoint_1].pack('U'.freeze) - end - else - ESCAPES_REPLACE[matched_str] - end - end - nil - end - - # This is not used during parsing because the parser - # doesn't actually need tokens. - def self.tokenize(string) - lexer = GraphQL::Language::Lexer.new(string) - tokens = [] - while (token_name = lexer.advance) - new_token = [ - token_name, - lexer.line_number, - lexer.column_number, - lexer.debug_token_value(token_name), - ] - tokens << new_token - end - tokens - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/nodes.rb b/vendor/gems/graphql/lib/graphql/language/nodes.rb deleted file mode 100644 index 8005a838450..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/nodes.rb +++ /dev/null @@ -1,800 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - module Nodes - NONE = GraphQL::EmptyObjects::EMPTY_ARRAY - # {AbstractNode} is the base class for all nodes in a GraphQL AST. - # - # It provides some APIs for working with ASTs: - # - `children` returns all AST nodes attached to this one. Used for tree traversal. - # - `scalars` returns all scalar (Ruby) values attached to this one. Used for comparing nodes. - # - `to_query_string` turns an AST node into a GraphQL string - class AbstractNode - - module DefinitionNode - # This AST node's {#line} returns the first line, which may be the description. - # @return [Integer] The first line of the definition (not the description) - attr_reader :definition_line - - def initialize(definition_line: nil, **_rest) - @definition_line = definition_line - super(**_rest) - end - - def marshal_dump - super << @definition_line - end - - def marshal_load(values) - @definition_line = values.pop - super - end - end - - attr_reader :filename - - def line - @line ||= @source&.line_at(@pos) - end - - def col - @col ||= @source&.column_at(@pos) - end - - def definition_line - @definition_line ||= (@source && @definition_pos) ? @source.line_at(@definition_pos) : nil - end - - # Value equality - # @return [Boolean] True if `self` is equivalent to `other` - def ==(other) - return true if equal?(other) - other.kind_of?(self.class) && - other.scalars == self.scalars && - other.children == self.children - end - - NO_CHILDREN = GraphQL::EmptyObjects::EMPTY_ARRAY - - # @return [Array] all nodes in the tree below this one - def children - NO_CHILDREN - end - - # @return [Array] Scalar values attached to this node - def scalars - NO_CHILDREN - end - - # This might be unnecessary, but its easiest to add it here. - def initialize_copy(other) - @children = nil - @scalars = nil - @query_string = nil - end - - def children_method_name - self.class.children_method_name - end - - def position - [line, col] - end - - def to_query_string(printer: GraphQL::Language::Printer.new) - if printer.is_a?(GraphQL::Language::Printer) - @query_string ||= printer.print(self) - else - printer.print(self) - end - end - - # This creates a copy of `self`, with `new_options` applied. - # @param new_options [Hash] - # @return [AbstractNode] a shallow copy of `self` - def merge(new_options) - dup.merge!(new_options) - end - - # Copy `self`, but modify the copy so that `previous_child` is replaced by `new_child` - def replace_child(previous_child, new_child) - # Figure out which list `previous_child` may be found in - method_name = previous_child.children_method_name - # Get the value from this (original) node - prev_children = public_send(method_name) - if prev_children.is_a?(Array) - # Copy that list, and replace `previous_child` with `new_child` - # in the list. - new_children = prev_children.dup - prev_idx = new_children.index(previous_child) - new_children[prev_idx] = new_child - else - # Use the new value for the given attribute - new_children = new_child - end - # Copy this node, but with the new child value - copy_of_self = merge(method_name => new_children) - # Return the copy: - copy_of_self - end - - # TODO DRY with `replace_child` - def delete_child(previous_child) - # Figure out which list `previous_child` may be found in - method_name = previous_child.children_method_name - # Copy that list, and delete previous_child - new_children = public_send(method_name).dup - new_children.delete(previous_child) - # Copy this node, but with the new list of children: - copy_of_self = merge(method_name => new_children) - # Return the copy: - copy_of_self - end - - protected - - def merge!(new_options) - new_options.each do |key, value| - instance_variable_set(:"@#{key}", value) - end - self - end - - class << self - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - # Add a default `#visit_method` and `#children_method_name` using the class name - def inherited(child_class) - super - name_underscored = child_class.name - .split("::").last - .gsub(/([a-z])([A-Z])/,'\1_\2') # insert underscores - .downcase # remove caps - - child_class.module_eval <<-RUBY, __FILE__, __LINE__ - def visit_method - :on_#{name_underscored} - end - - class << self - attr_accessor :children_method_name - - def visit_method - :on_#{name_underscored} - end - end - self.children_method_name = :#{name_underscored}s - RUBY - end - - def children_of_type - @children_methods - end - - private - - # Name accessors which return lists of nodes, - # along with the kind of node they return, if possible. - # - Add a reader for these children - # - Add a persistent update method to add a child - # - Generate a `#children` method - def children_methods(children_of_type) - if defined?(@children_methods) - raise "Can't re-call .children_methods for #{self} (already have: #{@children_methods})" - else - @children_methods = children_of_type - end - - if children_of_type == false - @children_methods = {} - # skip - else - - children_of_type.each do |method_name, node_type| - module_eval <<-RUBY, __FILE__, __LINE__ - # A reader for these children - attr_reader :#{method_name} - RUBY - - if node_type - # Only generate a method if we know what kind of node to make - module_eval <<-RUBY, __FILE__, __LINE__ - # Singular method: create a node with these options - # and return a new `self` which includes that node in this list. - def merge_#{method_name.to_s.sub(/s$/, "")}(**node_opts) - merge(#{method_name}: #{method_name} + [#{node_type.name}.new(**node_opts)]) - end - RUBY - end - end - - if children_of_type.size == 1 - module_eval <<-RUBY, __FILE__, __LINE__ - alias :children #{children_of_type.keys.first} - RUBY - else - module_eval <<-RUBY, __FILE__, __LINE__ - def children - @children ||= begin - if #{children_of_type.keys.map { |k| "@#{k}.any?" }.join(" || ")} - new_children = [] - #{children_of_type.keys.map { |k| "new_children.concat(@#{k})" }.join("; ")} - new_children.freeze - new_children - else - NO_CHILDREN - end - end - end - RUBY - end - end - - if defined?(@scalar_methods) - if !@initialize_was_generated - @initialize_was_generated = true - generate_initialize - else - # This method was defined manually - end - else - raise "Can't generate_initialize because scalar_methods wasn't called; call it before children_methods" - end - end - - # These methods return a plain Ruby value, not another node - # - Add reader methods - # - Add a `#scalars` method - def scalar_methods(*method_names) - if defined?(@scalar_methods) - raise "Can't re-call .scalar_methods for #{self} (already have: #{@scalar_methods})" - else - @scalar_methods = method_names - end - - if method_names == [false] - @scalar_methods = [] - # skip it - else - module_eval <<-RUBY, __FILE__, __LINE__ - # add readers for each scalar - attr_reader #{method_names.map { |m| ":#{m}"}.join(", ")} - - def scalars - @scalars ||= [#{method_names.map { |k| "@#{k}" }.join(", ")}].freeze - end - RUBY - end - end - - DEFAULT_INITIALIZE_OPTIONS = [ - "line: nil", - "col: nil", - "pos: nil", - "filename: nil", - "source: nil" - ] - - IGNORED_MARSHALLING_KEYWORDS = [:comment] - - def generate_initialize - return if method_defined?(:marshal_load, false) # checking for `:initialize` doesn't work right - - scalar_method_names = @scalar_methods - # TODO: These probably should be scalar methods, but `types` returns an array - [:types, :description, :comment].each do |extra_method| - if method_defined?(extra_method) - scalar_method_names += [extra_method] - end - end - - children_method_names = @children_methods.keys - - all_method_names = scalar_method_names + children_method_names - if all_method_names.include?(:alias) - # Rather than complicating this special case, - # let it be overridden (in field) - return - else - arguments = scalar_method_names.map { |m| "#{m}: nil"} + - children_method_names.map { |m| "#{m}: NO_CHILDREN" } + - DEFAULT_INITIALIZE_OPTIONS - - assignments = scalar_method_names.map { |m| "@#{m} = #{m}"} + - children_method_names.map { |m| "@#{m} = #{m}.freeze" } - - if name.end_with?("Definition") && name != "FragmentDefinition" - arguments << "definition_pos: nil" - assignments << "@definition_pos = definition_pos" - end - - keywords = scalar_method_names.map { |m| "#{m}: #{m}"} + - children_method_names.map { |m| "#{m}: #{m}" } - - ignored_keywords = IGNORED_MARSHALLING_KEYWORDS.map do |keyword| - "#{keyword.to_s}: nil" - end - - marshalling_method_names = all_method_names - IGNORED_MARSHALLING_KEYWORDS - - module_eval <<-RUBY, __FILE__, __LINE__ - def initialize(#{arguments.join(", ")}) - @line = line - @col = col - @pos = pos - @filename = filename - @source = source - #{assignments.join("\n")} - end - - def self.from_a(filename, line, col, #{marshalling_method_names.join(", ")}, #{ignored_keywords.join(", ")}) - self.new(filename: filename, line: line, col: col, #{keywords.join(", ")}) - end - - def marshal_dump - [ - line, col, # use methods here to force them to be calculated - @filename, - #{marshalling_method_names.map { |n| "@#{n}," }.join} - ] - end - - def marshal_load(values) - @line, @col, @filename #{marshalling_method_names.map { |n| ", @#{n}"}.join} = values - end - RUBY - end - end - # rubocop:enable Development/NoEvalCop - end - end - - # Base class for non-null type names and list type names - class WrapperType < AbstractNode - scalar_methods :of_type - children_methods(false) - end - - # Base class for nodes whose only value is a name (no child nodes or other scalars) - class NameOnlyNode < AbstractNode - scalar_methods :name - children_methods(false) - end - - # A key-value pair for a field's inputs - class Argument < AbstractNode - scalar_methods :name, :value - children_methods(false) - - # @!attribute name - # @return [String] the key for this argument - - # @!attribute value - # @return [String, Float, Integer, Boolean, Array, InputObject, VariableIdentifier] The value passed for this key - - def children - @children ||= Array(value).flatten.tap { _1.select! { |v| v.is_a?(AbstractNode) } } - end - end - - class Directive < AbstractNode - scalar_methods :name - children_methods(arguments: GraphQL::Language::Nodes::Argument) - end - - class DirectiveLocation < NameOnlyNode - end - - class DirectiveDefinition < AbstractNode - attr_reader :description - scalar_methods :name, :repeatable - children_methods( - arguments: Nodes::Argument, - locations: Nodes::DirectiveLocation, - ) - self.children_method_name = :definitions - end - - # An enum value. The string is available as {#name}. - class Enum < NameOnlyNode - end - - # A null value literal. - class NullValue < NameOnlyNode - end - - # A single selection in a GraphQL query. - class Field < AbstractNode - def initialize(name: nil, arguments: NONE, directives: NONE, selections: NONE, field_alias: nil, line: nil, col: nil, pos: nil, filename: nil, source: nil) - @name = name - @arguments = arguments || NONE - @directives = directives || NONE - @selections = selections || NONE - # oops, alias is a keyword: - @alias = field_alias - @line = line - @col = col - @pos = pos - @filename = filename - @source = source - end - - def self.from_a(filename, line, col, field_alias, name, arguments, directives, selections) # rubocop:disable Metrics/ParameterLists - self.new(filename: filename, line: line, col: col, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selections) - end - - def marshal_dump - [line, col, @filename, @name, @arguments, @directives, @selections, @alias] - end - - def marshal_load(values) - @line, @col, @filename, @name, @arguments, @directives, @selections, @alias = values - end - - scalar_methods :name, :alias - children_methods({ - arguments: GraphQL::Language::Nodes::Argument, - selections: GraphQL::Language::Nodes::Field, - directives: GraphQL::Language::Nodes::Directive, - }) - - # Override this because default is `:fields` - self.children_method_name = :selections - end - - # A reusable fragment, defined at document-level. - class FragmentDefinition < AbstractNode - def initialize(name: nil, type: nil, directives: NONE, selections: NONE, filename: nil, pos: nil, source: nil, line: nil, col: nil) - @name = name - @type = type - @directives = directives - @selections = selections - @filename = filename - @pos = pos - @source = source - @line = line - @col = col - end - - def self.from_a(filename, line, col, name, type, directives, selections) - self.new(filename: filename, line: line, col: col, name: name, type: type, directives: directives, selections: selections) - end - - def marshal_dump - [line, col, @filename, @name, @type, @directives, @selections] - end - - def marshal_load(values) - @line, @col, @filename, @name, @type, @directives, @selections = values - end - - scalar_methods :name, :type - children_methods({ - selections: GraphQL::Language::Nodes::Field, - directives: GraphQL::Language::Nodes::Directive, - }) - - self.children_method_name = :definitions - end - - # Application of a named fragment in a selection - class FragmentSpread < AbstractNode - scalar_methods :name - children_methods(directives: GraphQL::Language::Nodes::Directive) - - self.children_method_name = :selections - - # @!attribute name - # @return [String] The identifier of the fragment to apply, corresponds with {FragmentDefinition#name} - end - - # An unnamed fragment, defined directly in the query with `... { }` - class InlineFragment < AbstractNode - scalar_methods :type - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - selections: GraphQL::Language::Nodes::Field, - }) - - self.children_method_name = :selections - - # @!attribute type - # @return [String, nil] Name of the type this fragment applies to, or `nil` if this fragment applies to any type - end - - # A collection of key-value inputs which may be a field argument - class InputObject < AbstractNode - scalar_methods(false) - children_methods(arguments: GraphQL::Language::Nodes::Argument) - - # @!attribute arguments - # @return [Array] A list of key-value pairs inside this input object - - # @return [Hash] Recursively turn this input object into a Ruby Hash - def to_h(options={}) - arguments.inject({}) do |memo, pair| - v = pair.value - memo[pair.name] = serialize_value_for_hash v - memo - end - end - - self.children_method_name = :value - - private - - def serialize_value_for_hash(value) - case value - when InputObject - value.to_h - when Array - value.map do |v| - serialize_value_for_hash v - end - when Enum - value.name - when NullValue - nil - else - value - end - end - end - - # A list type definition, denoted with `[...]` (used for variable type definitions) - class ListType < WrapperType - end - - # A non-null type definition, denoted with `...!` (used for variable type definitions) - class NonNullType < WrapperType - end - - # An operation-level query variable - class VariableDefinition < AbstractNode - scalar_methods :name, :type, :default_value - children_methods(directives: Directive) - # @!attribute default_value - # @return [String, Integer, Float, Boolean, Array, NullValue] A Ruby value to use if no other value is provided - - # @!attribute type - # @return [TypeName, NonNullType, ListType] The expected type of this value - - # @!attribute name - # @return [String] The identifier for this variable, _without_ `$` - - self.children_method_name = :variables - end - - # A query, mutation or subscription. - # May be anonymous or named. - # May be explicitly typed (eg `mutation { ... }`) or implicitly a query (eg `{ ... }`). - class OperationDefinition < AbstractNode - scalar_methods :operation_type, :name - children_methods({ - variables: GraphQL::Language::Nodes::VariableDefinition, - directives: GraphQL::Language::Nodes::Directive, - selections: GraphQL::Language::Nodes::Field, - }) - - # @!attribute variables - # @return [Array] Variable $definitions for this operation - - # @!attribute selections - # @return [Array] Root-level fields on this operation - - # @!attribute operation_type - # @return [String, nil] The root type for this operation, or `nil` for implicit `"query"` - - # @!attribute name - # @return [String, nil] The name for this operation, or `nil` if unnamed - - self.children_method_name = :definitions - end - - # This is the AST root for normal queries - # - # @example Deriving a document by parsing a string - # document = GraphQL.parse(query_string) - # - # @example Creating a string from a document - # document.to_query_string - # # { ... } - # - # @example Creating a custom string from a document - # class VariableScrubber < GraphQL::Language::Printer - # def print_argument(arg) - # print_string("#{arg.name}: ") - # end - # end - # - # document.to_query_string(printer: VariableScrubber.new) - # - class Document < AbstractNode - scalar_methods false - children_methods(definitions: nil) - # @!attribute definitions - # @return [Array] top-level GraphQL units: operations or fragments - - def slice_definition(name) - GraphQL::Language::DefinitionSlice.slice(self, name) - end - end - - # A type name, used for variable definitions - class TypeName < NameOnlyNode - end - - # Usage of a variable in a query. Name does _not_ include `$`. - class VariableIdentifier < NameOnlyNode - self.children_method_name = :value - end - - class SchemaDefinition < AbstractNode - scalar_methods :query, :mutation, :subscription - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :definitions - end - - class SchemaExtension < AbstractNode - scalar_methods :query, :mutation, :subscription - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :definitions - end - - class ScalarTypeDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :definitions - end - - class ScalarTypeExtension < AbstractNode - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :definitions - end - - class InputValueDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name, :type, :default_value - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :fields - end - - class FieldDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name, :type - children_methods({ - arguments: GraphQL::Language::Nodes::InputValueDefinition, - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :fields - - # this is so that `children_method_name` of `InputValueDefinition` works properly - # with `#replace_child` - alias :fields :arguments - def merge(new_options) - if (f = new_options.delete(:fields)) - new_options[:arguments] = f - end - super - end - end - - class ObjectTypeDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name, :interfaces - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - fields: GraphQL::Language::Nodes::FieldDefinition, - }) - self.children_method_name = :definitions - end - - class ObjectTypeExtension < AbstractNode - scalar_methods :name, :interfaces - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - fields: GraphQL::Language::Nodes::FieldDefinition, - }) - self.children_method_name = :definitions - end - - class InterfaceTypeDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name - children_methods({ - interfaces: GraphQL::Language::Nodes::TypeName, - directives: GraphQL::Language::Nodes::Directive, - fields: GraphQL::Language::Nodes::FieldDefinition, - }) - self.children_method_name = :definitions - end - - class InterfaceTypeExtension < AbstractNode - scalar_methods :name - children_methods({ - interfaces: GraphQL::Language::Nodes::TypeName, - directives: GraphQL::Language::Nodes::Directive, - fields: GraphQL::Language::Nodes::FieldDefinition, - }) - self.children_method_name = :definitions - end - - class UnionTypeDefinition < AbstractNode - attr_reader :description, :comment, :types - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :definitions - end - - class UnionTypeExtension < AbstractNode - attr_reader :types - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :definitions - end - - class EnumValueDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - }) - self.children_method_name = :values - end - - class EnumTypeDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - values: GraphQL::Language::Nodes::EnumValueDefinition, - }) - self.children_method_name = :definitions - end - - class EnumTypeExtension < AbstractNode - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - values: GraphQL::Language::Nodes::EnumValueDefinition, - }) - self.children_method_name = :definitions - end - - class InputObjectTypeDefinition < AbstractNode - attr_reader :description, :comment - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - fields: GraphQL::Language::Nodes::InputValueDefinition, - }) - self.children_method_name = :definitions - end - - class InputObjectTypeExtension < AbstractNode - scalar_methods :name - children_methods({ - directives: GraphQL::Language::Nodes::Directive, - fields: GraphQL::Language::Nodes::InputValueDefinition, - }) - self.children_method_name = :definitions - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/parser.rb b/vendor/gems/graphql/lib/graphql/language/parser.rb deleted file mode 100644 index d18863b45ae..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/parser.rb +++ /dev/null @@ -1,845 +0,0 @@ -# frozen_string_literal: true - -require "strscan" -require "graphql/language/nodes" - -module GraphQL - module Language - class Parser - include GraphQL::Language::Nodes - include EmptyObjects - - class << self - attr_accessor :cache - - def parse(graphql_str, filename: nil, trace: Tracing::NullTrace, max_tokens: nil) - self.new(graphql_str, filename: filename, trace: trace, max_tokens: max_tokens).parse - end - - def parse_file(filename, trace: Tracing::NullTrace) - if cache - cache.fetch(filename) do - parse(File.read(filename), filename: filename, trace: trace) - end - else - parse(File.read(filename), filename: filename, trace: trace) - end - end - end - - def initialize(graphql_str, filename: nil, trace: Tracing::NullTrace, max_tokens: nil) - if graphql_str.nil? - raise GraphQL::ParseError.new("No query string was present", nil, nil, nil) - end - @lexer = Lexer.new(graphql_str, filename: filename, max_tokens: max_tokens) - @graphql_str = graphql_str - @filename = filename - @trace = trace - @dedup_identifiers = false - @lines_at = nil - end - - def parse - @document ||= begin - @trace.parse(query_string: @graphql_str) do - document - end - rescue SystemStackError - raise GraphQL::ParseError.new("This query is too large to execute.", nil, nil, @query_str, filename: @filename) - end - end - - def tokens_count - parse - @lexer.tokens_count - end - - def line_at(pos) - line = lines_at.bsearch_index { |l| l >= pos } - if line.nil? - @lines_at.size + 1 - else - line + 1 - end - end - - def column_at(pos) - next_line_idx = lines_at.bsearch_index { |l| l >= pos } || 0 - if next_line_idx > 0 - line_pos = @lines_at[next_line_idx - 1] - pos - line_pos - else - pos + 1 - end - end - - private - - # @return [Array] Positions of each line break in the original string - def lines_at - @lines_at ||= begin - la = [] - idx = 0 - while idx - idx = @graphql_str.index("\n", idx) - if idx - la << idx - idx += 1 - end - end - la - end - end - - attr_reader :token_name - - def advance_token - @token_name = @lexer.advance - end - - def pos - @lexer.pos - end - - def document - any_tokens = advance_token - defns = [] - if any_tokens - defns << definition - else - # Only ignored characters is not a valid document - raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @graphql_str) - end - while !@lexer.eos? - defns << definition - end - Document.new(pos: 0, definitions: defns, filename: @filename, source: self) - end - - def definition - case token_name - when :FRAGMENT - loc = pos - expect_token :FRAGMENT - f_name = if !at?(:ON) - parse_name - end - expect_token :ON - f_type = parse_type_name - directives = parse_directives - selections = selection_set - Nodes::FragmentDefinition.new( - pos: loc, - name: f_name, - type: f_type, - directives: directives, - selections: selections, - filename: @filename, - source: self - ) - when :QUERY, :MUTATION, :SUBSCRIPTION, :LCURLY - op_loc = pos - op_type = case token_name - when :LCURLY - "query" - else - parse_operation_type - end - - op_name = case token_name - when :LPAREN, :LCURLY, :DIR_SIGN - nil - else - parse_name - end - - variable_definitions = if at?(:LPAREN) - expect_token(:LPAREN) - defs = [] - while !at?(:RPAREN) - loc = pos - expect_token(:VAR_SIGN) - var_name = parse_name - expect_token(:COLON) - var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}") - default_value = if at?(:EQUALS) - advance_token - value - end - - directives = parse_directives - - defs << Nodes::VariableDefinition.new( - pos: loc, - name: var_name, - type: var_type, - default_value: default_value, - directives: directives, - filename: @filename, - source: self - ) - end - expect_token(:RPAREN) - defs - else - EmptyObjects::EMPTY_ARRAY - end - - directives = parse_directives - - OperationDefinition.new( - pos: op_loc, - operation_type: op_type, - name: op_name, - variables: variable_definitions, - directives: directives, - selections: selection_set, - filename: @filename, - source: self - ) - when :EXTEND - loc = pos - advance_token - case token_name - when :SCALAR - advance_token - name = parse_name - directives = parse_directives - ScalarTypeExtension.new(pos: loc, name: name, directives: directives, filename: @filename, source: self) - when :TYPE - advance_token - name = parse_name - implements_interfaces = parse_implements - directives = parse_directives - field_defns = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY - - ObjectTypeExtension.new(pos: loc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source: self) - when :INTERFACE - advance_token - name = parse_name - directives = parse_directives - interfaces = parse_implements - fields_definition = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY - InterfaceTypeExtension.new(pos: loc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source: self) - when :UNION - advance_token - name = parse_name - directives = parse_directives - union_member_types = parse_union_members - UnionTypeExtension.new(pos: loc, name: name, directives: directives, types: union_member_types, filename: @filename, source: self) - when :ENUM - advance_token - name = parse_name - directives = parse_directives - enum_values_definition = parse_enum_value_definitions - Nodes::EnumTypeExtension.new(pos: loc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source: self) - when :INPUT - advance_token - name = parse_name - directives = parse_directives - input_fields_definition = parse_input_object_field_definitions - InputObjectTypeExtension.new(pos: loc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self) - when :SCHEMA - advance_token - directives = parse_directives - query = mutation = subscription = nil - if at?(:LCURLY) - advance_token - while !at?(:RCURLY) - if at?(:QUERY) - advance_token - expect_token(:COLON) - query = parse_name - elsif at?(:MUTATION) - advance_token - expect_token(:COLON) - mutation = parse_name - elsif at?(:SUBSCRIPTION) - advance_token - expect_token(:COLON) - subscription = parse_name - else - expect_one_of([:QUERY, :MUTATION, :SUBSCRIPTION]) - end - end - expect_token :RCURLY - end - SchemaExtension.new( - subscription: subscription, - mutation: mutation, - query: query, - directives: directives, - pos: loc, - filename: @filename, - source: self, - ) - else - expect_one_of([:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE]) - end - else - loc = pos - desc = at?(:STRING) ? string_value : nil - defn_loc = pos - case token_name - when :SCHEMA - advance_token - directives = parse_directives - query = mutation = subscription = nil - expect_token :LCURLY - while !at?(:RCURLY) - if at?(:QUERY) - advance_token - expect_token(:COLON) - query = parse_name - elsif at?(:MUTATION) - advance_token - expect_token(:COLON) - mutation = parse_name - elsif at?(:SUBSCRIPTION) - advance_token - expect_token(:COLON) - subscription = parse_name - else - expect_one_of([:QUERY, :MUTATION, :SUBSCRIPTION]) - end - end - expect_token :RCURLY - SchemaDefinition.new(pos: loc, definition_pos: defn_loc, query: query, mutation: mutation, subscription: subscription, directives: directives, filename: @filename, source: self) - when :DIRECTIVE - advance_token - expect_token :DIR_SIGN - name = parse_name - arguments_definition = parse_argument_definitions - repeatable = if at?(:REPEATABLE) - advance_token - true - else - false - end - expect_token :ON - directive_locations = [DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source: self)] - while at?(:PIPE) - advance_token - directive_locations << DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source: self) - end - DirectiveDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, arguments: arguments_definition, locations: directive_locations, repeatable: repeatable, filename: @filename, source: self) - when :TYPE - advance_token - name = parse_name - implements_interfaces = parse_implements - directives = parse_directives - field_defns = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY - - ObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source: self) - when :INTERFACE - advance_token - name = parse_name - interfaces = parse_implements - directives = parse_directives - fields_definition = parse_field_definitions - InterfaceTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source: self) - when :UNION - advance_token - name = parse_name - directives = parse_directives - union_member_types = parse_union_members - UnionTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, types: union_member_types, filename: @filename, source: self) - when :SCALAR - advance_token - name = parse_name - directives = parse_directives - ScalarTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, filename: @filename, source: self) - when :ENUM - advance_token - name = parse_name - directives = parse_directives - enum_values_definition = parse_enum_value_definitions - Nodes::EnumTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source: self) - when :INPUT - advance_token - name = parse_name - directives = parse_directives - input_fields_definition = parse_input_object_field_definitions - InputObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self) - else - expect_one_of([:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE]) - end - end - end - - def parse_input_object_field_definitions - if at?(:LCURLY) - expect_token :LCURLY - list = [] - while !at?(:RCURLY) - list << parse_input_value_definition - end - expect_token :RCURLY - list - else - EMPTY_ARRAY - end - end - - def parse_enum_value_definitions - if at?(:LCURLY) - expect_token :LCURLY - list = [] - while !at?(:RCURLY) - v_loc = pos - description = if at?(:STRING); string_value; end - defn_loc = pos - # Any identifier, but not true, false, or null - enum_value = if at?(:TRUE) || at?(:FALSE) || at?(:NULL) - expect_token(:IDENTIFIER) - else - parse_name - end - v_directives = parse_directives - list << EnumValueDefinition.new(pos: v_loc, definition_pos: defn_loc, description: description, name: enum_value, directives: v_directives, filename: @filename, source: self) - end - expect_token :RCURLY - list - else - EMPTY_ARRAY - end - end - - def parse_union_members - if at?(:EQUALS) - expect_token :EQUALS - if at?(:PIPE) - advance_token - end - list = [parse_type_name] - while at?(:PIPE) - advance_token - list << parse_type_name - end - list - else - EMPTY_ARRAY - end - end - - def parse_implements - if at?(:IMPLEMENTS) - advance_token - list = [] - while true - advance_token if at?(:AMP) - break unless at?(:IDENTIFIER) - list << parse_type_name - end - list - else - EMPTY_ARRAY - end - end - - def parse_field_definitions - expect_token :LCURLY - list = [] - while !at?(:RCURLY) - loc = pos - description = if at?(:STRING); string_value; end - defn_loc = pos - name = parse_name - arguments_definition = parse_argument_definitions - expect_token :COLON - type = self.type - directives = parse_directives - - list << FieldDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, arguments: arguments_definition, type: type, directives: directives, filename: @filename, source: self) - end - expect_token :RCURLY - list - end - - def parse_argument_definitions - if at?(:LPAREN) - advance_token - list = [] - while !at?(:RPAREN) - list << parse_input_value_definition - end - expect_token :RPAREN - list - else - EMPTY_ARRAY - end - end - - def parse_input_value_definition - loc = pos - description = if at?(:STRING); string_value; end - defn_loc = pos - name = parse_name - expect_token :COLON - type = self.type - default_value = if at?(:EQUALS) - advance_token - value - else - nil - end - directives = parse_directives - InputValueDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, type: type, default_value: default_value, directives: directives, filename: @filename, source: self) - end - - def type - type = case token_name - when :IDENTIFIER - parse_type_name - when :LBRACKET - list_type - end - - if at?(:BANG) - type = Nodes::NonNullType.new(pos: pos, of_type: type, source: self) - expect_token(:BANG) - end - type - end - - def list_type - loc = pos - expect_token(:LBRACKET) - type = Nodes::ListType.new(pos: loc, of_type: self.type, source: self) - expect_token(:RBRACKET) - type - end - - def parse_operation_type - val = if at?(:QUERY) - "query" - elsif at?(:MUTATION) - "mutation" - elsif at?(:SUBSCRIPTION) - "subscription" - else - expect_one_of([:QUERY, :MUTATION, :SUBSCRIPTION]) - end - advance_token - val - end - - def selection_set - expect_token(:LCURLY) - selections = [] - while @token_name != :RCURLY - selections << if at?(:ELLIPSIS) - loc = pos - advance_token - case token_name - when :ON, :DIR_SIGN, :LCURLY - if_type = if at?(:ON) - advance_token - parse_type_name - else - nil - end - - directives = parse_directives - - Nodes::InlineFragment.new(pos: loc, type: if_type, directives: directives, selections: selection_set, filename: @filename, source: self) - else - name = parse_name_without_on - directives = parse_directives - - # Can this ever happen? - # expect_token(:IDENTIFIER) if at?(:ON) - - FragmentSpread.new(pos: loc, name: name, directives: directives, filename: @filename, source: self) - end - else - loc = pos - name = parse_name - - field_alias = nil - - if at?(:COLON) - advance_token - field_alias = name - name = parse_name - end - - arguments = at?(:LPAREN) ? parse_arguments : nil - directives = at?(:DIR_SIGN) ? parse_directives : nil - selection_set = at?(:LCURLY) ? self.selection_set : nil - - Nodes::Field.new(pos: loc, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selection_set, filename: @filename, source: self) - end - end - expect_token(:RCURLY) - selections - end - - def parse_name - case token_name - when :IDENTIFIER - expect_token_value(:IDENTIFIER) - when :SCHEMA - advance_token - "schema" - when :SCALAR - advance_token - "scalar" - when :IMPLEMENTS - advance_token - "implements" - when :INTERFACE - advance_token - "interface" - when :UNION - advance_token - "union" - when :ENUM - advance_token - "enum" - when :INPUT - advance_token - "input" - when :DIRECTIVE - advance_token - "directive" - when :TYPE - advance_token - "type" - when :QUERY - advance_token - "query" - when :MUTATION - advance_token - "mutation" - when :SUBSCRIPTION - advance_token - "subscription" - when :TRUE - advance_token - "true" - when :FALSE - advance_token - "false" - when :FRAGMENT - advance_token - "fragment" - when :REPEATABLE - advance_token - "repeatable" - when :NULL - advance_token - "null" - when :ON - advance_token - "on" - when :EXTEND - advance_token - "extend" - else - expect_token(:NAME) - end - end - - def parse_name_without_on - if at?(:ON) - expect_token(:IDENTIFIER) - else - parse_name - end - end - - def parse_type_name - TypeName.new(pos: pos, name: parse_name, filename: @filename, source: self) - end - - def parse_directives - if at?(:DIR_SIGN) - dirs = [] - while at?(:DIR_SIGN) - loc = pos - advance_token - name = parse_name - arguments = parse_arguments - - dirs << Nodes::Directive.new(pos: loc, name: name, arguments: arguments, filename: @filename, source: self) - end - dirs - else - EMPTY_ARRAY - end - end - - def parse_arguments - if at?(:LPAREN) - advance_token - args = [] - while !at?(:RPAREN) - loc = pos - name = parse_name - expect_token(:COLON) - args << Nodes::Argument.new(pos: loc, name: name, value: value, filename: @filename, source: self) - end - if args.empty? - expect_token(:ARGUMENT_NAME) # At least one argument is required - end - expect_token(:RPAREN) - args - else - EMPTY_ARRAY - end - end - - def string_value - token_value = @lexer.string_value - expect_token :STRING - token_value - end - - def value - case token_name - when :INT - expect_token_value(:INT).to_i - when :FLOAT - expect_token_value(:FLOAT).to_f - when :STRING - string_value - when :TRUE - advance_token - true - when :FALSE - advance_token - false - when :NULL - advance_token - NullValue.new(pos: pos, name: "null", filename: @filename, source: self) - when :IDENTIFIER - Nodes::Enum.new(pos: pos, name: expect_token_value(:IDENTIFIER), filename: @filename, source: self) - when :LBRACKET - advance_token - list = [] - while !at?(:RBRACKET) - list << value - end - expect_token(:RBRACKET) - list - when :LCURLY - start = pos - advance_token - args = [] - while !at?(:RCURLY) - loc = pos - n = parse_name - expect_token(:COLON) - args << Argument.new(pos: loc, name: n, value: value, filename: @filename, source: self) - end - expect_token(:RCURLY) - InputObject.new(pos: start, arguments: args, filename: @filename, source: self) - when :VAR_SIGN - loc = pos - advance_token - VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename, source: self) - when :SCHEMA - advance_token - Nodes::Enum.new(pos: pos, name: "schema", filename: @filename, source: self) - when :SCALAR - advance_token - Nodes::Enum.new(pos: pos, name: "scalar", filename: @filename, source: self) - when :IMPLEMENTS - advance_token - Nodes::Enum.new(pos: pos, name: "implements", filename: @filename, source: self) - when :INTERFACE - advance_token - Nodes::Enum.new(pos: pos, name: "interface", filename: @filename, source: self) - when :UNION - advance_token - Nodes::Enum.new(pos: pos, name: "union", filename: @filename, source: self) - when :ENUM - advance_token - Nodes::Enum.new(pos: pos, name: "enum", filename: @filename, source: self) - when :INPUT - advance_token - Nodes::Enum.new(pos: pos, name: "input", filename: @filename, source: self) - when :DIRECTIVE - advance_token - Nodes::Enum.new(pos: pos, name: "directive", filename: @filename, source: self) - when :TYPE - advance_token - Nodes::Enum.new(pos: pos, name: "type", filename: @filename, source: self) - when :QUERY - advance_token - Nodes::Enum.new(pos: pos, name: "query", filename: @filename, source: self) - when :MUTATION - advance_token - Nodes::Enum.new(pos: pos, name: "mutation", filename: @filename, source: self) - when :SUBSCRIPTION - advance_token - Nodes::Enum.new(pos: pos, name: "subscription", filename: @filename, source: self) - when :FRAGMENT - advance_token - Nodes::Enum.new(pos: pos, name: "fragment", filename: @filename, source: self) - when :REPEATABLE - advance_token - Nodes::Enum.new(pos: pos, name: "repeatable", filename: @filename, source: self) - when :ON - advance_token - Nodes::Enum.new(pos: pos, name: "on", filename: @filename, source: self) - when :EXTEND - advance_token - Nodes::Enum.new(pos: pos, name: "extend", filename: @filename, source: self) - else - expect_token(:VALUE) - end - end - - def at?(expected_token_name) - @token_name == expected_token_name - end - - def expect_token(expected_token_name) - unless @token_name == expected_token_name - raise_parse_error("Expected #{expected_token_name}, actual: #{token_name || "(none)"} (#{debug_token_value.inspect})") - end - advance_token - end - - def expect_one_of(token_names) - raise_parse_error("Expected one of #{token_names.join(", ")}, actual: #{token_name || "NOTHING"} (#{debug_token_value.inspect})") - end - - def raise_parse_error(message) - message += " at [#{@lexer.line_number}, #{@lexer.column_number}]" - raise GraphQL::ParseError.new( - message, - @lexer.line_number, - @lexer.column_number, - @graphql_str, - filename: @filename, - ) - - end - - # Only use when we care about the expected token's value - def expect_token_value(tok) - token_value = @lexer.token_value - if @dedup_identifiers - token_value = -token_value - end - expect_token(tok) - token_value - end - - # token_value works for when the scanner matched something - # which is usually fine and it's good for it to be fast at that. - def debug_token_value - @lexer.debug_token_value(token_name) - end - class SchemaParser < Parser - def initialize(*args, **kwargs) - super - @dedup_identifiers = true - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/printer.rb b/vendor/gems/graphql/lib/graphql/language/printer.rb deleted file mode 100644 index c861f33404d..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/printer.rb +++ /dev/null @@ -1,573 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - class Printer - OMISSION = "... (truncated)" - - class TruncatableBuffer - class TruncateSizeReached < StandardError; end - - DEFAULT_INIT_CAPACITY = 500 - - def initialize(truncate_size: nil) - @out = String.new(capacity: truncate_size || DEFAULT_INIT_CAPACITY) - @truncate_size = truncate_size - end - - def append(other) - if @truncate_size && (@out.size + other.size) > @truncate_size - @out << other.slice(0, @truncate_size - @out.size) - raise(TruncateSizeReached, "Truncate size reached") - else - @out << other - end - end - - def to_string - @out - end - end - - # Turn an arbitrary AST node back into a string. - # - # @example Turning a document into a query string - # document = GraphQL.parse(query_string) - # GraphQL::Language::Printer.new.print(document) - # # => "{ ... }" - # - # - # @example Building a custom printer - # - # class MyPrinter < GraphQL::Language::Printer - # def print_argument(arg) - # print_string("#{arg.name}: ") - # end - # end - # - # MyPrinter.new.print(document) - # # => "mutation { pay(creditCard: ) { success } }" - # - # @param node [Nodes::AbstractNode] - # @param indent [String] Whitespace to add to the printed node - # @param truncate_size [Integer, nil] The size to truncate to. - # @return [String] Valid GraphQL for `node` - def print(node, indent: "", truncate_size: nil) - truncate_size = truncate_size ? [truncate_size - OMISSION.size, 0].max : nil - @out = TruncatableBuffer.new(truncate_size: truncate_size) - print_node(node, indent: indent) - @out.to_string - rescue TruncatableBuffer::TruncateSizeReached - @out.to_string << OMISSION - end - - protected - - def print_string(str) - @out.append(str) - end - - def print_document(document) - document.definitions.each_with_index do |d, i| - print_node(d) - print_string("\n\n") if i < document.definitions.size - 1 - end - end - - def print_argument(argument) - print_string(argument.name) - print_string(": ") - print_node(argument.value) - end - - def print_input_object(input_object) - print_string("{") - input_object.arguments.each_with_index do |a, i| - print_argument(a) - print_string(", ") if i < input_object.arguments.size - 1 - end - print_string("}") - end - - def print_directive(directive) - print_string("@") - print_string(directive.name) - - if !directive.arguments.empty? - print_string("(") - directive.arguments.each_with_index do |a, i| - print_argument(a) - print_string(", ") if i < directive.arguments.size - 1 - end - print_string(")") - end - end - - def print_enum(enum) - print_string(enum.name) - end - - def print_null_value - print_string("null") - end - - def print_field(field, indent: "") - print_string(indent) - if field.alias - print_string(field.alias) - print_string(": ") - end - print_string(field.name) - if !field.arguments.empty? - print_string("(") - field.arguments.each_with_index do |a, i| - print_argument(a) - print_string(", ") if i < field.arguments.size - 1 - end - print_string(")") - end - print_directives(field.directives) - print_selections(field.selections, indent: indent) - end - - def print_fragment_definition(fragment_def, indent: "") - print_string(indent) - print_string("fragment") - if fragment_def.name - print_string(" ") - print_string(fragment_def.name) - end - - if fragment_def.type - print_string(" on ") - print_node(fragment_def.type) - end - print_directives(fragment_def.directives) - print_selections(fragment_def.selections, indent: indent) - end - - def print_fragment_spread(fragment_spread, indent: "") - print_string(indent) - print_string("...") - print_string(fragment_spread.name) - print_directives(fragment_spread.directives) - end - - def print_inline_fragment(inline_fragment, indent: "") - print_string(indent) - print_string("...") - if inline_fragment.type - print_string(" on ") - print_node(inline_fragment.type) - end - print_directives(inline_fragment.directives) - print_selections(inline_fragment.selections, indent: indent) - end - - def print_list_type(list_type) - print_string("[") - print_node(list_type.of_type) - print_string("]") - end - - def print_non_null_type(non_null_type) - print_node(non_null_type.of_type) - print_string("!") - end - - def print_operation_definition(operation_definition, indent: "") - print_string(indent) - print_string(operation_definition.operation_type) - if operation_definition.name - print_string(" ") - print_string(operation_definition.name) - end - - if !operation_definition.variables.empty? - print_string("(") - operation_definition.variables.each_with_index do |v, i| - print_variable_definition(v) - print_string(", ") if i < operation_definition.variables.size - 1 - end - print_string(")") - end - - print_directives(operation_definition.directives) - print_selections(operation_definition.selections, indent: indent) - end - - def print_type_name(type_name) - print_string(type_name.name) - end - - def print_variable_definition(variable_definition) - print_string("$") - print_string(variable_definition.name) - print_string(": ") - print_node(variable_definition.type) - unless variable_definition.default_value.nil? - print_string(" = ") - print_node(variable_definition.default_value) - end - variable_definition.directives.each do |dir| - print_string(" ") - print_directive(dir) - end - end - - def print_variable_identifier(variable_identifier) - print_string("$") - print_string(variable_identifier.name) - end - - def print_schema_definition(schema, extension: false) - has_conventional_names = (schema.query.nil? || schema.query == 'Query') && - (schema.mutation.nil? || schema.mutation == 'Mutation') && - (schema.subscription.nil? || schema.subscription == 'Subscription') - - if has_conventional_names && schema.directives.empty? - return - end - - extension ? print_string("extend schema") : print_string("schema") - - if !schema.directives.empty? - schema.directives.each do |dir| - print_string("\n ") - print_node(dir) - end - - if !has_conventional_names - print_string("\n") - end - end - - if !has_conventional_names - if schema.directives.empty? - print_string(" ") - end - print_string("{\n") - print_string(" query: #{schema.query}\n") if schema.query - print_string(" mutation: #{schema.mutation}\n") if schema.mutation - print_string(" subscription: #{schema.subscription}\n") if schema.subscription - print_string("}") - end - end - - - def print_scalar_type_definition(scalar_type, extension: false) - extension ? print_string("extend ") : print_description_and_comment(scalar_type) - print_string("scalar ") - print_string(scalar_type.name) - print_directives(scalar_type.directives) - end - - def print_object_type_definition(object_type, extension: false) - extension ? print_string("extend ") : print_description_and_comment(object_type) - print_string("type ") - print_string(object_type.name) - print_implements(object_type) unless object_type.interfaces.empty? - print_directives(object_type.directives) - print_field_definitions(object_type.fields) - end - - def print_implements(type) - print_string(" implements ") - i = 0 - type.interfaces.each do |int| - if i > 0 - print_string(" & ") - end - print_string(int.name) - i += 1 - end - end - - def print_input_value_definition(input_value) - print_string(input_value.name) - print_string(": ") - print_node(input_value.type) - unless input_value.default_value.nil? - print_string(" = ") - print_node(input_value.default_value) - end - print_directives(input_value.directives) - end - - def print_arguments(arguments, indent: "") - if arguments.all? { |arg| !arg.description && !arg.comment } - print_string("(") - arguments.each_with_index do |arg, i| - print_input_value_definition(arg) - print_string(", ") if i < arguments.size - 1 - end - print_string(")") - return - end - - print_string("(\n") - arguments.each_with_index do |arg, i| - print_comment(arg, indent: " " + indent, first_in_block: i == 0) - print_description(arg, indent: " " + indent, first_in_block: i == 0) - print_string(" ") - print_string(indent) - print_input_value_definition(arg) - print_string("\n") if i < arguments.size - 1 - end - print_string("\n") - print_string(indent) - print_string(")") - end - - def print_field_definition(field) - print_string(field.name) - unless field.arguments.empty? - print_arguments(field.arguments, indent: " ") - end - print_string(": ") - print_node(field.type) - print_directives(field.directives) - end - - def print_interface_type_definition(interface_type, extension: false) - extension ? print_string("extend ") : print_description_and_comment(interface_type) - print_string("interface ") - print_string(interface_type.name) - print_implements(interface_type) if !interface_type.interfaces.empty? - print_directives(interface_type.directives) - print_field_definitions(interface_type.fields) - end - - def print_union_type_definition(union_type, extension: false) - extension ? print_string("extend ") : print_description_and_comment(union_type) - print_string("union ") - print_string(union_type.name) - print_directives(union_type.directives) - if !union_type.types.empty? - print_string(" = ") - i = 0 - union_type.types.each do |t| - if i > 0 - print_string(" | ") - end - print_string(t.name) - i += 1 - end - end - end - - def print_enum_type_definition(enum_type, extension: false) - extension ? print_string("extend ") : print_description_and_comment(enum_type) - print_string("enum ") - print_string(enum_type.name) - print_directives(enum_type.directives) - if !enum_type.values.empty? - print_string(" {\n") - enum_type.values.each.with_index do |value, i| - print_description(value, indent: " ", first_in_block: i == 0) - print_comment(value, indent: " ", first_in_block: i == 0) - print_enum_value_definition(value) - end - print_string("}") - end - end - - def print_enum_value_definition(enum_value) - print_string(" ") - print_string(enum_value.name) - print_directives(enum_value.directives) - print_string("\n") - end - - def print_input_object_type_definition(input_object_type, extension: false) - extension ? print_string("extend ") : print_description_and_comment(input_object_type) - print_string("input ") - print_string(input_object_type.name) - print_directives(input_object_type.directives) - if !input_object_type.fields.empty? - print_string(" {\n") - input_object_type.fields.each.with_index do |field, i| - print_description(field, indent: " ", first_in_block: i == 0) - print_comment(field, indent: " ", first_in_block: i == 0) - print_string(" ") - print_input_value_definition(field) - print_string("\n") - end - print_string("}") - end - end - - def print_directive_definition(directive) - print_description(directive) - print_string("directive @") - print_string(directive.name) - - if !directive.arguments.empty? - print_arguments(directive.arguments) - end - - if directive.repeatable - print_string(" repeatable") - end - - print_string(" on ") - i = 0 - directive.locations.each do |loc| - if i > 0 - print_string(" | ") - end - print_string(loc.name) - i += 1 - end - end - - def print_description(node, indent: "", first_in_block: true) - return unless node.description - - print_string("\n") if indent != "" && !first_in_block - print_string(GraphQL::Language::BlockString.print(node.description, indent: indent)) - end - - def print_comment(node, indent: "", first_in_block: true) - return unless node.comment - - print_string("\n") if indent != "" && !first_in_block - print_string(GraphQL::Language::Comment.print(node.comment, indent: indent)) - end - - def print_description_and_comment(node) - print_description(node) - print_comment(node) - end - - def print_field_definitions(fields) - return if fields.empty? - - print_string(" {\n") - i = 0 - fields.each do |field| - print_description(field, indent: " ", first_in_block: i == 0) - print_comment(field, indent: " ", first_in_block: i == 0) - print_string(" ") - print_field_definition(field) - print_string("\n") - i += 1 - end - print_string("}") - end - - def print_directives(directives) - return if directives.empty? - - directives.each do |d| - print_string(" ") - print_directive(d) - end - end - - def print_selections(selections, indent: "") - return if selections.empty? - - print_string(" {\n") - selections.each do |selection| - print_node(selection, indent: indent + " ") - print_string("\n") - end - print_string(indent) - print_string("}") - end - - def print_node(node, indent: "") - case node - when Nodes::Document - print_document(node) - when Nodes::Argument - print_argument(node) - when Nodes::Directive - print_directive(node) - when Nodes::Enum - print_enum(node) - when Nodes::NullValue - print_null_value - when Nodes::Field - print_field(node, indent: indent) - when Nodes::FragmentDefinition - print_fragment_definition(node, indent: indent) - when Nodes::FragmentSpread - print_fragment_spread(node, indent: indent) - when Nodes::InlineFragment - print_inline_fragment(node, indent: indent) - when Nodes::InputObject - print_input_object(node) - when Nodes::ListType - print_list_type(node) - when Nodes::NonNullType - print_non_null_type(node) - when Nodes::OperationDefinition - print_operation_definition(node, indent: indent) - when Nodes::TypeName - print_type_name(node) - when Nodes::VariableDefinition - print_variable_definition(node) - when Nodes::VariableIdentifier - print_variable_identifier(node) - when Nodes::SchemaDefinition - print_schema_definition(node) - when Nodes::SchemaExtension - print_schema_definition(node, extension: true) - when Nodes::ScalarTypeDefinition - print_scalar_type_definition(node) - when Nodes::ScalarTypeExtension - print_scalar_type_definition(node, extension: true) - when Nodes::ObjectTypeDefinition - print_object_type_definition(node) - when Nodes::ObjectTypeExtension - print_object_type_definition(node, extension: true) - when Nodes::InputValueDefinition - print_input_value_definition(node) - when Nodes::FieldDefinition - print_field_definition(node) - when Nodes::InterfaceTypeDefinition - print_interface_type_definition(node) - when Nodes::InterfaceTypeExtension - print_interface_type_definition(node, extension: true) - when Nodes::UnionTypeDefinition - print_union_type_definition(node) - when Nodes::UnionTypeExtension - print_union_type_definition(node, extension: true) - when Nodes::EnumTypeDefinition - print_enum_type_definition(node) - when Nodes::EnumTypeExtension - print_enum_type_definition(node, extension: true) - when Nodes::EnumValueDefinition - print_enum_value_definition(node) - when Nodes::InputObjectTypeDefinition - print_input_object_type_definition(node) - when Nodes::InputObjectTypeExtension - print_input_object_type_definition(node, extension: true) - when Nodes::DirectiveDefinition - print_directive_definition(node) - when FalseClass, Float, Integer, NilClass, String, TrueClass, Symbol - print_string(GraphQL::Language.serialize(node)) - when Array - print_string("[") - node.each_with_index do |v, i| - print_node(v) - print_string(", ") if i < node.length - 1 - end - print_string("]") - when Hash - print_string("{") - node.each_with_index do |(k, v), i| - print_string(k) - print_string(": ") - print_node(v) - print_string(", ") if i < node.length - 1 - end - print_string("}") - else - print_string(GraphQL::Language.serialize(node.to_s)) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/sanitized_printer.rb b/vendor/gems/graphql/lib/graphql/language/sanitized_printer.rb deleted file mode 100644 index 82d576ff63d..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/sanitized_printer.rb +++ /dev/null @@ -1,220 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - # A custom printer used to print sanitized queries. It inlines provided variables - # within the query for facilitate logging and analysis of queries. - # - # The printer returns `nil` if the query is invalid. - # - # Since the GraphQL Ruby AST for a GraphQL query doesnt contain any reference - # on the type of fields or arguments, we have to track the current object, field - # and input type while printing the query. - # - # @example Printing a scrubbed string - # printer = QueryPrinter.new(query) - # puts printer.sanitized_query_string - # - # @see {Query#sanitized_query_string} - class SanitizedPrinter < GraphQL::Language::Printer - - REDACTED = "\"\"" - - def initialize(query, inline_variables: true) - @query = query - @current_type = nil - @current_field = nil - @current_input_type = nil - @inline_variables = inline_variables - end - - # @return [String, nil] A scrubbed query string, if the query was valid. - def sanitized_query_string - if query.valid? - print(query.document) - else - nil - end - end - - def print_node(node, indent: "") - case node - when FalseClass, Float, Integer, String, TrueClass - if @current_argument && redact_argument_value?(@current_argument, node) - print_string(redacted_argument_value(@current_argument)) - else - super - end - when Array - old_input_type = @current_input_type - if @current_input_type && @current_input_type.list? - @current_input_type = @current_input_type.of_type - @current_input_type = @current_input_type.of_type if @current_input_type.non_null? - end - - super - @current_input_type = old_input_type - else - super - end - end - - # Indicates whether or not to redact non-null values for the given argument. Defaults to redacting all strings - # arguments but this can be customized by subclasses. - def redact_argument_value?(argument, value) - # Default to redacting any strings or custom scalars encoded as strings - type = argument.type.unwrap - value.is_a?(String) && type.kind.scalar? && (type.graphql_name == "String" || !type.default_scalar?) - end - - # Returns the value to use for redacted versions of the given argument. Defaults to the - # string "". - def redacted_argument_value(argument) - REDACTED - end - - def print_argument(argument) - # We won't have type information if we're recursing into a custom scalar - return super if @current_input_type && @current_input_type.kind.scalar? - - arg_owner = @current_input_type || @current_directive || @current_field - old_current_argument = @current_argument - @current_argument = arg_owner.get_argument(argument.name, @query.context) - - old_input_type = @current_input_type - @current_input_type = @current_argument.type.non_null? ? @current_argument.type.of_type : @current_argument.type - - argument_value = if coerce_argument_value_to_list?(@current_input_type, argument.value) - [argument.value] - else - argument.value - end - - print_string("#{argument.name}: ") - print_node(argument_value) - - @current_input_type = old_input_type - @current_argument = old_current_argument - end - - def coerce_argument_value_to_list?(type, value) - type.list? && - !value.is_a?(Array) && - !value.nil? && - !value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) - end - - def print_variable_identifier(variable_id) - if @inline_variables - variable_value = query.variables[variable_id.name] - print_node(value_to_ast(variable_value, @current_input_type)) - else - super - end - end - - def print_field(field, indent: "") - @current_field = query.types.field(@current_type, field.name) - old_type = @current_type - @current_type = @current_field.type.unwrap - super - @current_type = old_type - end - - def print_inline_fragment(inline_fragment, indent: "") - old_type = @current_type - - if inline_fragment.type - @current_type = query.get_type(inline_fragment.type.name) - end - - super - - @current_type = old_type - end - - def print_fragment_definition(fragment_def, indent: "") - old_type = @current_type - @current_type = query.get_type(fragment_def.type.name) - - super - - @current_type = old_type - end - - def print_directive(directive) - @current_directive = query.schema.directives[directive.name] - - super - - @current_directive = nil - end - - # Print the operation definition but do not include the variable - # definitions since we will inline them within the query - def print_operation_definition(operation_definition, indent: "") - old_type = @current_type - @current_type = query.schema.public_send(operation_definition.operation_type) - - if @inline_variables - print_string("#{indent}#{operation_definition.operation_type}") - print_string(" #{operation_definition.name}") if operation_definition.name - print_directives(operation_definition.directives) - print_selections(operation_definition.selections, indent: indent) - else - super - end - - @current_type = old_type - end - - private - - def value_to_ast(value, type) - type = type.of_type if type.non_null? - - if value.nil? - return GraphQL::Language::Nodes::NullValue.new(name: "null") - end - - case type.kind.name - when "INPUT_OBJECT" - value = if value.respond_to?(:to_unsafe_h) - # for ActionController::Parameters - value.to_unsafe_h - else - value.to_h - end - - arguments = value.map do |key, val| - sub_type = type.get_argument(key.to_s, @query.context).type - - GraphQL::Language::Nodes::Argument.new( - name: key.to_s, - value: value_to_ast(val, sub_type) - ) - end - GraphQL::Language::Nodes::InputObject.new( - arguments: arguments - ) - when "LIST" - if value.is_a?(Array) - value.map { |v| value_to_ast(v, type.of_type) } - else - [value].map { |v| value_to_ast(v, type.of_type) } - end - when "ENUM" - if value.is_a?(GraphQL::Language::Nodes::Enum) - # if it was a default value, it's already wrapped - value - else - GraphQL::Language::Nodes::Enum.new(name: value) - end - else - value - end - end - - attr_reader :query - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/static_visitor.rb b/vendor/gems/graphql/lib/graphql/language/static_visitor.rb deleted file mode 100644 index 6a7dfcbf425..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/static_visitor.rb +++ /dev/null @@ -1,171 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - # Like `GraphQL::Language::Visitor` except it doesn't support - # making changes to the document -- only visiting it as-is. - class StaticVisitor - def initialize(document) - @document = document - end - - # Visit `document` and all children - # @return [void] - def visit - # `@document` may be any kind of node: - visit_method = @document.visit_method - result = public_send(visit_method, @document, nil) - @result = if result.is_a?(Array) - result.first - else - # The node wasn't modified - @document - end - end - - def on_document_children(document_node) - document_node.children.each do |child_node| - visit_method = child_node.visit_method - public_send(visit_method, child_node, document_node) - end - end - - def on_field_children(new_node) - new_node.arguments.each do |arg_node| # rubocop:disable Development/ContextIsPassedCop - on_argument(arg_node, new_node) - end - visit_directives(new_node) - visit_selections(new_node) - end - - def visit_directives(new_node) - new_node.directives.each do |dir_node| - on_directive(dir_node, new_node) - end - end - - def visit_selections(new_node) - new_node.selections.each do |selection| - case selection - when GraphQL::Language::Nodes::Field - on_field(selection, new_node) - when GraphQL::Language::Nodes::InlineFragment - on_inline_fragment(selection, new_node) - when GraphQL::Language::Nodes::FragmentSpread - on_fragment_spread(selection, new_node) - else - raise ArgumentError, "Invariant: unexpected field selection #{selection.class} (#{selection.inspect})" - end - end - end - - def on_fragment_definition_children(new_node) - visit_directives(new_node) - visit_selections(new_node) - end - - alias :on_inline_fragment_children :on_fragment_definition_children - - def on_operation_definition_children(new_node) - new_node.variables.each do |arg_node| - on_variable_definition(arg_node, new_node) - end - visit_directives(new_node) - visit_selections(new_node) - end - - def on_argument_children(new_node) - new_node.children.each do |value_node| - case value_node - when Language::Nodes::VariableIdentifier - on_variable_identifier(value_node, new_node) - when Language::Nodes::InputObject - on_input_object(value_node, new_node) - when Language::Nodes::Enum - on_enum(value_node, new_node) - when Language::Nodes::NullValue - on_null_value(value_node, new_node) - else - raise ArgumentError, "Invariant: unexpected argument value node #{value_node.class} (#{value_node.inspect})" - end - end - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - # We don't use `alias` here because it breaks `super` - def self.make_visit_methods(ast_node_class) - node_method = ast_node_class.visit_method - children_of_type = ast_node_class.children_of_type - child_visit_method = :"#{node_method}_children" - - class_eval(<<-RUBY, __FILE__, __LINE__ + 1) - # The default implementation for visiting an AST node. - # It doesn't _do_ anything, but it continues to visiting the node's children. - # To customize this hook, override one of its make_visit_methods (or the base method?) - # in your subclasses. - # - # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited - # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node. - # @return [void] - def #{node_method}(node, parent) - #{ - if method_defined?(child_visit_method) - "#{child_visit_method}(node)" - elsif children_of_type - children_of_type.map do |child_accessor, child_class| - "node.#{child_accessor}.each do |child_node| - #{child_class.visit_method}(child_node, node) - end" - end.join("\n") - else - "" - end - } - end - RUBY - end - - [ - Language::Nodes::Argument, - Language::Nodes::Directive, - Language::Nodes::DirectiveDefinition, - Language::Nodes::DirectiveLocation, - Language::Nodes::Document, - Language::Nodes::Enum, - Language::Nodes::EnumTypeDefinition, - Language::Nodes::EnumTypeExtension, - Language::Nodes::EnumValueDefinition, - Language::Nodes::Field, - Language::Nodes::FieldDefinition, - Language::Nodes::FragmentDefinition, - Language::Nodes::FragmentSpread, - Language::Nodes::InlineFragment, - Language::Nodes::InputObject, - Language::Nodes::InputObjectTypeDefinition, - Language::Nodes::InputObjectTypeExtension, - Language::Nodes::InputValueDefinition, - Language::Nodes::InterfaceTypeDefinition, - Language::Nodes::InterfaceTypeExtension, - Language::Nodes::ListType, - Language::Nodes::NonNullType, - Language::Nodes::NullValue, - Language::Nodes::ObjectTypeDefinition, - Language::Nodes::ObjectTypeExtension, - Language::Nodes::OperationDefinition, - Language::Nodes::ScalarTypeDefinition, - Language::Nodes::ScalarTypeExtension, - Language::Nodes::SchemaDefinition, - Language::Nodes::SchemaExtension, - Language::Nodes::TypeName, - Language::Nodes::UnionTypeDefinition, - Language::Nodes::UnionTypeExtension, - Language::Nodes::VariableDefinition, - Language::Nodes::VariableIdentifier, - ].each do |ast_node_class| - make_visit_methods(ast_node_class) - end - - # rubocop:disable Development/NoEvalCop - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/language/visitor.rb b/vendor/gems/graphql/lib/graphql/language/visitor.rb deleted file mode 100644 index e2e842fb012..00000000000 --- a/vendor/gems/graphql/lib/graphql/language/visitor.rb +++ /dev/null @@ -1,293 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Language - # Depth-first traversal through the tree, calling hooks at each stop. - # - # @example Create a visitor counting certain field names - # class NameCounter < GraphQL::Language::Visitor - # def initialize(document, field_name) - # super(document) - # @field_name = field_name - # @count = 0 - # end - # - # attr_reader :count - # - # def on_field(node, parent) - # # if this field matches our search, increment the counter - # if node.name == @field_name - # @count += 1 - # end - # # Continue visiting subfields: - # super - # end - # end - # - # # Initialize a visitor - # visitor = NameCounter.new(document, "name") - # # Run it - # visitor.visit - # # Check the result - # visitor.count - # # => 3 - # - # @see GraphQL::Language::StaticVisitor for a faster visitor that doesn't support modifying the document - class Visitor - class DeleteNode; end - - # When this is returned from a visitor method, - # Then the `node` passed into the method is removed from `parent`'s children. - DELETE_NODE = DeleteNode.new - - def initialize(document) - @document = document - @result = nil - end - - # @return [GraphQL::Language::Nodes::Document] The document with any modifications applied - attr_reader :result - - # Visit `document` and all children - # @return [void] - def visit - # `@document` may be any kind of node: - visit_method = :"#{@document.visit_method}_with_modifications" - result = public_send(visit_method, @document, nil) - @result = if result.is_a?(Array) - result.first - else - # The node wasn't modified - @document - end - end - - def on_document_children(document_node) - new_node = document_node - document_node.children.each do |child_node| - visit_method = :"#{child_node.visit_method}_with_modifications" - new_child_and_node = public_send(visit_method, child_node, new_node) - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end - new_node - end - - def on_field_children(new_node) - new_node.arguments.each do |arg_node| # rubocop:disable Development/ContextIsPassedCop - new_child_and_node = on_argument_with_modifications(arg_node, new_node) - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end - new_node = visit_directives(new_node) - new_node = visit_selections(new_node) - new_node - end - - def visit_directives(new_node) - new_node.directives.each do |dir_node| - new_child_and_node = on_directive_with_modifications(dir_node, new_node) - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end - new_node - end - - def visit_selections(new_node) - new_node.selections.each do |selection| - new_child_and_node = case selection - when GraphQL::Language::Nodes::Field - on_field_with_modifications(selection, new_node) - when GraphQL::Language::Nodes::InlineFragment - on_inline_fragment_with_modifications(selection, new_node) - when GraphQL::Language::Nodes::FragmentSpread - on_fragment_spread_with_modifications(selection, new_node) - else - raise ArgumentError, "Invariant: unexpected field selection #{selection.class} (#{selection.inspect})" - end - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end - new_node - end - - def on_fragment_definition_children(new_node) - new_node = visit_directives(new_node) - new_node = visit_selections(new_node) - new_node - end - - alias :on_inline_fragment_children :on_fragment_definition_children - - def on_operation_definition_children(new_node) - new_node.variables.each do |arg_node| - new_child_and_node = on_variable_definition_with_modifications(arg_node, new_node) - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end - new_node = visit_directives(new_node) - new_node = visit_selections(new_node) - new_node - end - - def on_argument_children(new_node) - new_node.children.each do |value_node| - new_child_and_node = case value_node - when Language::Nodes::VariableIdentifier - on_variable_identifier_with_modifications(value_node, new_node) - when Language::Nodes::InputObject - on_input_object_with_modifications(value_node, new_node) - when Language::Nodes::Enum - on_enum_with_modifications(value_node, new_node) - when Language::Nodes::NullValue - on_null_value_with_modifications(value_node, new_node) - else - raise ArgumentError, "Invariant: unexpected argument value node #{value_node.class} (#{value_node.inspect})" - end - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end - new_node - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - # We don't use `alias` here because it breaks `super` - def self.make_visit_methods(ast_node_class) - node_method = ast_node_class.visit_method - children_of_type = ast_node_class.children_of_type - child_visit_method = :"#{node_method}_children" - - class_eval(<<-RUBY, __FILE__, __LINE__ + 1) - # The default implementation for visiting an AST node. - # It doesn't _do_ anything, but it continues to visiting the node's children. - # To customize this hook, override one of its make_visit_methods (or the base method?) - # in your subclasses. - # - # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited - # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node. - # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`. - def #{node_method}(node, parent) - if node.equal?(DELETE_NODE) - # This might be passed to `super(DELETE_NODE, ...)` - # by a user hook, don't want to keep visiting in that case. - [node, parent] - else - new_node = node - #{ - if method_defined?(child_visit_method) - "new_node = #{child_visit_method}(new_node)" - elsif children_of_type - children_of_type.map do |child_accessor, child_class| - "node.#{child_accessor}.each do |child_node| - new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node) - # Reassign `node` in case the child hook makes a modification - if new_child_and_node.is_a?(Array) - new_node = new_child_and_node[1] - end - end" - end.join("\n") - else - "" - end - } - - if new_node.equal?(node) - [node, parent] - else - [new_node, parent] - end - end - end - - def #{node_method}_with_modifications(node, parent) - new_node_and_new_parent = #{node_method}(node, parent) - apply_modifications(node, parent, new_node_and_new_parent) - end - RUBY - end - - [ - Language::Nodes::Argument, - Language::Nodes::Directive, - Language::Nodes::DirectiveDefinition, - Language::Nodes::DirectiveLocation, - Language::Nodes::Document, - Language::Nodes::Enum, - Language::Nodes::EnumTypeDefinition, - Language::Nodes::EnumTypeExtension, - Language::Nodes::EnumValueDefinition, - Language::Nodes::Field, - Language::Nodes::FieldDefinition, - Language::Nodes::FragmentDefinition, - Language::Nodes::FragmentSpread, - Language::Nodes::InlineFragment, - Language::Nodes::InputObject, - Language::Nodes::InputObjectTypeDefinition, - Language::Nodes::InputObjectTypeExtension, - Language::Nodes::InputValueDefinition, - Language::Nodes::InterfaceTypeDefinition, - Language::Nodes::InterfaceTypeExtension, - Language::Nodes::ListType, - Language::Nodes::NonNullType, - Language::Nodes::NullValue, - Language::Nodes::ObjectTypeDefinition, - Language::Nodes::ObjectTypeExtension, - Language::Nodes::OperationDefinition, - Language::Nodes::ScalarTypeDefinition, - Language::Nodes::ScalarTypeExtension, - Language::Nodes::SchemaDefinition, - Language::Nodes::SchemaExtension, - Language::Nodes::TypeName, - Language::Nodes::UnionTypeDefinition, - Language::Nodes::UnionTypeExtension, - Language::Nodes::VariableDefinition, - Language::Nodes::VariableIdentifier, - ].each do |ast_node_class| - make_visit_methods(ast_node_class) - end - - # rubocop:enable Development/NoEvalCop - - private - - def apply_modifications(node, parent, new_node_and_new_parent) - if new_node_and_new_parent.is_a?(Array) - new_node = new_node_and_new_parent[0] - new_parent = new_node_and_new_parent[1] - if new_node.is_a?(Nodes::AbstractNode) && !node.equal?(new_node) - # The user-provided hook returned a new node. - new_parent = new_parent && new_parent.replace_child(node, new_node) - return new_node, new_parent - elsif new_node.equal?(DELETE_NODE) - # The user-provided hook requested to remove this node - new_parent = new_parent && new_parent.delete_child(node) - return nil, new_parent - elsif new_node_and_new_parent.none? { |n| n == nil || n.class < Nodes::AbstractNode } - # The user-provided hook returned an array of who-knows-what - # return nil here to signify that no changes should be made - nil - else - new_node_and_new_parent - end - else - # The user-provided hook didn't make any modifications. - # In fact, the hook might have returned who-knows-what, so - # ignore the return value and use the original values. - new_node_and_new_parent - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/load_application_object_failed_error.rb b/vendor/gems/graphql/lib/graphql/load_application_object_failed_error.rb deleted file mode 100644 index 6546d5ba45b..00000000000 --- a/vendor/gems/graphql/lib/graphql/load_application_object_failed_error.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - # Raised when a argument is configured with `loads:` and the client provides an `ID`, - # but no object is loaded for that ID. - # - # @see GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader#load_application_object_failed, A hook which you can override in resolvers, mutations and input objects. - class LoadApplicationObjectFailedError < GraphQL::ExecutionError - # @return [GraphQL::Schema::Argument] the argument definition for the argument that was looked up - attr_reader :argument - # @return [String] The ID provided by the client - attr_reader :id - # @return [Object] The value found with this ID - attr_reader :object - # @return [GraphQL::Query::Context] - attr_reader :context - - def initialize(argument:, id:, object:, context:) - @id = id - @argument = argument - @object = object - @context = context - super("No object found for `#{argument.graphql_name}: #{id.inspect}`") - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/name_validator.rb b/vendor/gems/graphql/lib/graphql/name_validator.rb deleted file mode 100644 index a8584ffd967..00000000000 --- a/vendor/gems/graphql/lib/graphql/name_validator.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class NameValidator - VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/ - - def self.validate!(name) - name = name.is_a?(String) ? name : name.to_s - raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless name.match?(VALID_NAME_REGEX) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination.rb b/vendor/gems/graphql/lib/graphql/pagination.rb deleted file mode 100644 index 636223d9c1b..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -require "graphql/pagination/array_connection" -require "graphql/pagination/active_record_relation_connection" -require "graphql/pagination/connections" -require "graphql/pagination/mongoid_relation_connection" -require "graphql/pagination/sequel_dataset_connection" diff --git a/vendor/gems/graphql/lib/graphql/pagination/active_record_relation_connection.rb b/vendor/gems/graphql/lib/graphql/pagination/active_record_relation_connection.rb deleted file mode 100644 index 4c7d027d450..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/active_record_relation_connection.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true -require "graphql/pagination/relation_connection" - -module GraphQL - module Pagination - # Customizes `RelationConnection` to work with `ActiveRecord::Relation`s. - class ActiveRecordRelationConnection < Pagination::RelationConnection - private - - def relation_count(relation) - int_or_hash = if already_loaded?(relation) - relation.size - elsif relation.respond_to?(:unscope) - relation.unscope(:order).count(:all) - else - # Rails 3 - relation.count - end - if int_or_hash.is_a?(Integer) - int_or_hash - else - # Grouped relations return count-by-group hashes - int_or_hash.length - end - end - - def relation_limit(relation) - if relation.is_a?(Array) - nil - else - relation.limit_value - end - end - - def relation_offset(relation) - if relation.is_a?(Array) - nil - else - relation.offset_value - end - end - - def null_relation(relation) - if relation.respond_to?(:none) - relation.none - else - # Rails 3 - relation.where("1=2") - end - end - - def set_limit(nodes, limit) - if already_loaded?(nodes) - nodes.take(limit) - else - super - end - end - - def set_offset(nodes, offset) - if already_loaded?(nodes) - # If the client sent a bogus cursor beyond the size of the relation, - # it might get `nil` from `#[...]`, so return an empty array in that case - nodes[offset..-1] || [] - else - super - end - end - - private - - def already_loaded?(relation) - relation.is_a?(Array) || relation.loaded? - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination/array_connection.rb b/vendor/gems/graphql/lib/graphql/pagination/array_connection.rb deleted file mode 100644 index 4a4718c72fb..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/array_connection.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true -require "graphql/pagination/connection" - -module GraphQL - module Pagination - class ArrayConnection < Pagination::Connection - def nodes - load_nodes - @nodes - end - - def has_previous_page - load_nodes - @has_previous_page - end - - def has_next_page - load_nodes - @has_next_page - end - - def cursor_for(item) - idx = items.find_index(item) + 1 - encode(idx.to_s) - end - - private - - def index_from_cursor(cursor) - decode(cursor).to_i - end - - # Populate all the pagination info _once_, - # It doesn't do anything on subsequent calls. - def load_nodes - @nodes ||= begin - sliced_nodes = if before && after - end_idx = index_from_cursor(before) - 2 - end_idx < 0 ? [] : items[index_from_cursor(after)..end_idx] || [] - elsif before - end_idx = index_from_cursor(before) - 2 - end_idx < 0 ? [] : items[0..end_idx] || [] - elsif after - items[index_from_cursor(after)..-1] || [] - else - items - end - - @has_previous_page = if last - # There are items preceding the ones in this result - sliced_nodes.count > last - elsif after - # We've paginated into the Array a bit, there are some behind us - index_from_cursor(after) > 0 - else - false - end - - @has_next_page = if before - # The original array is longer than the `before` index - index_from_cursor(before) < items.length + 1 - elsif first - # There are more items after these items - sliced_nodes.count > first - else - false - end - - limited_nodes = sliced_nodes - - limited_nodes = limited_nodes.first(first) if first - limited_nodes = limited_nodes.last(last) if last - - limited_nodes - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination/connection.rb b/vendor/gems/graphql/lib/graphql/pagination/connection.rb deleted file mode 100644 index e22c6256197..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/connection.rb +++ /dev/null @@ -1,280 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Pagination - # A Connection wraps a list of items and provides cursor-based pagination over it. - # - # Connections were introduced by Facebook's `Relay` front-end framework, but - # proved to be generally useful for GraphQL APIs. When in doubt, use connections - # to serve lists (like Arrays, ActiveRecord::Relations) via GraphQL. - # - # Unlike the previous connection implementation, these default to bidirectional pagination. - # - # Pagination arguments and context may be provided at initialization or assigned later (see {Schema::Field::ConnectionExtension}). - class Connection - class PaginationImplementationMissingError < GraphQL::Error - end - - # @return [Object] A list object, from the application. This is the unpaginated value passed into the connection. - attr_reader :items - - # @return [GraphQL::Query::Context] - attr_reader :context - - def context=(new_ctx) - @context = new_ctx - if @was_authorized_by_scope_items.nil? - @was_authorized_by_scope_items = detect_was_authorized_by_scope_items - end - @context - end - - # @return [Object] the object this collection belongs to - attr_accessor :parent - - # Raw access to client-provided values. (`max_page_size` not applied to first or last.) - attr_accessor :before_value, :after_value, :first_value, :last_value - - # @return [String, nil] the client-provided cursor. `""` is treated as `nil`. - def before - if defined?(@before) - @before - else - @before = @before_value == "" ? nil : @before_value - end - end - - # @return [String, nil] the client-provided cursor. `""` is treated as `nil`. - def after - if defined?(@after) - @after - else - @after = @after_value == "" ? nil : @after_value - end - end - - # @return [Hash Object>] The field arguments from the field that returned this connection - attr_accessor :arguments - - # @param items [Object] some unpaginated collection item, like an `Array` or `ActiveRecord::Relation` - # @param context [Query::Context] - # @param parent [Object] The object this collection belongs to - # @param first [Integer, nil] The limit parameter from the client, if it provided one - # @param after [String, nil] A cursor for pagination, if the client provided one - # @param last [Integer, nil] Limit parameter from the client, if provided - # @param before [String, nil] A cursor for pagination, if the client provided one. - # @param arguments [Hash] The arguments to the field that returned the collection wrapped by this connection - # @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given and no `default_page_size` is set. - # @param default_page_size [Integer, nil] A configured value to determine the result size when neither first or last are given. - def initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, last: nil, before: nil, edge_class: nil, arguments: nil) - @items = items - @parent = parent - @context = context - @field = field - @first_value = first - @after_value = after - @last_value = last - @before_value = before - @arguments = arguments - @edge_class = edge_class || self.class::Edge - # This is only true if the object was _initialized_ with an override - # or if one is assigned later. - @has_max_page_size_override = max_page_size != NOT_CONFIGURED - @max_page_size = if max_page_size == NOT_CONFIGURED - nil - else - max_page_size - end - @has_default_page_size_override = default_page_size != NOT_CONFIGURED - @default_page_size = if default_page_size == NOT_CONFIGURED - nil - else - default_page_size - end - @was_authorized_by_scope_items = detect_was_authorized_by_scope_items - end - - def was_authorized_by_scope_items? - @was_authorized_by_scope_items - end - - def max_page_size=(new_value) - @has_max_page_size_override = true - @max_page_size = new_value - end - - def max_page_size - if @has_max_page_size_override - @max_page_size - else - context.schema.default_max_page_size - end - end - - def has_max_page_size_override? - @has_max_page_size_override - end - - def default_page_size=(new_value) - @has_default_page_size_override = true - @default_page_size = new_value - end - - def default_page_size - if @has_default_page_size_override - @default_page_size - else - context.schema.default_page_size - end - end - - def has_default_page_size_override? - @has_default_page_size_override - end - - attr_writer :first - # @return [Integer, nil] - # A clamped `first` value. - # (The underlying instance variable doesn't have limits on it.) - # If neither `first` nor `last` is given, but `default_page_size` is - # present, default_page_size is used for first. If `default_page_size` - # is greater than `max_page_size``, it'll be clamped down to - # `max_page_size`. If `default_page_size` is nil, use `max_page_size`. - def first - @first ||= begin - capped = limit_pagination_argument(@first_value, max_page_size) - if capped.nil? && last.nil? - capped = limit_pagination_argument(default_page_size, max_page_size) || max_page_size - end - capped - end - end - - # This is called by `Relay::RangeAdd` -- it can be overridden - # when `item` needs some modifications based on this connection's state. - # - # @param item [Object] An item newly added to `items` - # @return [Edge] - def range_add_edge(item) - edge_class.new(item, self) - end - - attr_writer :last - # @return [Integer, nil] A clamped `last` value. (The underlying instance variable doesn't have limits on it) - def last - @last ||= limit_pagination_argument(@last_value, max_page_size) - end - - # @return [Array] {nodes}, but wrapped with Edge instances - def edges - @edges ||= nodes.map { |n| @edge_class.new(n, self) } - end - - # @return [Class] A wrapper class for edges of this connection - attr_accessor :edge_class - - # @return [GraphQL::Schema::Field] The field this connection was returned by - attr_accessor :field - - # @return [Array] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value} - def nodes - raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`" - end - - # A dynamic alias for compatibility with {Relay::BaseConnection}. - # @deprecated use {#nodes} instead - def edge_nodes - nodes - end - - # The connection object itself implements `PageInfo` fields - def page_info - self - end - - # @return [Boolean] True if there are more items after this page - def has_next_page - raise PaginationImplementationMissingError, "Implement #{self.class}#has_next_page to return the next-page check" - end - - # @return [Boolean] True if there were items before these items - def has_previous_page - raise PaginationImplementationMissingError, "Implement #{self.class}#has_previous_page to return the previous-page check" - end - - # @return [String] The cursor of the first item in {nodes} - def start_cursor - nodes.first && cursor_for(nodes.first) - end - - # @return [String] The cursor of the last item in {nodes} - def end_cursor - nodes.last && cursor_for(nodes.last) - end - - # Return a cursor for this item. - # @param item [Object] one of the passed in {items}, taken from {nodes} - # @return [String] - def cursor_for(item) - raise PaginationImplementationMissingError, "Implement #{self.class}#cursor_for(item) to return the cursor for #{item.inspect}" - end - - private - - def detect_was_authorized_by_scope_items - if @context && - (current_runtime_state = Fiber[:__graphql_runtime_info]) && - (query_runtime_state = current_runtime_state[@context.query]) - query_runtime_state.was_authorized_by_scope_items - else - nil - end - end - - # @param argument [nil, Integer] `first` or `last`, as provided by the client - # @param max_page_size [nil, Integer] - # @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size` - def limit_pagination_argument(argument, max_page_size) - if argument - if argument < 0 - argument = 0 - elsif max_page_size && argument > max_page_size - argument = max_page_size - end - end - argument - end - - def decode(cursor) - context.schema.cursor_encoder.decode(cursor, nonce: true) - end - - def encode(cursor) - context.schema.cursor_encoder.encode(cursor, nonce: true) - end - - # A wrapper around paginated items. It includes a {cursor} for pagination - # and could be extended with custom relationship-level data. - class Edge - attr_reader :node - - def initialize(node, connection) - @connection = connection - @node = node - end - - def parent - @connection.parent - end - - def cursor - @cursor ||= @connection.cursor_for(@node) - end - - def was_authorized_by_scope_items? - @connection.was_authorized_by_scope_items? - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination/connections.rb b/vendor/gems/graphql/lib/graphql/pagination/connections.rb deleted file mode 100644 index 00a144b0002..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/connections.rb +++ /dev/null @@ -1,135 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Pagination - # A schema-level connection wrapper manager. - # - # Attach as a plugin. - # - # @example Adding a custom wrapper - # class MySchema < GraphQL::Schema - # connections.add(MyApp::SearchResults, MyApp::SearchResultsConnection) - # end - # - # @example Removing default connection support for arrays (they can still be manually wrapped) - # class MySchema < GraphQL::Schema - # connections.delete(Array) - # end - # - # @see {Schema.connections} - class Connections - class ImplementationMissingError < GraphQL::Error - end - - def initialize(schema:) - @schema = schema - @wrappers = {} - add_default - end - - def add(nodes_class, implementation) - @wrappers[nodes_class] = implementation - end - - def delete(nodes_class) - @wrappers.delete(nodes_class) - end - - def all_wrappers - all_wrappers = {} - @schema.ancestors.reverse_each do |schema_class| - if schema_class.respond_to?(:connections) && (c = schema_class.connections) - all_wrappers.merge!(c.wrappers) - end - end - all_wrappers - end - - def wrapper_for(items, wrappers: all_wrappers) - impl = nil - - items.class.ancestors.each { |cls| - impl = wrappers[cls] - break if impl - } - - impl - end - - # Used by the runtime to wrap values in connection wrappers. - # @api Private - def wrap(field, parent, items, arguments, context) - return items if GraphQL::Execution::Interpreter::RawValue === items - wrappers = context ? context.namespace(:connections)[:all_wrappers] : all_wrappers - impl = wrapper_for(items, wrappers: wrappers) - - if impl - impl.new( - items, - context: context, - parent: parent, - field: field, - max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size, - default_page_size: field.has_default_page_size? ? field.default_page_size : context.schema.default_page_size, - first: arguments[:first], - after: arguments[:after], - last: arguments[:last], - before: arguments[:before], - arguments: arguments, - edge_class: edge_class_for_field(field), - ) - else - raise ImplementationMissingError, "Couldn't find a connection wrapper for #{items.class} during #{field.path} (#{items.inspect})" - end - end - - # use an override if there is one - # @api private - def edge_class_for_field(field) - conn_type = field.type.unwrap - conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class - if conn_type_edge_type && conn_type_edge_type != Pagination::Connection::Edge - conn_type_edge_type - else - nil - end - end - protected - - attr_reader :wrappers - - private - - def add_default - add(Array, Pagination::ArrayConnection) - - if defined?(ActiveRecord::Relation) - add(ActiveRecord::Relation, Pagination::ActiveRecordRelationConnection) - end - - if defined?(Sequel::Dataset) - add(Sequel::Dataset, Pagination::SequelDatasetConnection) - end - - if defined?(Mongoid::Criteria) - add(Mongoid::Criteria, Pagination::MongoidRelationConnection) - end - - # Mongoid 5 and 6 - if defined?(Mongoid::Relations::Targets::Enumerable) - add(Mongoid::Relations::Targets::Enumerable, Pagination::MongoidRelationConnection) - end - - # Mongoid 7 - if defined?(Mongoid::Association::Referenced::HasMany::Targets::Enumerable) - add(Mongoid::Association::Referenced::HasMany::Targets::Enumerable, Pagination::MongoidRelationConnection) - end - - # Mongoid 7.3+ - if defined?(Mongoid::Association::Referenced::HasMany::Enumerable) - add(Mongoid::Association::Referenced::HasMany::Enumerable, Pagination::MongoidRelationConnection) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination/mongoid_relation_connection.rb b/vendor/gems/graphql/lib/graphql/pagination/mongoid_relation_connection.rb deleted file mode 100644 index 29d92642049..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/mongoid_relation_connection.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -require "graphql/pagination/relation_connection" - -module GraphQL - module Pagination - class MongoidRelationConnection < Pagination::RelationConnection - def relation_offset(relation) - relation.options.skip - end - - def relation_limit(relation) - relation.options.limit - end - - def relation_count(relation) - relation.all.count(relation.options.slice(:limit, :skip)) - end - - def null_relation(relation) - relation.without_options.none - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination/relation_connection.rb b/vendor/gems/graphql/lib/graphql/pagination/relation_connection.rb deleted file mode 100644 index 0e6b091474f..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/relation_connection.rb +++ /dev/null @@ -1,228 +0,0 @@ -# frozen_string_literal: true -require "graphql/pagination/connection" - -module GraphQL - module Pagination - # A generic class for working with database query objects. - class RelationConnection < Pagination::Connection - def nodes - load_nodes - @nodes - end - - def has_previous_page - if @has_previous_page.nil? - @has_previous_page = if after_offset && after_offset > 0 - true - elsif last - # See whether there are any nodes _before_ the current offset. - # If there _is no_ current offset, then there can't be any nodes before it. - # Assume that if the offset is positive, there are nodes before the offset. - limited_nodes - !(@paged_nodes_offset.nil? || @paged_nodes_offset == 0) - else - false - end - end - @has_previous_page - end - - def has_next_page - if @has_next_page.nil? - @has_next_page = if before_offset && before_offset > 0 - true - elsif first - if @nodes && @nodes.count < first - false - else - relation_larger_than(sliced_nodes, @sliced_nodes_offset, first) - end - else - false - end - end - @has_next_page - end - - def cursor_for(item) - load_nodes - # index in nodes + existing offset + 1 (because it's offset, not index) - offset = nodes.index(item) + 1 + (@paged_nodes_offset || 0) - (relation_offset(items) || 0) - encode(offset.to_s) - end - - private - - # @param relation [Object] A database query object - # @param _initial_offset [Integer] The number of items already excluded from the relation - # @param size [Integer] The value against which we check the relation size - # @return [Boolean] True if the number of items in this relation is larger than `size` - def relation_larger_than(relation, _initial_offset, size) - relation_count(set_limit(relation, size + 1)) == size + 1 - end - - # @param relation [Object] A database query object - # @return [Integer, nil] The offset value, or nil if there isn't one - def relation_offset(relation) - raise "#{self.class}#relation_offset(relation) must return the offset value for a #{relation.class} (#{relation.inspect})" - end - - # @param relation [Object] A database query object - # @return [Integer, nil] The limit value, or nil if there isn't one - def relation_limit(relation) - raise "#{self.class}#relation_limit(relation) must return the limit value for a #{relation.class} (#{relation.inspect})" - end - - # @param relation [Object] A database query object - # @return [Integer, nil] The number of items in this relation (hopefully determined without loading all records into memory!) - def relation_count(relation) - raise "#{self.class}#relation_count(relation) must return the count of records for a #{relation.class} (#{relation.inspect})" - end - - # @param relation [Object] A database query object - # @return [Object] A modified query object which will return no records - def null_relation(relation) - raise "#{self.class}#null_relation(relation) must return an empty relation for a #{relation.class} (#{relation.inspect})" - end - - # @return [Integer] - def offset_from_cursor(cursor) - decode(cursor).to_i - end - - # Abstract this operation so we can always ignore inputs less than zero. - # (Sequel doesn't like it, understandably.) - def set_offset(relation, offset_value) - if offset_value >= 0 - relation.offset(offset_value) - else - relation.offset(0) - end - end - - # Abstract this operation so we can always ignore inputs less than zero. - # (Sequel doesn't like it, understandably.) - def set_limit(relation, limit_value) - if limit_value > 0 - relation.limit(limit_value) - elsif limit_value == 0 - null_relation(relation) - else - relation - end - end - - def calculate_sliced_nodes_parameters - if defined?(@sliced_nodes_limit) - return - else - next_offset = relation_offset(items) || 0 - relation_limit = relation_limit(items) - - if after_offset - next_offset += after_offset - end - - if before_offset && after_offset - if after_offset < before_offset - # Get the number of items between the two cursors - space_between = before_offset - after_offset - 1 - relation_limit = space_between - else - # The cursors overextend one another to an empty set - @sliced_nodes_null_relation = true - end - elsif before_offset - # Use limit to cut off the tail of the relation - relation_limit = before_offset - 1 - end - - @sliced_nodes_limit = relation_limit - @sliced_nodes_offset = next_offset - end - end - - # Apply `before` and `after` to the underlying `items`, - # returning a new relation. - def sliced_nodes - @sliced_nodes ||= begin - calculate_sliced_nodes_parameters - paginated_nodes = items - - if @sliced_nodes_null_relation - paginated_nodes = null_relation(paginated_nodes) - else - if @sliced_nodes_limit - paginated_nodes = set_limit(paginated_nodes, @sliced_nodes_limit) - end - - if @sliced_nodes_offset - paginated_nodes = set_offset(paginated_nodes, @sliced_nodes_offset) - end - end - - paginated_nodes - end - end - - # @return [Integer, nil] - def before_offset - @before_offset ||= before && offset_from_cursor(before) - end - - # @return [Integer, nil] - def after_offset - @after_offset ||= after && offset_from_cursor(after) - end - - # Apply `first` and `last` to `sliced_nodes`, - # returning a new relation - def limited_nodes - @limited_nodes ||= begin - calculate_sliced_nodes_parameters - if @sliced_nodes_null_relation - # it's an empty set - return sliced_nodes - end - relation_limit = @sliced_nodes_limit - relation_offset = @sliced_nodes_offset - - if first && (relation_limit.nil? || relation_limit > first) - # `first` would create a stricter limit that the one already applied, so add it - relation_limit = first - end - - if last - if relation_limit - if last <= relation_limit - # `last` is a smaller slice than the current limit, so apply it - relation_offset += (relation_limit - last) - relation_limit = last - end - else - # No limit, so get the last items - sliced_nodes_count = relation_count(sliced_nodes) - relation_offset += (sliced_nodes_count - [last, sliced_nodes_count].min) - relation_limit = last - end - end - - @paged_nodes_offset = relation_offset - paginated_nodes = items - paginated_nodes = set_offset(paginated_nodes, relation_offset) - if relation_limit - paginated_nodes = set_limit(paginated_nodes, relation_limit) - end - paginated_nodes - end - end - - # Load nodes after applying first/last/before/after, - # returns an array of nodes - def load_nodes - # Return an array so we can consistently use `.index(node)` on it - @nodes ||= limited_nodes.to_a - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/pagination/sequel_dataset_connection.rb b/vendor/gems/graphql/lib/graphql/pagination/sequel_dataset_connection.rb deleted file mode 100644 index 3a451247e15..00000000000 --- a/vendor/gems/graphql/lib/graphql/pagination/sequel_dataset_connection.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -require "graphql/pagination/relation_connection" - -module GraphQL - module Pagination - # Customizes `RelationConnection` to work with `Sequel::Dataset`s. - class SequelDatasetConnection < Pagination::RelationConnection - private - - def relation_offset(relation) - relation.opts[:offset] - end - - def relation_limit(relation) - relation.opts[:limit] - end - - def relation_count(relation) - # Remove order to make it faster - relation.order(nil).count - end - - def null_relation(relation) - relation.where(false) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/parse_error.rb b/vendor/gems/graphql/lib/graphql/parse_error.rb deleted file mode 100644 index 5262bde532c..00000000000 --- a/vendor/gems/graphql/lib/graphql/parse_error.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class ParseError < GraphQL::Error - attr_reader :line, :col, :query - def initialize(message, line, col, query, filename: nil) - if filename - message += " (#{filename})" - end - - super(message) - @line = line - @col = col - @query = query - end - - def to_h - locations = line ? [{ "line" => line, "column" => col }] : [] - { - "message" => message, - "locations" => locations, - } - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query.rb b/vendor/gems/graphql/lib/graphql/query.rb deleted file mode 100644 index a6503964f3f..00000000000 --- a/vendor/gems/graphql/lib/graphql/query.rb +++ /dev/null @@ -1,513 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - # A combination of query string and {Schema} instance which can be reduced to a {#result}. - class Query - extend Autoload - include Tracing::Traceable - extend Forwardable - - autoload :Context, "graphql/query/context" - autoload :Fingerprint, "graphql/query/fingerprint" - autoload :NullContext, "graphql/query/null_context" - autoload :Result, "graphql/query/result" - autoload :Variables, "graphql/query/variables" - autoload :InputValidationResult, "graphql/query/input_validation_result" - autoload :VariableValidationError, "graphql/query/variable_validation_error" - autoload :ValidationPipeline, "graphql/query/validation_pipeline" - - class OperationNameMissingError < GraphQL::ExecutionError - def initialize(name) - msg = if name.nil? - %|An operation name is required| - else - %|No operation named "#{name}"| - end - super(msg) - end - end - - attr_reader :schema, :context, :provided_variables - - # The value for root types - attr_accessor :root_value - - # @return [nil, String] The operation name provided by client or the one inferred from the document. Used to determine which operation to run. - attr_accessor :operation_name - - # @return [Boolean] if false, static validation is skipped (execution behavior for invalid queries is undefined) - attr_reader :validate - - # @param new_validate [Boolean] if false, static validation is skipped. This can't be reasssigned after validation. - def validate=(new_validate) - if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated? - raise ArgumentError, "Can't reassign Query#validate= after validation has run, remove this assignment." - else - @validate = new_validate - end - end - - # @return [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. - attr_reader :static_validator - - # @param new_validate [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. This can't be reasssigned after validation. - def static_validator=(new_validator) - if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated? - raise ArgumentError, "Can't reassign Query#static_validator= after validation has run, remove this assignment." - elsif !new_validator.is_a?(GraphQL::StaticValidation::Validator) - raise ArgumentError, "Expected a `GraphQL::StaticValidation::Validator` instance." - else - @static_validator = new_validator - end - end - - attr_writer :query_string - - # @return [GraphQL::Language::Nodes::Document] - def document - # It's ok if this hasn't been assigned yet - if @query_string || @document - with_prepared_ast { @document } - else - nil - end - end - - def inspect - "query ..." - end - - # @return [String, nil] The name of the operation to run (may be inferred) - def selected_operation_name - return nil unless selected_operation - selected_operation.name - end - - # @return [String, nil] the triggered event, if this query is a subscription update - attr_reader :subscription_topic - - attr_reader :tracers - - # Prepare query `query_string` on `schema` - # @param schema [GraphQL::Schema] - # @param query_string [String] - # @param context [#[]] an arbitrary hash of values which you can access in {GraphQL::Field#resolve} - # @param variables [Hash] values for `$variables` in the query - # @param operation_name [String] if the query string contains many operations, this is the one which should be executed - # @param root_value [Object] the object used to resolve fields on the root type - # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value) - # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value) - # @param visibility_profile [Symbol] Another way to assign `context[:visibility_profile]` - def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil) - # Even if `variables: nil` is passed, use an empty hash for simpler logic - variables ||= {} - @schema = schema - @context = schema.context_class.new(query: self, values: context) - if visibility_profile - @context[:visibility_profile] ||= visibility_profile - end - - if use_visibility_profile.nil? - use_visibility_profile = warden ? false : schema.use_visibility_profile? - end - - if use_visibility_profile - @visibility_profile = @schema.visibility.profile_for(@context) - @warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema) - else - @visibility_profile = nil - @warden = warden - end - - @subscription_topic = subscription_topic - @root_value = root_value - @fragments = nil - @operations = nil - @validate = validate - self.static_validator = static_validator if static_validator - context_tracers = (context ? context.fetch(:tracers, []) : []) - @tracers = schema.tracers + context_tracers - - if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers) - raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it." - end - - @analysis_errors = [] - if variables.is_a?(String) - raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables." - else - @provided_variables = variables || {} - end - - @query_string = query_string || query - @document = document - - if @query_string && @document - raise ArgumentError, "Query should only be provided a query string or a document, not both." - end - - if @query_string && !@query_string.is_a?(String) - raise ArgumentError, "Query string argument should be a String, got #{@query_string.class.name} instead." - end - - # A two-layer cache of type resolution: - # { abstract_type => { value => resolved_type } } - @resolved_types_cache = Hash.new do |h1, k1| - h1[k1] = Hash.new do |h2, k2| - h2[k2] = @schema.resolve_type(k1, k2, @context) - end - end - - # Trying to execute a document - # with no operations returns an empty hash - @ast_variables = [] - @mutation = false - @operation_name = operation_name - @prepared_ast = false - @validation_pipeline = nil - @max_depth = max_depth - @max_complexity = max_complexity - - @result_values = nil - @executed = false - - @logger = if context && context[:logger] == false - Logger.new(IO::NULL) - elsif context && (l = context[:logger]) - l - else - schema.default_logger - end - end - - # If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document - def query_string - @query_string ||= (document ? document.to_query_string : nil) - end - - # @return [Symbol, nil] - attr_reader :visibility_profile - - attr_accessor :multiplex - - # @return [GraphQL::Tracing::Trace] - def current_trace - @current_trace ||= context[:trace] || (multiplex ? multiplex.current_trace : schema.new_trace(multiplex: multiplex, query: self)) - end - - def subscription_update? - @subscription_topic && subscription? - end - - # A lookahead for the root selections of this query - # @return [GraphQL::Execution::Lookahead] - def lookahead - @lookahead ||= begin - ast_node = selected_operation - if ast_node.nil? - GraphQL::Execution::Lookahead::NULL_LOOKAHEAD - else - root_type = case ast_node.operation_type - when nil, "query" - types.query_root # rubocop:disable Development/ContextIsPassedCop - when "mutation" - types.mutation_root # rubocop:disable Development/ContextIsPassedCop - when "subscription" - types.subscription_root # rubocop:disable Development/ContextIsPassedCop - end - GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node]) - end - end - end - - # @api private - def result_values=(result_hash) - if @executed - raise "Invariant: Can't reassign result" - else - @executed = true - @result_values = result_hash - end - end - - # @api private - attr_reader :result_values - - def fragments - with_prepared_ast { @fragments } - end - - def operations - with_prepared_ast { @operations } - end - - # Get the result for this query, executing it once - # @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys - def result - if !@executed - Execution::Interpreter.run_all(@schema, [self], context: @context) - end - @result ||= Query::Result.new(query: self, values: @result_values) - end - - def executed? - @executed - end - - def static_errors - validation_errors + analysis_errors + context.errors - end - - # This is the operation to run for this query. - # If more than one operation is present, it must be named at runtime. - # @return [GraphQL::Language::Nodes::OperationDefinition, nil] - def selected_operation - with_prepared_ast { @selected_operation } - end - - # Determine the values for variables of this query, using default values - # if a value isn't provided at runtime. - # - # If some variable is invalid, errors are added to {#validation_errors}. - # - # @return [GraphQL::Query::Variables] Variables to apply to this query - def variables - @variables ||= begin - with_prepared_ast { - GraphQL::Query::Variables.new( - @context, - @ast_variables, - @provided_variables, - ) - } - end - end - - # Node-level cache for calculating arguments. Used during execution and query analysis. - # @param ast_node [GraphQL::Language::Nodes::AbstractNode] - # @param definition [GraphQL::Schema::Field] - # @param parent_object [GraphQL::Schema::Object] - # @return Hash{Symbol => Object} - def arguments_for(ast_node, definition, parent_object: nil) - arguments_cache.fetch(ast_node, definition, parent_object) - end - - def arguments_cache - @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self) - end - - # A version of the given query string, with: - # - Variables inlined to the query - # - Strings replaced with `` - # @return [String, nil] Returns nil if the query is invalid. - def sanitized_query_string(inline_variables: true) - with_prepared_ast { - schema.sanitized_printer.new(self, inline_variables: inline_variables).sanitized_query_string - } - end - - # This contains a few components: - # - # - The selected operation name (or `anonymous`) - # - The fingerprint of the query string - # - The number of given variables (for readability) - # - The fingerprint of the given variables - # - # This fingerprint can be used to track runs of the same operation-variables combination over time. - # - # @see operation_fingerprint - # @see variables_fingerprint - # @return [String] An opaque hash identifying this operation-variables combination - def fingerprint - @fingerprint ||= "#{operation_fingerprint}/#{variables_fingerprint}" - end - - # @return [String] An opaque hash for identifying this query's given query string and selected operation - def operation_fingerprint - @operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string || "")}" - end - - # @return [String] An opaque hash for identifying this query's given a variable values (not including defaults) - def variables_fingerprint - @variables_fingerprint ||= "#{provided_variables.size}/#{Fingerprint.generate(provided_variables.to_json)}" - end - - def validation_pipeline - with_prepared_ast { @validation_pipeline } - end - - def_delegators :validation_pipeline, :validation_errors, - :analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining - - attr_accessor :analysis_errors - def valid? - validation_pipeline.valid? && analysis_errors.empty? - end - - def warden - with_prepared_ast { @warden } - end - - def get_type(type_name) - types.type(type_name) # rubocop:disable Development/ContextIsPassedCop - end - - def get_field(owner, field_name) - types.field(owner, field_name) # rubocop:disable Development/ContextIsPassedCop - end - - def possible_types(type) - types.possible_types(type) # rubocop:disable Development/ContextIsPassedCop - end - - def root_type_for_operation(op_type) - case op_type - when "query" - types.query_root # rubocop:disable Development/ContextIsPassedCop - when "mutation" - types.mutation_root # rubocop:disable Development/ContextIsPassedCop - when "subscription" - types.subscription_root # rubocop:disable Development/ContextIsPassedCop - else - raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'" - end - end - - def types - @visibility_profile || warden.visibility_profile - end - - # @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType] - # @param value [Object] Any runtime value - # @return [GraphQL::ObjectType, nil] The runtime type of `value` from {Schema#resolve_type} - # @see {#possible_types} to apply filtering from `only` / `except` - def resolve_type(abstract_type, value = NOT_CONFIGURED) - if value.is_a?(Symbol) && value == NOT_CONFIGURED - # Old method signature - value = abstract_type - abstract_type = nil - end - if value.is_a?(GraphQL::Schema::Object) - value = value.object - end - @resolved_types_cache[abstract_type][value] - end - - def mutation? - with_prepared_ast { @mutation } - end - - def query? - with_prepared_ast { @query } - end - - def subscription? - with_prepared_ast { @subscription } - end - - # @api private - def handle_or_reraise(err) - schema.handle_or_reraise(context, err) - end - - def after_lazy(value, &block) - if !defined?(@runtime_instance) - @runtime_instance = context.namespace(:interpreter_runtime)[:runtime] - end - - if @runtime_instance - @runtime_instance.minimal_after_lazy(value, &block) - else - @schema.after_lazy(value, &block) - end - end - - attr_reader :logger - - private - - def find_operation(operations, operation_name) - if operation_name.nil? && operations.length == 1 - operations.values.first - elsif !operations.key?(operation_name) - nil - else - operations.fetch(operation_name) - end - end - - def prepare_ast - @prepared_ast = true - @warden ||= @schema.warden_class.new(schema: @schema, context: @context) - parse_error = nil - @document ||= begin - current_trace.begin_parse(query_string) - if query_string - GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens) - end - rescue GraphQL::ParseError => err - parse_error = err - @schema.parse_error(err, @context) - nil - ensure - current_trace.end_parse(query_string) - end - - @fragments = {} - @operations = {} - if @document - @document.definitions.each do |part| - case part - when GraphQL::Language::Nodes::FragmentDefinition - @fragments[part.name] = part - when GraphQL::Language::Nodes::OperationDefinition - @operations[part.name] = part - end - end - elsif parse_error - # This will be handled later - else - parse_error = GraphQL::ExecutionError.new("No query string was present") - @context.add_error(parse_error) - end - - # Trying to execute a document - # with no operations returns an empty hash - @ast_variables = [] - @mutation = false - @subscription = false - operation_name_error = nil - if !@operations.empty? - @selected_operation = find_operation(@operations, @operation_name) - if @selected_operation.nil? - operation_name_error = GraphQL::Query::OperationNameMissingError.new(@operation_name) - else - if @operation_name.nil? - @operation_name = @selected_operation.name - end - @ast_variables = @selected_operation.variables - @mutation = @selected_operation.operation_type == "mutation" - @query = @selected_operation.operation_type == "query" - @subscription = @selected_operation.operation_type == "subscription" - end - end - - @validation_pipeline = GraphQL::Query::ValidationPipeline.new( - query: self, - parse_error: parse_error, - operation_name_error: operation_name_error, - max_depth: @max_depth, - max_complexity: @max_complexity - ) - end - - # Since the query string is processed at the last possible moment, - # any internal values which depend on it should be accessed within this wrapper. - def with_prepared_ast - if !@prepared_ast - prepare_ast - end - yield - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/context.rb b/vendor/gems/graphql/lib/graphql/query/context.rb deleted file mode 100644 index 538a03adaaa..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/context.rb +++ /dev/null @@ -1,292 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Query - # Expose some query-specific info to field resolve functions. - # It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`. - class Context - - class ExecutionErrors - def initialize(ctx) - @context = ctx - end - - def add(err_or_msg) - err = case err_or_msg - when String - GraphQL::ExecutionError.new(err_or_msg) - when GraphQL::ExecutionError - err_or_msg - else - raise ArgumentError, "expected String or GraphQL::ExecutionError, not #{err_or_msg.class} (#{err_or_msg.inspect})" - end - # This will assign ast_node and path - @context.add_error(err) - end - - alias :>> :add - alias :push :add - end - - extend Forwardable - - # @return [Array] errors returned during execution - attr_reader :errors - - # @return [GraphQL::Query] The query whose context this is - attr_reader :query - - # @return [GraphQL::Schema] - attr_reader :schema - - # @return [Array] The current position in the result - attr_reader :path - - # Make a new context which delegates key lookup to `values` - # @param query [GraphQL::Query] the query who owns this context - # @param values [Hash] A hash of arbitrary values which will be accessible at query-time - def initialize(query:, schema: query.schema, values:) - @query = query - @schema = schema - @provided_values = values || {} - # Namespaced storage, where user-provided values are in `nil` namespace: - @storage = Hash.new { |h, k| h[k] = {} } - @storage[nil] = @provided_values - @errors = [] - @path = [] - @value = nil - @context = self # for SharedMethods TODO delete sharedmethods - @scoped_context = ScopedContext.new(self) - end - - # @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }` - def response_extensions - namespace(:__query_result_extensions__) - end - - def dataloader - @dataloader ||= self[:dataloader] || (query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new) - end - - # @api private - attr_writer :interpreter - - # @api private - attr_writer :value - - # @api private - attr_reader :scoped_context - - def []=(key, value) - @provided_values[key] = value - end - - def_delegators :@query, :trace - - def types - @types ||= @query.types - end - - attr_writer :types - - RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path]) - # @!method []=(key, value) - # Reassign `key` to the hash passed to {Schema#execute} as `context:` - - # Lookup `key` from the hash passed to {Schema#execute} as `context:` - def [](key) - if @scoped_context.key?(key) - @scoped_context[key] - elsif @provided_values.key?(key) - @provided_values[key] - elsif RUNTIME_METADATA_KEYS.include?(key) - if key == :current_path - current_path - else - (current_runtime_state = Fiber[:__graphql_runtime_info]) && - (query_runtime_state = current_runtime_state[@query]) && - (query_runtime_state.public_send(key)) - end - else - # not found - nil - end - end - - # Return this value to tell the runtime - # to exclude this field from the response altogether - def skip - GraphQL::Execution::SKIP - end - - # Add error at query-level. - # @param error [GraphQL::ExecutionError] an execution error - # @return [void] - def add_error(error) - if !error.is_a?(ExecutionError) - raise TypeError, "expected error to be a ExecutionError, but was #{error.class}" - end - errors << error - nil - end - - # @example Print the GraphQL backtrace during field resolution - # puts ctx.backtrace - # - # @return [GraphQL::Backtrace] The backtrace for this point in query execution - def backtrace - GraphQL::Backtrace.new(self) - end - - def execution_errors - @execution_errors ||= ExecutionErrors.new(self) - end - - def current_path - current_runtime_state = Fiber[:__graphql_runtime_info] - query_runtime_state = current_runtime_state && current_runtime_state[@query] - - path = query_runtime_state && - (result = query_runtime_state.current_result) && - (result.path) - if path && (rn = query_runtime_state.current_result_name) - path = path.dup - path.push(rn) - end - path - end - - def delete(key) - if @scoped_context.key?(key) - @scoped_context.delete(key) - else - @provided_values.delete(key) - end - end - - UNSPECIFIED_FETCH_DEFAULT = Object.new - - def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT) - if RUNTIME_METADATA_KEYS.include?(key) - (runtime = Fiber[:__graphql_runtime_info]) && - (query_runtime_state = runtime[@query]) && - (query_runtime_state.public_send(key)) - elsif @scoped_context.key?(key) - scoped_context[key] - elsif @provided_values.key?(key) - @provided_values[key] - elsif default != UNSPECIFIED_FETCH_DEFAULT - default - elsif block_given? - yield(self, key) - else - raise KeyError.new(key: key) - end - end - - def dig(key, *other_keys) - if RUNTIME_METADATA_KEYS.include?(key) - (current_runtime_state = Fiber[:__graphql_runtime_info]) && - (query_runtime_state = current_runtime_state[@query]) && - (obj = query_runtime_state.public_send(key)) && - if other_keys.empty? - obj - else - obj.dig(*other_keys) - end - elsif @scoped_context.key?(key) - @scoped_context.dig(key, *other_keys) - else - @provided_values.dig(key, *other_keys) - end - end - - def to_h - if (current_scoped_context = @scoped_context.merged_context) - @provided_values.merge(current_scoped_context) - else - @provided_values - end - end - - alias :to_hash :to_h - - def key?(key) - @scoped_context.key?(key) || @provided_values.key?(key) - end - - # @return [GraphQL::Schema::Warden] - def warden - @warden ||= (@query && @query.warden) - end - - # @api private - attr_writer :warden - - # Get an isolated hash for `ns`. Doesn't affect user-provided storage. - # @param ns [Object] a usage-specific namespace identifier - # @return [Hash] namespaced storage - def namespace(ns) - if ns == :interpreter - self - else - @storage[ns] - end - end - - # @return [Boolean] true if this namespace was accessed before - def namespace?(ns) - @storage.key?(ns) - end - - def logger - @query && @query.logger - end - - def inspect - "#" - end - - def scoped_merge!(hash) - @scoped_context.merge!(hash) - end - - def scoped_set!(key, value) - scoped_merge!(key => value) - nil - end - - # Use this when you need to do a scoped set _inside_ a lazy-loaded (or batch-loaded) - # block of code. - # - # @example using scoped context inside a promise - # scoped_ctx = context.scoped - # SomeBatchLoader.load(...).then do |thing| - # # use a scoped_ctx which was created _before_ dataloading: - # scoped_ctx.set!(:thing, thing) - # end - # @return [Context::Scoped] - def scoped - Scoped.new(@scoped_context, current_path) - end - - class Scoped - def initialize(scoped_context, path) - @path = path - @scoped_context = scoped_context - end - - def merge!(hash) - @scoped_context.merge!(hash, at: @path) - end - - def set!(key, value) - @scoped_context.merge!({ key => value }, at: @path) - nil - end - end - end - end -end - -require "graphql/query/context/scoped_context" diff --git a/vendor/gems/graphql/lib/graphql/query/context/scoped_context.rb b/vendor/gems/graphql/lib/graphql/query/context/scoped_context.rb deleted file mode 100644 index c0b3d80f5a8..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/context/scoped_context.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Query - class Context - class ScopedContext - def initialize(query_context) - @query_context = query_context - @scoped_contexts = nil - @all_keys = nil - end - - def merged_context - if @scoped_contexts.nil? - GraphQL::EmptyObjects::EMPTY_HASH - else - merged_ctx = {} - each_present_path_ctx do |path_ctx| - merged_ctx = path_ctx.merge(merged_ctx) - end - merged_ctx - end - end - - def merge!(hash, at: current_path) - @all_keys ||= Set.new - @all_keys.merge(hash.keys) - ctx = @scoped_contexts ||= {} - at.each do |path_part| - ctx = ctx[path_part] ||= { parent: ctx } - end - this_scoped_ctx = ctx[:scoped_context] ||= {} - this_scoped_ctx.merge!(hash) - end - - def key?(key) - if @all_keys && @all_keys.include?(key) - each_present_path_ctx do |path_ctx| - if path_ctx.key?(key) - return true - end - end - end - false - end - - def [](key) - each_present_path_ctx do |path_ctx| - if path_ctx.key?(key) - return path_ctx[key] - end - end - nil - end - - def current_path - @query_context.current_path || GraphQL::EmptyObjects::EMPTY_ARRAY - end - - def dig(key, *other_keys) - each_present_path_ctx do |path_ctx| - if path_ctx.key?(key) - found_value = path_ctx[key] - if !other_keys.empty? - return found_value.dig(*other_keys) - else - return found_value - end - end - end - nil - end - - private - - # Start at the current location, - # but look up the tree for previously-assigned scoped values - def each_present_path_ctx - ctx = @scoped_contexts - if ctx.nil? - # no-op - else - current_path.each do |path_part| - if ctx.key?(path_part) - ctx = ctx[path_part] - else - break - end - end - - while ctx - if (scoped_ctx = ctx[:scoped_context]) - yield(scoped_ctx) - end - ctx = ctx[:parent] - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/fingerprint.rb b/vendor/gems/graphql/lib/graphql/query/fingerprint.rb deleted file mode 100644 index 8757d85984c..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/fingerprint.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -require 'digest/sha2' - -module GraphQL - class Query - # @api private - # @see Query#query_fingerprint - # @see Query#variables_fingerprint - # @see Query#fingerprint - module Fingerprint - # Make an obfuscated hash of the given string (either a query string or variables JSON) - # @param string [String] - # @return [String] A normalized, opaque hash - def self.generate(input_str) - # Implemented to be: - # - Short (and uniform) length - # - Stable - # - Irreversibly Opaque (don't want to leak variable values) - # - URL-friendly - bytes = Digest::SHA256.digest(input_str) - Base64.urlsafe_encode64(bytes) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/input_validation_result.rb b/vendor/gems/graphql/lib/graphql/query/input_validation_result.rb deleted file mode 100644 index 44b4f9aa159..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/input_validation_result.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Query - class InputValidationResult - attr_accessor :problems - - def self.from_problem(explanation, path = nil, extensions: nil, message: nil) - result = self.new - result.add_problem(explanation, path, extensions: extensions, message: message) - result - end - - def initialize(valid: true, problems: nil) - @valid = valid - @problems = problems - end - - def valid? - @valid - end - - def add_problem(explanation, path = nil, extensions: nil, message: nil) - @problems ||= [] - @valid = false - problem = { "path" => path || [], "explanation" => explanation } - if extensions - problem["extensions"] = extensions - end - if message - problem["message"] = message - end - @problems.push(problem) - end - - def merge_result!(path, inner_result) - return if inner_result.nil? || inner_result.valid? - - if inner_result.problems - inner_result.problems.each do |p| - item_path = [path, *p["path"]] - add_problem(p["explanation"], item_path, message: p["message"], extensions: p["extensions"]) - end - end - # It could have been explicitly set on inner_result (if it had no problems) - @valid = false - end - - VALID = self.new - VALID.freeze - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/null_context.rb b/vendor/gems/graphql/lib/graphql/query/null_context.rb deleted file mode 100644 index a1183985d1e..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/null_context.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true -require "graphql/query/context" -module GraphQL - class Query - # This object can be `ctx` in places where there is no query - class NullContext < Context - include Singleton - - class NullQuery - def after_lazy(value) - yield(value) - end - end - - class NullSchema < GraphQL::Schema - end - - extend Forwardable - - attr_reader :schema, :query, :warden, :dataloader - def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?, :to_h - - def initialize - @query = NullQuery.new - @dataloader = GraphQL::Dataloader::NullDataloader.new - @schema = NullSchema - @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema) - @types = @warden.visibility_profile - freeze - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/result.rb b/vendor/gems/graphql/lib/graphql/query/result.rb deleted file mode 100644 index 92ce480b178..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/result.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Query - # A result from {Schema#execute}. - # It provides the requested data and - # access to the {Query} and {Query::Context}. - class Result - extend Forwardable - - def initialize(query:, values:) - @query = query - @to_h = values - end - - # @return [GraphQL::Query] The query that was executed - attr_reader :query - - # @return [Hash] The resulting hash of "data" and/or "errors" - attr_reader :to_h - - def_delegators :@query, :context, :mutation?, :query?, :subscription? - - def_delegators :@to_h, :[], :keys, :values, :to_json, :as_json - - # Delegate any hash-like method to the underlying hash. - def method_missing(method_name, *args, &block) - if @to_h.respond_to?(method_name) - @to_h.public_send(method_name, *args, &block) - else - super - end - end - - def respond_to_missing?(method_name, include_private = false) - @to_h.respond_to?(method_name) || super - end - - def inspect - "#" - end - - # A result is equal to another object when: - # - # - The other object is a Hash whose value matches `result.to_h` - # - The other object is a Result whose value matches `result.to_h` - # - # (The query is ignored for comparing result equality.) - # - # @return [Boolean] - def ==(other) - case other - when Hash - @to_h == other - when Query::Result - @to_h == other.to_h - else - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/validation_pipeline.rb b/vendor/gems/graphql/lib/graphql/query/validation_pipeline.rb deleted file mode 100644 index 58dc7142b1a..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/validation_pipeline.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Query - # Contain the validation pipeline and expose the results. - # - # 0. Checks in {Query#initialize}: - # - Rescue a ParseError, halt if there is one - # - Check for selected operation, halt if not found - # 1. Validate the AST, halt if errors - # 2. Validate the variables, halt if errors - # 3. Run query analyzers, halt if errors - # - # {#valid?} is false if any of the above checks halted the pipeline. - # - # @api private - class ValidationPipeline - attr_reader :max_depth, :max_complexity, :validate_timeout_remaining - - def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:) - @validation_errors = [] - @parse_error = parse_error - @operation_name_error = operation_name_error - @query = query - @schema = query.schema - @max_depth = max_depth - @max_complexity = max_complexity - - @has_validated = false - end - - # @return [Boolean] does this query have errors that should prevent it from running? - def valid? - ensure_has_validated - @valid - end - - # @return [Array] Static validation errors for the query string - def validation_errors - ensure_has_validated - @validation_errors - end - - def analyzers - ensure_has_validated - @query_analyzers - end - - def has_validated? - @has_validated == true - end - - private - - # If the pipeline wasn't run yet, run it. - # If it was already run, do nothing. - def ensure_has_validated - return if @has_validated - @has_validated = true - - if @parse_error - # This is kind of crazy: we push the parse error into `ctx` - # in `def self.parse_error` by default so that users can _opt out_ by redefining that hook. - # That means we can't _re-add_ the error here (otherwise we'd either - # add it twice _or_ override the user's choice to not add it). - # So we just have to know that it was invalid and go from there. - @valid = false - return - elsif @operation_name_error - @validation_errors << @operation_name_error - else - validator = @query.static_validator || @schema.static_validator - validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors) - @validation_errors.concat(validation_result[:errors]) - @validate_timeout_remaining = validation_result[:remaining_timeout] - if @validation_errors.empty? - @validation_errors.concat(@query.variables.errors) - end - - if @validation_errors.empty? - @query_analyzers = build_analyzers( - @schema, - @max_depth, - @max_complexity - ) - end - end - - @valid = @validation_errors.empty? - rescue SystemStackError => err - @valid = false - @schema.query_stack_error(@query, err) - end - - # If there are max_* values, add them, - # otherwise reuse the schema's list of analyzers. - def build_analyzers(schema, max_depth, max_complexity) - qa = schema.query_analyzers.dup - - if max_depth || max_complexity - # Depending on the analysis engine, we must use different analyzers - # remove this once everything has switched over to AST analyzers - if max_depth - qa << GraphQL::Analysis::MaxQueryDepth - end - if max_complexity - qa << GraphQL::Analysis::MaxQueryComplexity - end - qa - else - qa - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/variable_validation_error.rb b/vendor/gems/graphql/lib/graphql/query/variable_validation_error.rb deleted file mode 100644 index 3a934dfc164..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/variable_validation_error.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Query - class VariableValidationError < GraphQL::ExecutionError - attr_accessor :value, :validation_result - - def initialize(variable_ast, type, value, validation_result, msg: nil) - @value = value - @validation_result = validation_result - - msg ||= "Variable $#{variable_ast.name} of type #{type.to_type_signature} was provided invalid value" - - if !problem_fields.empty? - msg += " for #{problem_fields.join(", ")}" - end - - super(msg) - self.ast_node = variable_ast - end - - def to_h - # It is possible there are other extension items in this error, so handle - # a one level deep merge explicitly. However beyond that only show the - # latest value and problems. - super.merge({ "extensions" => { "value" => value, "problems" => validation_result.problems }}) do |key, oldValue, newValue| - if oldValue.respond_to?(:merge) - oldValue.merge(newValue) - else - newValue - end - end - end - - private - - def problem_fields - @problem_fields ||= @validation_result - .problems - .reject { |problem| problem["path"].empty? } - .map { |problem| "#{problem['path'].join('.')} (#{problem['explanation']})" } - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/query/variables.rb b/vendor/gems/graphql/lib/graphql/query/variables.rb deleted file mode 100644 index 6fbc73192eb..00000000000 --- a/vendor/gems/graphql/lib/graphql/query/variables.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Query - # Read-only access to query variables, applying default values if needed. - class Variables - extend Forwardable - - # @return [Array] Any errors encountered when parsing the provided variables and literal values - attr_reader :errors - - attr_reader :context - - def initialize(ctx, ast_variables, provided_variables) - schema = ctx.schema - @context = ctx - - @provided_variables = deep_stringify(provided_variables) - @errors = [] - @storage = ast_variables.each_with_object({}) do |ast_variable, memo| - if schema.validate_max_errors && schema.validate_max_errors <= @errors.count - add_max_errors_reached_message - break - end - # Find the right value for this variable: - # - First, use the value provided at runtime - # - Then, fall back to the default value from the query string - # If it's still nil, raise an error if it's required. - variable_type = schema.type_from_ast(ast_variable.type, context: ctx) - if variable_type.nil? || !variable_type.unwrap.kind.input? - # Pass -- it will get handled by a validator - else - variable_name = ast_variable.name - default_value = ast_variable.default_value - provided_value = @provided_variables[variable_name] - value_was_provided = @provided_variables.key?(variable_name) - max_errors = schema.validate_max_errors - @errors.count if schema.validate_max_errors - begin - validation_result = variable_type.validate_input(provided_value, ctx, max_errors: max_errors) - if validation_result.valid? - if value_was_provided - # Add the variable if a value was provided - memo[variable_name] = provided_value - elsif default_value != nil - memo[variable_name] = if default_value.is_a?(Language::Nodes::NullValue) - nil - else - default_value - end - end - end - rescue GraphQL::ExecutionError => ex - # TODO: This should really include the path to the problematic node in the variable value - # like InputValidationResults generated by validate_non_null_input but unfortunately we don't - # have this information available in the coerce_input call chain. Note this path is the path - # that appears under errors.extensions.problems.path and NOT the result path under errors.path. - validation_result = GraphQL::Query::InputValidationResult.from_problem(ex.message) - end - - if !validation_result.valid? - @errors << GraphQL::Query::VariableValidationError.new(ast_variable, variable_type, provided_value, validation_result) - end - end - end - end - - def_delegators :@storage, :length, :key?, :[], :fetch, :to_h - - private - - def deep_stringify(val) - case val - when Array - val.map { |v| deep_stringify(v) } - when Hash - new_val = {} - val.each do |k, v| - new_val[k.to_s] = deep_stringify(v) - end - new_val - else - val - end - end - - def add_max_errors_reached_message - message = "Too many errors processing variables, max validation error limit reached. Execution aborted" - validation_result = GraphQL::Query::InputValidationResult.from_problem(message) - errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/railtie.rb b/vendor/gems/graphql/lib/graphql/railtie.rb deleted file mode 100644 index b99b50f0576..00000000000 --- a/vendor/gems/graphql/lib/graphql/railtie.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - # Support {GraphQL::Parser::Cache} and {GraphQL.eager_load!} - # - # @example Enable the parser cache with default directory - # - # config.graphql.parser_cache = true - # - class Railtie < Rails::Railtie - config.graphql = ActiveSupport::OrderedOptions.new - config.graphql.parser_cache = false - config.eager_load_namespaces << GraphQL - - initializer("graphql.cache") do |app| - if config.graphql.parser_cache - Language::Parser.cache ||= Language::Cache.new( - app.root.join("tmp/cache/graphql") - ) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rake_task.rb b/vendor/gems/graphql/lib/graphql/rake_task.rb deleted file mode 100644 index e5601843b92..00000000000 --- a/vendor/gems/graphql/lib/graphql/rake_task.rb +++ /dev/null @@ -1,164 +0,0 @@ -# frozen_string_literal: true -require "fileutils" -require "rake" -require "graphql/rake_task/validate" - -module GraphQL - # A rake task for dumping a schema as IDL or JSON. - # - # By default, schemas are looked up by name as constants using `schema_name:`. - # You can provide a `load_schema` function to return your schema another way. - # - # Use `load_context:` and `visible?` to dump schemas under certain visibility constraints. - # - # @example Dump a Schema to .graphql + .json files - # require "graphql/rake_task" - # GraphQL::RakeTask.new(schema_name: "MySchema") - # - # # $ rake graphql:schema:dump - # # Schema IDL dumped to ./schema.graphql - # # Schema JSON dumped to ./schema.json - # - # @example Invoking the task from Ruby - # require "rake" - # Rake::Task["graphql:schema:dump"].invoke - # - # @example Providing arguments to build the introspection query - # require "graphql/rake_task" - # GraphQL::RakeTask.new(schema_name: "MySchema", include_is_one_of: true) - class RakeTask - include Rake::DSL - - DEFAULT_OPTIONS = { - namespace: "graphql", - dependencies: nil, - schema_name: nil, - load_schema: ->(task) { Object.const_get(task.schema_name) }, - load_context: ->(task) { {} }, - directory: ".", - idl_outfile: "schema.graphql", - json_outfile: "schema.json", - include_deprecated_args: true, - include_schema_description: false, - include_is_repeatable: false, - include_specified_by_url: false, - include_is_one_of: false - } - - # @return [String] Namespace for generated tasks - attr_writer :namespace - - def rake_namespace - @namespace - end - - # @return [Array] - attr_accessor :dependencies - - # @return [String] By default, used to find the schema as a constant. - # @see {#load_schema} for loading a schema another way - attr_accessor :schema_name - - # @return [<#call(task)>] A proc for loading the target GraphQL schema - attr_accessor :load_schema - - # @return [<#call(task)>] A callable for loading the query context - attr_accessor :load_context - - # @return [String] target for IDL task - attr_accessor :idl_outfile - - # @return [String] target for JSON task - attr_accessor :json_outfile - - # @return [String] directory for IDL & JSON files - attr_accessor :directory - - # @return [Boolean] Options for additional fields in the introspection query JSON response - # @see GraphQL::Schema.as_json - attr_accessor :include_deprecated_args, :include_schema_description, :include_is_repeatable, :include_specified_by_url, :include_is_one_of - - # Set the parameters of this task by passing keyword arguments - # or assigning attributes inside the block - def initialize(options = {}) - all_options = DEFAULT_OPTIONS.merge(options) - all_options.each do |k, v| - self.public_send("#{k}=", v) - end - - if block_given? - yield(self) - end - - define_task - end - - private - - # Use the provided `method_name` to generate a string from the specified schema - # then write it to `file`. - def write_outfile(method_name, file) - schema = @load_schema.call(self) - context = @load_context.call(self) - result = case method_name - when :to_json - schema.to_json( - include_is_one_of: include_is_one_of, - include_deprecated_args: include_deprecated_args, - include_is_repeatable: include_is_repeatable, - include_specified_by_url: include_specified_by_url, - include_schema_description: include_schema_description, - context: context - ) - when :to_definition - schema.to_definition(context: context) - else - raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}" - end - dir = File.dirname(file) - FileUtils.mkdir_p(dir) - if !result.end_with?("\n") - result += "\n" - end - File.write(file, result) - end - - def idl_path - File.join(@directory, @idl_outfile) - end - - def json_path - File.join(@directory, @json_outfile) - end - - def load_rails_environment_if_defined - if Rake::Task.task_defined?('environment') - Rake::Task['environment'].invoke - end - end - - # Use the Rake DSL to add tasks - def define_task - namespace(@namespace) do - namespace("schema") do - desc("Dump the schema to IDL in #{idl_path}") - task :idl => @dependencies do - load_rails_environment_if_defined - write_outfile(:to_definition, idl_path) - puts "Schema IDL dumped into #{idl_path}" - end - - desc("Dump the schema to JSON in #{json_path}") - task :json => @dependencies do - load_rails_environment_if_defined - write_outfile(:to_json, json_path) - puts "Schema JSON dumped into #{json_path}" - end - - desc("Dump the schema to JSON and IDL") - task :dump => [:idl, :json] - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rake_task/validate.rb b/vendor/gems/graphql/lib/graphql/rake_task/validate.rb deleted file mode 100644 index e0e87d5abf8..00000000000 --- a/vendor/gems/graphql/lib/graphql/rake_task/validate.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class RakeTask - extend Rake::DSL - - desc "Get the checksum of a graphql-pro version and compare it to published versions on GitHub and graphql-ruby.org" - task "graphql:pro:validate", [:gem_version] do |t, args| - version = args[:gem_version] - if version.nil? - raise ArgumentError, "A specific version is required, eg `rake graphql:pro:validate[1.12.0]`" - end - check = "\e[32m✓\e[0m" - ex = "\e[31m✘\e[0m" - puts "Validating graphql-pro v#{version}" - puts " - Checking for graphql-pro credentials..." - - creds = `bundle config gems.graphql.pro --parseable`[/[a-z0-9]{11}:[a-z0-9]{11}/] - if creds.nil? - puts " #{ex} failed, please set with `bundle config gems.graphql.pro $MY_CREDENTIALS`" - exit(1) - else - puts " #{check} found" - end - - puts " - Fetching the gem..." - fetch_result = `gem fetch graphql-pro -v #{version} --source https://#{creds}@gems.graphql.pro` - if fetch_result.empty? - puts " #{ex} failed to fetch v#{version}" - exit(1) - else - puts " #{check} fetched" - end - - puts " - Validating digest..." - require "digest/sha2" - gem_digest = Digest::SHA512.new.hexdigest(File.read("graphql-pro-#{version}.gem")) - require "net/http" - github_uri = URI("https://raw.githubusercontent.com/rmosolgo/graphql-ruby/master/guides/pro/checksums/graphql-pro-#{version}.txt") - # Remove final newline from .txt file - github_digest = Net::HTTP.get(github_uri).chomp - - docs_uri = URI("https://graphql-ruby.org/pro/checksums/graphql-pro-#{version}.txt") - docs_digest = Net::HTTP.get(docs_uri).chomp - - if docs_digest == gem_digest && github_digest == gem_digest - puts " #{check} validated from GitHub" - puts " #{check} validated from graphql-ruby.org" - else - puts " #{ex} SHA mismatch:" - puts " Downloaded: #{gem_digest}" - puts " GitHub: #{github_digest}" - puts " graphql-ruby.org: #{docs_digest}" - puts "" - puts " This download of graphql-pro is invalid, please open an issue:" - puts " https://github.com/rmosolgo/graphql-ruby/issues/new?title=graphql-pro%20digest%20mismatch%20(#{version})" - exit(1) - end - - puts "\e[32m✔\e[0m graphql-pro #{version} validated successfully!" - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/relay.rb b/vendor/gems/graphql/lib/graphql/relay.rb deleted file mode 100644 index e11682b68aa..00000000000 --- a/vendor/gems/graphql/lib/graphql/relay.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -require 'graphql/relay/range_add' diff --git a/vendor/gems/graphql/lib/graphql/relay/range_add.rb b/vendor/gems/graphql/lib/graphql/relay/range_add.rb deleted file mode 100644 index 13358fb670e..00000000000 --- a/vendor/gems/graphql/lib/graphql/relay/range_add.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Relay - # This provides some isolation from `GraphQL::Relay` internals. - # - # Given a list of items and a new item, it will provide a connection and an edge. - # - # The connection doesn't receive outside arguments, so the list of items - # should be ordered and paginated before providing it here. - # - # @example Adding a comment to list of comments - # post = Post.find(args[:post_id]) - # comments = post.comments - # new_comment = comments.build(body: args[:body]) - # new_comment.save! - # - # range_add = GraphQL::Relay::RangeAdd.new( - # parent: post, - # collection: comments, - # item: new_comment, - # context: context, - # ) - # - # response = { - # post: post, - # comments_connection: range_add.connection, - # new_comment_edge: range_add.edge, - # } - class RangeAdd - attr_reader :edge, :connection, :parent - - # @param collection [Object] The list of items to wrap in a connection - # @param item [Object] The newly-added item (will be wrapped in `edge_class`) - # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection - # @param parent [Object] The owner of `collection`, will be passed to the connection if provided - # @param edge_class [Class] The class to wrap `item` with (defaults to the connection's edge class) - def initialize(collection:, item:, context:, parent: nil, edge_class: nil) - conn_class = context.schema.connections.wrapper_for(collection) - # The rest will be added by ConnectionExtension - @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class) - # Check if this connection supports it, to support old versions of GraphQL-Pro - @edge = if @connection.respond_to?(:range_add_edge) - @connection.range_add_edge(item) - else - @connection.edge_class.new(item, @connection) - end - - @parent = parent - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rubocop.rb b/vendor/gems/graphql/lib/graphql/rubocop.rb deleted file mode 100644 index c537e0b530f..00000000000 --- a/vendor/gems/graphql/lib/graphql/rubocop.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -require "graphql/rubocop/graphql/default_null_true" -require "graphql/rubocop/graphql/default_required_true" -require "graphql/rubocop/graphql/field_type_in_block" -require "graphql/rubocop/graphql/root_types_in_block" diff --git a/vendor/gems/graphql/lib/graphql/rubocop/graphql/base_cop.rb b/vendor/gems/graphql/lib/graphql/rubocop/graphql/base_cop.rb deleted file mode 100644 index 25b46749823..00000000000 --- a/vendor/gems/graphql/lib/graphql/rubocop/graphql/base_cop.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true -require "rubocop" - -module GraphQL - module Rubocop - module GraphQL - class BaseCop < RuboCop::Cop::Base - extend RuboCop::Cop::AutoCorrector - - # Return the source of `send_node`, but without the keyword argument represented by `pair_node` - def source_without_keyword_argument(send_node, pair_node) - # work back to the preceding comma - first_pos = pair_node.location.expression.begin_pos - end_pos = pair_node.location.expression.end_pos - node_source = send_node.source_range.source - node_first_pos = send_node.location.expression.begin_pos - - relative_first_pos = first_pos - node_first_pos - relative_last_pos = end_pos - node_first_pos - - begin_removal_pos = relative_first_pos - while node_source[begin_removal_pos] != "," - begin_removal_pos -= 1 - if begin_removal_pos < 1 - raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})" - end - end - - end_removal_pos = relative_last_pos - cleaned_node_source = node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1] - cleaned_node_source - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rubocop/graphql/default_null_true.rb b/vendor/gems/graphql/lib/graphql/rubocop/graphql/default_null_true.rb deleted file mode 100644 index edfc641bf38..00000000000 --- a/vendor/gems/graphql/lib/graphql/rubocop/graphql/default_null_true.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true -require_relative "base_cop" - -module GraphQL - module Rubocop - module GraphQL - # Identify (and auto-correct) any field configuration which duplicates - # the default `null: true` property. - # - # `null: true` is default because nullable fields can always be converted - # to non-null fields (`null: false`) without a breaking change. (The opposite change, from `null: false` - # to `null: true`, change.) - # - # @example - # # Both of these define `name: String` in GraphQL: - # - # # bad - # field :name, String, null: true - # - # # good - # field :name, String - # - class DefaultNullTrue < BaseCop - MSG = "`null: true` is the default and can be removed." - - def_node_matcher :field_config_with_null_true?, <<-Pattern - ( - send nil? :field ... (hash $(pair (sym :null) (true)) ...) - ) - Pattern - - def on_send(node) - field_config_with_null_true?(node) do |null_config| - add_offense(null_config) do |corrector| - cleaned_node_source = source_without_keyword_argument(node, null_config) - corrector.replace(node.source_range, cleaned_node_source) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rubocop/graphql/default_required_true.rb b/vendor/gems/graphql/lib/graphql/rubocop/graphql/default_required_true.rb deleted file mode 100644 index d3ba15a9018..00000000000 --- a/vendor/gems/graphql/lib/graphql/rubocop/graphql/default_required_true.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true -require_relative "./base_cop" - -module GraphQL - module Rubocop - module GraphQL - # Identify (and auto-correct) any argument configuration which duplicates - # the default `required: true` property. - # - # `required: true` is default because required arguments can always be converted - # to optional arguments (`required: false`) without a breaking change. (The opposite change, from `required: false` - # to `required: true`, change.) - # - # @example - # # Both of these define `id: ID!` in GraphQL: - # - # # bad - # argument :id, ID, required: true - # - # # good - # argument :id, ID - # - class DefaultRequiredTrue < BaseCop - MSG = "`required: true` is the default and can be removed." - - def_node_matcher :argument_config_with_required_true?, <<-Pattern - ( - send {nil? _} :argument ... (hash <$(pair (sym :required) (true)) ...>) - ) - Pattern - - def on_send(node) - argument_config_with_required_true?(node) do |required_config| - add_offense(required_config) do |corrector| - cleaned_node_source = source_without_keyword_argument(node, required_config) - corrector.replace(node, cleaned_node_source) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rubocop/graphql/field_type_in_block.rb b/vendor/gems/graphql/lib/graphql/rubocop/graphql/field_type_in_block.rb deleted file mode 100644 index d98838a6d7b..00000000000 --- a/vendor/gems/graphql/lib/graphql/rubocop/graphql/field_type_in_block.rb +++ /dev/null @@ -1,144 +0,0 @@ -# frozen_string_literal: true -require_relative "./base_cop" - -module GraphQL - module Rubocop - module GraphQL - # Identify (and auto-correct) any field whose type configuration isn't given - # in the configuration block. - # - # @example - # # bad, immediately causes Rails to load `app/graphql/types/thing.rb` - # field :thing, Types::Thing - # - # # good, defers loading until the file is needed - # field :thing do - # type(Types::Thing) - # end - # - class FieldTypeInBlock < BaseCop - MSG = "type configuration can be moved to a block to defer loading the type's file" - - BUILT_IN_SCALAR_NAMES = ["Float", "Int", "Integer", "String", "ID", "Boolean"] - def_node_matcher :field_config_with_inline_type, <<-Pattern - ( - send {nil? _} :field sym ${const array} ... - ) - Pattern - - def_node_matcher :field_config_with_inline_type_and_block, <<-Pattern - ( - block - (send {nil? _} :field sym ${const array} ...) ... - (args) - _ - - ) - Pattern - - def on_block(node) - ignore_node(node) - field_config_with_inline_type_and_block(node) do |type_const| - type_const_str = get_type_argument_str(node, type_const) - if ignore_inline_type_str?(type_const_str) - # Do nothing ... - else - add_offense(type_const) do |corrector| - cleaned_node_source = delete_type_argument(node, type_const) - field_indent = determine_field_indent(node) - cleaned_node_source.sub!(/(\{|do)/, "\\1\n#{field_indent} type #{type_const_str}") - corrector.replace(node, cleaned_node_source) - end - end - end - end - - def on_send(node) - return if part_of_ignored_node?(node) - field_config_with_inline_type(node) do |type_const| - type_const_str = get_type_argument_str(node, type_const) - if ignore_inline_type_str?(type_const_str) - # Do nothing -- not loading from another file - else - add_offense(type_const) do |corrector| - cleaned_node_source = delete_type_argument(node, type_const) - field_indent = determine_field_indent(node) - cleaned_node_source += " do\n#{field_indent} type #{type_const_str}\n#{field_indent}end" - corrector.replace(node, cleaned_node_source) - end - end - end - end - - - private - - def ignore_inline_type_str?(type_str) - if BUILT_IN_SCALAR_NAMES.include?(type_str) - true - elsif (inner_type_str = type_str.sub(/\[([A-Za-z]+)(, null: (true|false))?\]/, '\1')) && BUILT_IN_SCALAR_NAMES.include?(inner_type_str) - true - else - false - end - end - - def get_type_argument_str(send_node, type_const) - first_pos = type_const.location.expression.begin_pos - end_pos = type_const.location.expression.end_pos - node_source = send_node.source_range.source - node_first_pos = send_node.location.expression.begin_pos - - relative_first_pos = first_pos - node_first_pos - end_removal_pos = end_pos - node_first_pos - - node_source[relative_first_pos...end_removal_pos] - end - - def delete_type_argument(send_node, type_const) - first_pos = type_const.location.expression.begin_pos - end_pos = type_const.location.expression.end_pos - node_source = send_node.source_range.source - node_first_pos = send_node.location.expression.begin_pos - - relative_first_pos = first_pos - node_first_pos - end_removal_pos = end_pos - node_first_pos - - begin_removal_pos = relative_first_pos - while node_source[begin_removal_pos] != "," - begin_removal_pos -= 1 - if begin_removal_pos < 1 - raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})" - end - end - - node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1] - end - - def determine_field_indent(send_node) - type_defn_node = send_node - - while (type_defn_node && !(type_defn_node.class_definition? || type_defn_node.module_definition?)) - type_defn_node = type_defn_node.parent - end - - if type_defn_node.nil? - raise "Invariant: Something went wrong in GraphQL-Ruby, couldn't find surrounding class definition for field (#{send_node}).\n\nPlease report this error on GitHub." - end - - type_defn_source = type_defn_node.source - indent_test_idx = send_node.location.expression.begin_pos - type_defn_node.source_range.begin_pos - 1 - field_indent = "".dup - while type_defn_source[indent_test_idx] == " " - field_indent << " " - indent_test_idx -= 1 - if indent_test_idx == 0 - raise "Invariant: somehow backtracted to beginning of class when looking for field indent (source: #{node_source.inspect})" - end - end - field_indent - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/rubocop/graphql/root_types_in_block.rb b/vendor/gems/graphql/lib/graphql/rubocop/graphql/root_types_in_block.rb deleted file mode 100644 index 80cbb0a5b21..00000000000 --- a/vendor/gems/graphql/lib/graphql/rubocop/graphql/root_types_in_block.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true -require_relative "./base_cop" - -module GraphQL - module Rubocop - module GraphQL - # Identify (and auto-correct) any root types in your schema file. - # - # @example - # # bad, immediately causes Rails to load `app/graphql/types/query.rb` - # query Types::Query - # - # # good, defers loading until the file is needed - # query { Types::Query } - # - class RootTypesInBlock < BaseCop - MSG = "type configuration can be moved to a block to defer loading the type's file" - - def_node_matcher :root_type_config_without_block, <<-Pattern - ( - send nil? {:query :mutation :subscription} const - ) - Pattern - - def on_send(node) - root_type_config_without_block(node) do - add_offense(node) do |corrector| - new_node_source = node.source_range.source - new_node_source.sub!(/(query|mutation|subscription)/, '\1 {') - new_node_source << " }" - corrector.replace(node, new_node_source) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/runtime_type_error.rb b/vendor/gems/graphql/lib/graphql/runtime_type_error.rb deleted file mode 100644 index 8f3f29a397d..00000000000 --- a/vendor/gems/graphql/lib/graphql/runtime_type_error.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class RuntimeTypeError < GraphQL::Error - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema.rb b/vendor/gems/graphql/lib/graphql/schema.rb deleted file mode 100644 index a5ada61b33a..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema.rb +++ /dev/null @@ -1,1836 +0,0 @@ -# frozen_string_literal: true -require "logger" -require "graphql/schema/addition" -require "graphql/schema/always_visible" -require "graphql/schema/base_64_encoder" -require "graphql/schema/find_inherited_value" -require "graphql/schema/finder" -require "graphql/schema/introspection_system" -require "graphql/schema/late_bound_type" -require "graphql/schema/timeout" -require "graphql/schema/type_expression" -require "graphql/schema/unique_within_type" -require "graphql/schema/warden" -require "graphql/schema/build_from_definition" - -require "graphql/schema/validator" -require "graphql/schema/member" -require "graphql/schema/wrapper" -require "graphql/schema/list" -require "graphql/schema/non_null" -require "graphql/schema/argument" -require "graphql/schema/enum_value" -require "graphql/schema/enum" -require "graphql/schema/field_extension" -require "graphql/schema/field" -require "graphql/schema/input_object" -require "graphql/schema/interface" -require "graphql/schema/scalar" -require "graphql/schema/object" -require "graphql/schema/union" -require "graphql/schema/directive" -require "graphql/schema/directive/deprecated" -require "graphql/schema/directive/include" -require "graphql/schema/directive/one_of" -require "graphql/schema/directive/skip" -require "graphql/schema/directive/feature" -require "graphql/schema/directive/flagged" -require "graphql/schema/directive/transform" -require "graphql/schema/directive/specified_by" -require "graphql/schema/type_membership" - -require "graphql/schema/resolver" -require "graphql/schema/mutation" -require "graphql/schema/has_single_input_argument" -require "graphql/schema/relay_classic_mutation" -require "graphql/schema/subscription" -require "graphql/schema/visibility" - -module GraphQL - # A GraphQL schema which may be queried with {GraphQL::Query}. - # - # The {Schema} contains: - # - # - types for exposing your application - # - query analyzers for assessing incoming queries (including max depth & max complexity restrictions) - # - execution strategies for running incoming queries - # - # Schemas start with root types, {Schema#query}, {Schema#mutation} and {Schema#subscription}. - # The schema will traverse the tree of fields & types, using those as starting points. - # Any undiscoverable types may be provided with the `types` configuration. - # - # Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations. - # (These configurations can be overridden by specific calls to {Schema#execute}) - # - # @example defining a schema - # class MySchema < GraphQL::Schema - # query QueryType - # # If types are only connected by way of interfaces, they must be added here - # orphan_types ImageType, AudioType - # end - # - class Schema - extend GraphQL::Schema::Member::HasAstNode - extend GraphQL::Schema::FindInheritedValue - extend Autoload - - autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types" - - class DuplicateNamesError < GraphQL::Error - attr_reader :duplicated_name - def initialize(duplicated_name:, duplicated_definition_1:, duplicated_definition_2:) - @duplicated_name = duplicated_name - super( - "Found two visible definitions for `#{duplicated_name}`: #{duplicated_definition_1}, #{duplicated_definition_2}" - ) - end - end - - class UnresolvedLateBoundTypeError < GraphQL::Error - attr_reader :type - def initialize(type:) - @type = type - super("Late bound type was never found: #{type.inspect}") - end - end - - # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string. - class InvalidDocumentError < Error; end; - - class << self - # Create schema with the result of an introspection query. - # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY} - # @return [Class] the schema described by `input` - def from_introspection(introspection_result) - GraphQL::Schema::Loader.load(introspection_result) - end - - # Create schema from an IDL schema or file containing an IDL definition. - # @param definition_or_path [String] A schema definition string, or a path to a file containing the definition - # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution - # @param parser [Object] An object for handling definition string parsing (must respond to `parse`) - # @param using [Hash] Plugins to attach to the created schema with `use(key, value)` - # @return [Class] the schema described by `document` - def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}) - # If the file ends in `.graphql` or `.graphqls`, treat it like a filepath - if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls") - GraphQL::Schema::BuildFromDefinition.from_definition_path( - self, - definition_or_path, - default_resolve: default_resolve, - parser: parser, - using: using, - ) - else - GraphQL::Schema::BuildFromDefinition.from_definition( - self, - definition_or_path, - default_resolve: default_resolve, - parser: parser, - using: using, - ) - end - end - - def deprecated_graphql_definition - graphql_definition(silence_deprecation_warning: true) - end - - # @return [GraphQL::Subscriptions] - def subscriptions(inherited: true) - defined?(@subscriptions) ? @subscriptions : (inherited ? find_inherited_value(:subscriptions, nil) : nil) - end - - def subscriptions=(new_implementation) - @subscriptions = new_implementation - end - - # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set. - def default_trace_mode(new_mode = nil) - if new_mode - @default_trace_mode = new_mode - elsif defined?(@default_trace_mode) - @default_trace_mode - elsif superclass.respond_to?(:default_trace_mode) - superclass.default_trace_mode - else - :default - end - end - - def trace_class(new_class = nil) - if new_class - # If any modules were already added for `:default`, - # re-apply them here - mods = trace_modules_for(:default) - mods.each { |mod| new_class.include(mod) } - new_class.include(DefaultTraceClass) - trace_mode(:default, new_class) - end - trace_class_for(:default, build: true) - end - - # @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined. - def trace_class_for(mode, build: false) - if (trace_class = own_trace_modes[mode]) - trace_class - elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false)) - trace_class - elsif build - own_trace_modes[mode] = build_trace_mode(mode) - else - nil - end - end - - # Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested. - # {default_trace_mode} is used when no `trace_mode: ...` is requested. - # - # When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)` - # unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.) - # - # Subclasses of the schema will use `trace_class` as a base class for this mode and those - # subclass also will _not_ receive default tracing modules. - # - # @param mode_name [Symbol] - # @param trace_class [Class] subclass of GraphQL::Tracing::Trace - # @return void - def trace_mode(mode_name, trace_class) - own_trace_modes[mode_name] = trace_class - nil - end - - def own_trace_modes - @own_trace_modes ||= {} - end - - def build_trace_mode(mode) - case mode - when :default - # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class. - base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: true)) || GraphQL::Tracing::Trace - const_set(:DefaultTrace, Class.new(base_class) do - include DefaultTraceClass - end) - else - # First, see if the superclass has a custom-defined class for this. - # Then, if it doesn't, use this class's default trace - base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true) - # Prepare the default trace class if it hasn't been initialized yet - base_class ||= (own_trace_modes[:default] = build_trace_mode(:default)) - mods = trace_modules_for(mode) - if base_class < DefaultTraceClass - mods = trace_modules_for(:default) + mods - end - # Copy the existing default options into this mode's options - default_options = trace_options_for(:default) - add_trace_options_for(mode, default_options) - - Class.new(base_class) do - !mods.empty? && include(*mods) - end - end - end - - def own_trace_modules - @own_trace_modules ||= Hash.new { |h, k| h[k] = [] } - end - - # @return [Array] Modules added for tracing in `trace_mode`, including inherited ones - def trace_modules_for(trace_mode) - modules = own_trace_modules[trace_mode] - if superclass.respond_to?(:trace_modules_for) - modules += superclass.trace_modules_for(trace_mode) - end - modules - end - - - # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}. - # @see {#as_json} - # @return [String] - def to_json(**args) - JSON.pretty_generate(as_json(**args)) - end - - # Return the Hash response of {Introspection::INTROSPECTION_QUERY}. - # @param context [Hash] - # @param only [<#call(member, ctx)>] - # @param except [<#call(member, ctx)>] - # @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response - # @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response - # @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives - # @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response - # @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects - # @return [Hash] GraphQL result - def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false) - introspection_query = Introspection.query( - include_deprecated_args: include_deprecated_args, - include_schema_description: include_schema_description, - include_is_repeatable: include_is_repeatable, - include_is_one_of: include_is_one_of, - include_specified_by_url: include_specified_by_url, - ) - - execute(introspection_query, context: context).to_h - end - - # Return the GraphQL IDL for the schema - # @param context [Hash] - # @return [String] - def to_definition(context: {}) - GraphQL::Schema::Printer.print_schema(self, context: context) - end - - # Return the GraphQL::Language::Document IDL AST for the schema - # @return [GraphQL::Language::Document] - def to_document - GraphQL::Language::DocumentFromSchemaDefinition.new(self).document - end - - # @return [String, nil] - def description(new_description = nil) - if new_description - @description = new_description - elsif defined?(@description) - @description - else - find_inherited_value(:description, nil) - end - end - - def find(path) - if !@finder - @find_cache = {} - @finder ||= GraphQL::Schema::Finder.new(self) - end - @find_cache[path] ||= @finder.find(path) - end - - def static_validator - GraphQL::StaticValidation::Validator.new(schema: self) - end - - # Add `plugin` to this schema - # @param plugin [#use] A Schema plugin - # @return void - def use(plugin, **kwargs) - if !kwargs.empty? - plugin.use(self, **kwargs) - else - plugin.use(self) - end - own_plugins << [plugin, kwargs] - end - - def plugins - find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins - end - - # Build a map of `{ name => type }` and return it - # @return [Hash Class>] A dictionary of type classes by their GraphQL name - # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes. - def types(context = GraphQL::Query::NullContext.instance) - if use_visibility_profile? - types = Visibility::Profile.from_context(context, self) - return types.all_types_h - end - all_types = non_introspection_types.merge(introspection_system.types) - visible_types = {} - all_types.each do |k, v| - visible_types[k] =if v.is_a?(Array) - visible_t = nil - v.each do |t| - if t.visible?(context) - if visible_t.nil? - visible_t = t - else - raise DuplicateNamesError.new( - duplicated_name: k, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect - ) - end - end - end - visible_t - else - v - end - end - visible_types - end - - # @param type_name [String] - # @param context [GraphQL::Query::Context] Used for filtering definitions at query-time - # @param use_visibility_profile Private, for migration to {Schema::Visibility} - # @return [Module, nil] A type, or nil if there's no type called `type_name` - def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?) - if use_visibility_profile - return Visibility::Profile.from_context(context, self).type(type_name) - end - local_entry = own_types[type_name] - type_defn = case local_entry - when nil - nil - when Array - if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile) - local_entry - else - visible_t = nil - warden = Warden.from_context(context) - local_entry.each do |t| - if warden.visible_type?(t, context) - if visible_t.nil? - visible_t = t - else - raise DuplicateNamesError.new( - duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect - ) - end - end - end - visible_t - end - when Module - local_entry - else - raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}" - end - - type_defn || - introspection_system.types[type_name] || # todo context-specific introspection? - (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil) - end - - # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility? - def has_defined_type?(type_name) - own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false) - end - - # @api private - attr_writer :connections - - # @return [GraphQL::Pagination::Connections] if installed - def connections - if defined?(@connections) - @connections - else - inherited_connections = find_inherited_value(:connections, nil) - # This schema is part of an inheritance chain which is using new connections, - # make a new instance, so we don't pollute the upstream one. - if inherited_connections - @connections = Pagination::Connections.new(schema: self) - else - nil - end - end - end - - # Get or set the root `query { ... }` object for this schema. - # - # @example Using `Types::Query` as the entry-point - # query { Types::Query } - # - # @param new_query_object [Class] The root type to use for queries - # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root query type. - # @return [Class, nil] The configured query root type, if there is one. - def query(new_query_object = nil, &lazy_load_block) - if new_query_object || block_given? - if @query_object - dup_defn = new_query_object || yield - raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}" - elsif use_visibility_profile? - if block_given? - if visibility.preload? - @query_object = lazy_load_block.call - self.visibility.query_configured(@query_object) - else - @query_object = lazy_load_block - end - else - @query_object = new_query_object - self.visibility.query_configured(@query_object) - end - else - @query_object = new_query_object || lazy_load_block.call - add_type_and_traverse(@query_object, root: true) - end - nil - elsif @query_object.is_a?(Proc) - @query_object = @query_object.call - self.visibility&.query_configured(@query_object) - @query_object - else - @query_object || find_inherited_value(:query) - end - end - - # Get or set the root `mutation { ... }` object for this schema. - # - # @example Using `Types::Mutation` as the entry-point - # mutation { Types::Mutation } - # - # @param new_mutation_object [Class] The root type to use for mutations - # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type. - # @return [Class, nil] The configured mutation root type, if there is one. - def mutation(new_mutation_object = nil, &lazy_load_block) - if new_mutation_object || block_given? - if @mutation_object - dup_defn = new_mutation_object || yield - raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}" - elsif use_visibility_profile? - if block_given? - if visibility.preload? - @mutation_object = lazy_load_block.call - self.visibility.mutation_configured(@mutation_object) - else - @mutation_object = lazy_load_block - end - else - @mutation_object = new_mutation_object - self.visibility.mutation_configured(@mutation_object) - end - else - @mutation_object = new_mutation_object || lazy_load_block.call - add_type_and_traverse(@mutation_object, root: true) - end - nil - elsif @mutation_object.is_a?(Proc) - @mutation_object = @mutation_object.call - self.visibility&.mutation_configured(@mutation_object) - @mutation_object - else - @mutation_object || find_inherited_value(:mutation) - end - end - - # Get or set the root `subscription { ... }` object for this schema. - # - # @example Using `Types::Subscription` as the entry-point - # subscription { Types::Subscription } - # - # @param new_subscription_object [Class] The root type to use for subscriptions - # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type. - # @return [Class, nil] The configured subscription root type, if there is one. - def subscription(new_subscription_object = nil, &lazy_load_block) - if new_subscription_object || block_given? - if @subscription_object - dup_defn = new_subscription_object || yield - raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}" - elsif use_visibility_profile? - if block_given? - if visibility.preload? - @subscription_object = lazy_load_block.call - visibility.subscription_configured(@subscription_object) - else - @subscription_object = lazy_load_block - end - else - @subscription_object = new_subscription_object - self.visibility.subscription_configured(@subscription_object) - end - add_subscription_extension_if_necessary - else - @subscription_object = new_subscription_object || lazy_load_block.call - add_subscription_extension_if_necessary - add_type_and_traverse(@subscription_object, root: true) - end - nil - elsif @subscription_object.is_a?(Proc) - @subscription_object = @subscription_object.call - add_subscription_extension_if_necessary - self.visibility.subscription_configured(@subscription_object) - @subscription_object - else - @subscription_object || find_inherited_value(:subscription) - end - end - - # @api private - def root_type_for_operation(operation) - case operation - when "query" - query - when "mutation" - mutation - when "subscription" - subscription - else - raise ArgumentError, "unknown operation type: #{operation}" - end - end - - # @return [Array] The root types (query, mutation, subscription) defined for this schema - def root_types - if use_visibility_profile? - [query, mutation, subscription].compact - else - @root_types - end - end - - # @api private - def warden_class - if defined?(@warden_class) - @warden_class - elsif superclass.respond_to?(:warden_class) - superclass.warden_class - else - GraphQL::Schema::Warden - end - end - - # @api private - attr_writer :warden_class - - # @api private - def visibility_profile_class - if defined?(@visibility_profile_class) - @visibility_profile_class - elsif superclass.respond_to?(:visibility_profile_class) - superclass.visibility_profile_class - else - GraphQL::Schema::Visibility::Profile - end - end - - # @api private - attr_writer :visibility_profile_class, :use_visibility_profile - # @api private - attr_accessor :visibility - # @api private - def use_visibility_profile? - if defined?(@use_visibility_profile) - @use_visibility_profile - elsif superclass.respond_to?(:use_visibility_profile?) - superclass.use_visibility_profile? - else - false - end - end - - # @param type [Module] The type definition whose possible types you want to see - # @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime - # @param use_visibility_profile Private, for migration to {Schema::Visibility} - # @return [Hash] All possible types, if no `type` is given. - # @return [Array] Possible types for `type`, if it's given. - def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?) - if use_visibility_profile - if type - return Visibility::Profile.from_context(context, self).possible_types(type) - else - raise "Schema.possible_types is not implemented for `use_visibility_profile?`" - end - end - if type - # TODO duck-typing `.possible_types` would probably be nicer here - if type.kind.union? - type.possible_types(context: context) - else - stored_possible_types = own_possible_types[type] - visible_possible_types = if stored_possible_types && type.kind.interface? - stored_possible_types.select do |possible_type| - possible_type.interfaces(context).include?(type) - end - else - stored_possible_types - end - visible_possible_types || - introspection_system.possible_types[type] || - ( - superclass.respond_to?(:possible_types) ? - superclass.possible_types(type, context, use_visibility_profile) : - EMPTY_ARRAY - ) - end - else - find_inherited_value(:possible_types, EMPTY_HASH) - .merge(own_possible_types) - .merge(introspection_system.possible_types) - end - end - - def union_memberships(type = nil) - if type - own_um = own_union_memberships.fetch(type.graphql_name, EMPTY_ARRAY) - inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY) - own_um + inherited_um - else - joined_um = own_union_memberships.dup - find_inherited_value(:union_memberhips, EMPTY_HASH).each do |k, v| - um = joined_um[k] ||= [] - um.concat(v) - end - joined_um - end - end - - # @api private - # @see GraphQL::Dataloader - def dataloader_class - @dataloader_class || GraphQL::Dataloader::NullDataloader - end - - attr_writer :dataloader_class - - def references_to(to_type = nil, from: nil) - if to_type - if from - refs = own_references_to[to_type] ||= [] - refs << from - else - get_references_to(to_type) || EMPTY_ARRAY - end - else - # `@own_references_to` can be quite large for big schemas, - # and generally speaking, we won't inherit any values. - # So optimize the most common case -- don't create a duplicate Hash. - inherited_value = find_inherited_value(:references_to, EMPTY_HASH) - if !inherited_value.empty? - inherited_value.merge(own_references_to) - else - own_references_to - end - end - end - - def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context) - GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node) - end - - def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance) - parent_type = case type_or_name - when LateBoundType - get_type(type_or_name.name, context) - when String - get_type(type_or_name, context) - when Module - type_or_name - else - raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})" - end - - if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context)) - field - elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name)) - entry_point_field - elsif (dynamic_field = introspection_system.dynamic_field(name: field_name)) - dynamic_field - else - nil - end - end - - def get_fields(type, context = GraphQL::Query::NullContext.instance) - type.fields(context) - end - - # Pass a custom introspection module here to use it for this schema. - # @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema - # @return [Module, nil] The configured namespace, if there is one - def introspection(new_introspection_namespace = nil) - if new_introspection_namespace - @introspection = new_introspection_namespace - # reset this cached value: - @introspection_system = nil - introspection_system - @introspection - else - @introspection || find_inherited_value(:introspection) - end - end - - # @return [Schema::IntrospectionSystem] Based on {introspection} - def introspection_system - if !@introspection_system - @introspection_system = Schema::IntrospectionSystem.new(self) - @introspection_system.resolve_late_bindings - self.visibility&.introspection_system_configured(@introspection_system) - end - @introspection_system - end - - def cursor_encoder(new_encoder = nil) - if new_encoder - @cursor_encoder = new_encoder - end - @cursor_encoder || find_inherited_value(:cursor_encoder, Base64Encoder) - end - - def default_max_page_size(new_default_max_page_size = nil) - if new_default_max_page_size - @default_max_page_size = new_default_max_page_size - else - @default_max_page_size || find_inherited_value(:default_max_page_size) - end - end - - # A limit on the number of tokens to accept on incoming query strings. - # Use this to prevent parsing maliciously-large query strings. - # @return [nil, Integer] - def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_max_tokens) - defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens) - else - @max_query_string_tokens = new_max_tokens - end - end - - def default_page_size(new_default_page_size = nil) - if new_default_page_size - @default_page_size = new_default_page_size - else - @default_page_size || find_inherited_value(:default_page_size) - end - end - - def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true) - if deprecation_warning - warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead." - warn " #{caller(1, 1).first}" - end - if new_query_execution_strategy - @query_execution_strategy = new_query_execution_strategy - else - @query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy) - end - end - - def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true) - if deprecation_warning - warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead." - warn " #{caller(1, 1).first}" - end - if new_mutation_execution_strategy - @mutation_execution_strategy = new_mutation_execution_strategy - else - @mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy) - end - end - - def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true) - if deprecation_warning - warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead." - warn " #{caller(1, 1).first}" - end - if new_subscription_execution_strategy - @subscription_execution_strategy = new_subscription_execution_strategy - else - @subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy) - end - end - - attr_writer :validate_timeout - - def validate_timeout(new_validate_timeout = nil) - if new_validate_timeout - @validate_timeout = new_validate_timeout - elsif defined?(@validate_timeout) - @validate_timeout - else - find_inherited_value(:validate_timeout) - end - end - - # Validate a query string according to this schema. - # @param string_or_document [String, GraphQL::Language::Nodes::Document] - # @return [Array] - def validate(string_or_document, rules: nil, context: nil) - doc = if string_or_document.is_a?(String) - GraphQL.parse(string_or_document) - else - string_or_document - end - query = query_class.new(self, document: doc, context: context) - validator_opts = { schema: self } - rules && (validator_opts[:rules] = rules) - validator = GraphQL::StaticValidation::Validator.new(**validator_opts) - res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors) - res[:errors] - end - - # @param new_query_class [Class] A subclass to use when executing queries - def query_class(new_query_class = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_query_class) - @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query) - else - @query_class = new_query_class - end - end - - attr_writer :validate_max_errors - - def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_validate_max_errors) - defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors) - else - @validate_max_errors = new_validate_max_errors - end - end - - attr_writer :max_complexity - - def max_complexity(max_complexity = nil, count_introspection_fields: true) - if max_complexity - @max_complexity = max_complexity - @max_complexity_count_introspection_fields = count_introspection_fields - elsif defined?(@max_complexity) - @max_complexity - else - find_inherited_value(:max_complexity) - end - end - - def max_complexity_count_introspection_fields - if defined?(@max_complexity_count_introspection_fields) - @max_complexity_count_introspection_fields - else - find_inherited_value(:max_complexity_count_introspection_fields, true) - end - end - - attr_writer :analysis_engine - - def analysis_engine - @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine) - end - - def error_bubbling(new_error_bubbling = nil) - if !new_error_bubbling.nil? - warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0") - @error_bubbling = new_error_bubbling - else - @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling - end - end - - attr_writer :error_bubbling - - attr_writer :max_depth - - def max_depth(new_max_depth = nil, count_introspection_fields: true) - if new_max_depth - @max_depth = new_max_depth - @count_introspection_fields = count_introspection_fields - elsif defined?(@max_depth) - @max_depth - else - find_inherited_value(:max_depth) - end - end - - def count_introspection_fields - if defined?(@count_introspection_fields) - @count_introspection_fields - else - find_inherited_value(:count_introspection_fields, true) - end - end - - def disable_introspection_entry_points - @disable_introspection_entry_points = true - # TODO: this clears the cache made in `def types`. But this is not a great solution. - @introspection_system = nil - end - - def disable_schema_introspection_entry_point - @disable_schema_introspection_entry_point = true - # TODO: this clears the cache made in `def types`. But this is not a great solution. - @introspection_system = nil - end - - def disable_type_introspection_entry_point - @disable_type_introspection_entry_point = true - # TODO: this clears the cache made in `def types`. But this is not a great solution. - @introspection_system = nil - end - - def disable_introspection_entry_points? - if instance_variable_defined?(:@disable_introspection_entry_points) - @disable_introspection_entry_points - else - find_inherited_value(:disable_introspection_entry_points?, false) - end - end - - def disable_schema_introspection_entry_point? - if instance_variable_defined?(:@disable_schema_introspection_entry_point) - @disable_schema_introspection_entry_point - else - find_inherited_value(:disable_schema_introspection_entry_point?, false) - end - end - - def disable_type_introspection_entry_point? - if instance_variable_defined?(:@disable_type_introspection_entry_point) - @disable_type_introspection_entry_point - else - find_inherited_value(:disable_type_introspection_entry_point?, false) - end - end - - # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema - # @return [Array] Type definitions added to this schema - def extra_types(*new_extra_types) - if !new_extra_types.empty? - new_extra_types = new_extra_types.flatten - @own_extra_types ||= [] - @own_extra_types.concat(new_extra_types) - end - inherited_et = find_inherited_value(:extra_types, nil) - if inherited_et - if @own_extra_types - inherited_et + @own_extra_types - else - inherited_et - end - else - @own_extra_types || EMPTY_ARRAY - end - end - - # Tell the schema about these types so that they can be registered as implementations of interfaces in the schema. - # - # This method must be used when an object type is connected to the schema as an interface implementor but - # not as a return type of a field. In that case, if the object type isn't registered here, GraphQL-Ruby won't be able to find it. - # - # @param new_orphan_types [Array>] Object types to register as implementations of interfaces in the schema. - # @return [Array>] All previously-registered orphan types for this schema - def orphan_types(*new_orphan_types) - if !new_orphan_types.empty? - new_orphan_types = new_orphan_types.flatten - non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object } - if !non_object_types.empty? - raise ArgumentError, <<~ERR - Only object type classes should be added as `orphan_types(...)`. - - - Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")} - - See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types - - To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types - ERR - end - add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile? - own_orphan_types.concat(new_orphan_types.flatten) - self.visibility&.orphan_types_configured(new_orphan_types) - end - - inherited_ot = find_inherited_value(:orphan_types, nil) - if inherited_ot - if !own_orphan_types.empty? - inherited_ot + own_orphan_types - else - inherited_ot - end - else - own_orphan_types - end - end - - def default_execution_strategy - if superclass <= GraphQL::Schema - superclass.default_execution_strategy - else - @default_execution_strategy ||= GraphQL::Execution::Interpreter - end - end - - def default_analysis_engine - if superclass <= GraphQL::Schema - superclass.default_analysis_engine - else - @default_analysis_engine ||= GraphQL::Analysis::AST - end - end - - - # @param new_default_logger [#log] Something to use for logging messages - def default_logger(new_default_logger = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_default_logger) - if defined?(@default_logger) - @default_logger - elsif superclass.respond_to?(:default_logger) - superclass.default_logger - elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger) - rails_logger - else - def_logger = Logger.new($stdout) - def_logger.info! # It doesn't output debug info by default - def_logger - end - elsif new_default_logger == nil - @default_logger = Logger.new(IO::NULL) - else - @default_logger = new_default_logger - end - end - - # @param new_context_class [Class] A subclass to use when executing queries - def context_class(new_context_class = nil) - if new_context_class - @context_class = new_context_class - else - @context_class || find_inherited_value(:context_class, GraphQL::Query::Context) - end - end - - # Register a handler for errors raised during execution. The handlers can return a new value or raise a new error. - # - # @example Handling "not found" with a client-facing error - # rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" } - # - # @param err_classes [Array] Classes which should be rescued by `handler_block` - # @param handler_block The code to run when one of those errors is raised during execution - # @yieldparam error [StandardError] An instance of one of the configured `err_classes` - # @yieldparam object [Object] The current application object in the query when the error was raised - # @yieldparam arguments [GraphQL::Query::Arguments] The current field arguments when the error was raised - # @yieldparam context [GraphQL::Query::Context] The context for the currently-running operation - # @yieldreturn [Object] Some object to use in the place where this error was raised - # @raise [GraphQL::ExecutionError] In the handler, raise to add a client-facing error to the response - # @raise [StandardError] In the handler, raise to crash the query with a developer-facing error - def rescue_from(*err_classes, &handler_block) - err_classes.each do |err_class| - Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block) - end - end - - NEW_HANDLER_HASH = ->(h, k) { - h[k] = { - class: k, - handler: nil, - subclass_handlers: Hash.new(&NEW_HANDLER_HASH), - } - } - - def error_handlers - @error_handlers ||= { - class: nil, - handler: nil, - subclass_handlers: Hash.new(&NEW_HANDLER_HASH), - } - end - - # @api private - attr_accessor :using_backtrace - - # @api private - def handle_or_reraise(context, err) - handler = Execution::Errors.find_handler_for(self, err.class) - if handler - obj = context[:current_object] - args = context[:current_arguments] - args = args && args.respond_to?(:keyword_arguments) ? args.keyword_arguments : nil - field = context[:current_field] - if obj.is_a?(GraphQL::Schema::Object) - obj = obj.object - end - handler[:handler].call(err, obj, args, context, field) - else - if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError) - err = GraphQL::Backtrace::TracedError.new(err, context) - end - - raise err - end - end - - # rubocop:disable Lint/DuplicateMethods - module ResolveTypeWithType - def resolve_type(type, obj, ctx) - maybe_lazy_resolve_type_result = if type.is_a?(Module) && type.respond_to?(:resolve_type) - type.resolve_type(obj, ctx) - else - super - end - - after_lazy(maybe_lazy_resolve_type_result) do |resolve_type_result| - if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2 - resolved_type = resolve_type_result[0] - resolved_value = resolve_type_result[1] - else - resolved_type = resolve_type_result - resolved_value = obj - end - - if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind)) - [resolved_type, resolved_value] - else - raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`" - end - end - end - end - - # GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object. - # - # Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}. - # But this method is called in other cases, too -- for example, when {GraphQL::Schema::Argument.loads} cases an object to be directly loaded from the database. - # - # @example Returning a GraphQL type based on the object's class name - # class MySchema < GraphQL::Schema - # def resolve_type(_abs_type, object, _context) - # graphql_type_name = "Types::#{object.class.name}Type" - # graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method - # end - # end - # @param abstract_type [Class, Module, nil] The Interface or Union type which is being resolved, if there is one - # @param application_object [Object] The object returned from a field whose type must be determined - # @param context [GraphQL::Query::Context] The query context for the currently-executing query - # @return [Class GraphQL::Schema::Directive::Include, - "skip" => GraphQL::Schema::Directive::Skip, - "deprecated" => GraphQL::Schema::Directive::Deprecated, - "oneOf" => GraphQL::Schema::Directive::OneOf, - "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy, - }.freeze - end - - # @return [GraphQL::Tracing::DetailedTrace] if it has been configured for this schema - attr_accessor :detailed_trace - - # @param query [GraphQL::Query, GraphQL::Execution::Multiplex] Called with a multiplex when multiple queries are executed at once (with {.multiplex}) - # @return [Boolean] When `true`, save a detailed trace for this query. - # @see Tracing::DetailedTrace DetailedTrace saves traces when this method returns true - def detailed_trace?(query) - raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition." - end - - def tracer(new_tracer, silence_deprecation_warning: false) - if !silence_deprecation_warning - warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html") - warn " #{caller(1, 1).first}" - end - default_trace = trace_class_for(:default, build: true) - if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers) - trace_with(GraphQL::Tracing::CallLegacyTracers) - end - - own_tracers << new_tracer - end - - def tracers - find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers - end - - # Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime. - # - # You can attach a module to run in only _some_ circumstances by using `mode:`. When a module is added with `mode:`, - # it will only run for queries with a matching `context[:trace_mode]`. - # - # Any custom trace modes _also_ include the default `trace_with ...` modules (that is, those added _without_ any particular `mode: ...` configuration). - # - # @example Adding a trace in a special mode - # # only runs when `query.context[:trace_mode]` is `:special` - # trace_with SpecialTrace, mode: :special - # - # @param trace_mod [Module] A module that implements tracing methods - # @param mode [Symbol] Trace module will only be used for this trade mode - # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize` - # @return [void] - # @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods - def trace_with(trace_mod, mode: :default, **options) - if mode.is_a?(Array) - mode.each { |m| trace_with(trace_mod, mode: m, **options) } - else - tc = own_trace_modes[mode] ||= build_trace_mode(mode) - tc.include(trace_mod) - own_trace_modules[mode] << trace_mod - add_trace_options_for(mode, options) - if mode == :default - # This module is being added as a default tracer. If any other mode classes - # have already been created, but get their default behavior from a superclass, - # Then mix this into this schema's subclass. - # (But don't mix it into mode classes that aren't default-based.) - own_trace_modes.each do |other_mode_name, other_mode_class| - if other_mode_class < DefaultTraceClass - # Don't add it back to the inheritance tree if it's already there - if !(other_mode_class < trace_mod) - other_mode_class.include(trace_mod) - end - # Add any options so they'll be available - add_trace_options_for(other_mode_name, options) - end - end - end - end - nil - end - - # The options hash for this trace mode - # @return [Hash] - def trace_options_for(mode) - @trace_options_for_mode ||= {} - @trace_options_for_mode[mode] ||= begin - # It may be time to create an options hash for a mode that wasn't registered yet. - # Mix in the default options in that case. - default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default) - # Make sure this returns a new object so that other hashes aren't modified later - if superclass.respond_to?(:trace_options_for) - superclass.trace_options_for(mode).merge(default_options) - else - default_options.dup - end - end - end - - # Create a trace instance which will include the trace modules specified for the optional mode. - # - # If no `mode:` is given, then {default_trace_mode} will be used. - # - # If this schema is using {Tracing::DetailedTrace} and {.detailed_trace?} returns `true`, then - # DetailedTrace's mode will override the passed-in `mode`. - # - # @param mode [Symbol] Trace modules for this trade mode will be included - # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize` - # @return [Tracing::Trace] - def new_trace(mode: nil, **options) - should_sample = if detailed_trace - if (query = options[:query]) - detailed_trace?(query) - elsif (multiplex = options[:multiplex]) - if multiplex.queries.length == 1 - detailed_trace?(multiplex.queries.first) - else - detailed_trace?(multiplex) - end - end - else - false - end - - if should_sample - mode = detailed_trace.trace_mode - else - target = options[:query] || options[:multiplex] - mode ||= target && target.context[:trace_mode] - end - - trace_mode = mode || default_trace_mode - base_trace_options = trace_options_for(trace_mode) - trace_options = base_trace_options.merge(options) - trace_class_for_mode = trace_class_for(trace_mode, build: true) - trace_class_for_mode.new(**trace_options) - end - - # @param new_analyzer [Class] An analyzer to run on queries to this schema - # @see GraphQL::Analysis the analysis system - def query_analyzer(new_analyzer) - own_query_analyzers << new_analyzer - end - - def query_analyzers - find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers - end - - # @param new_analyzer [Class] An analyzer to run on multiplexes to this schema - # @see GraphQL::Analysis the analysis system - def multiplex_analyzer(new_analyzer) - own_multiplex_analyzers << new_analyzer - end - - def multiplex_analyzers - find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers - end - - def sanitized_printer(new_sanitized_printer = nil) - if new_sanitized_printer - @own_sanitized_printer = new_sanitized_printer - else - @own_sanitized_printer || GraphQL::Language::SanitizedPrinter - end - end - - # Execute a query on itself. - # @see {Query#initialize} for arguments. - # @return [GraphQL::Query::Result] query result, ready to be serialized as JSON - def execute(query_str = nil, **kwargs) - if query_str - kwargs[:query] = query_str - end - # Some of the query context _should_ be passed to the multiplex, too - multiplex_context = if (ctx = kwargs[:context]) - { - backtrace: ctx[:backtrace], - tracers: ctx[:tracers], - trace: ctx[:trace], - dataloader: ctx[:dataloader], - trace_mode: ctx[:trace_mode], - } - else - {} - end - # Since we're running one query, don't run a multiplex-level complexity analyzer - all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context) - all_results[0] - end - - # Execute several queries on itself, concurrently. - # - # @example Run several queries at once - # context = { ... } - # queries = [ - # { query: params[:query_1], variables: params[:variables_1], context: context }, - # { query: params[:query_2], variables: params[:variables_2], context: context }, - # ] - # results = MySchema.multiplex(queries) - # render json: { - # result_1: results[0], - # result_2: results[1], - # } - # - # @see {Query#initialize} for query keyword arguments - # @see {Execution::Multiplex#run_all} for multiplex keyword arguments - # @param queries [Array] Keyword arguments for each query - # @param context [Hash] Multiplex-level context - # @return [Array] One result for each query in the input - def multiplex(queries, **kwargs) - GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs) - end - - def instrumenters - inherited_instrumenters = find_inherited_value(:instrumenters) || Hash.new { |h,k| h[k] = [] } - inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own| - inherited + own - end - end - - # @api private - def add_subscription_extension_if_necessary - # TODO: when there's a proper API for extending root types, migrat this to use it. - if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions - @subscription_extension_added = true - subscription.all_field_definitions.each do |field| - if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) } - field.extension(Subscriptions::DefaultSubscriptionResolveExtension) - end - end - end - end - - # Called when execution encounters a `SystemStackError`. By default, it adds a client-facing error to the response. - # You could modify this method to report this error to your bug tracker. - # @param query [GraphQL::Query] - # @param err [SystemStackError] - # @return [void] - def query_stack_error(query, err) - query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute.")) - end - - # Call the given block at the right time, either: - # - Right away, if `value` is not registered with `lazy_resolve` - # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`) - # @api private - def after_lazy(value, &block) - if lazy?(value) - GraphQL::Execution::Lazy.new do - result = sync_lazy(value) - # The returned result might also be lazy, so check it, too - after_lazy(result, &block) - end - else - yield(value) if block_given? - end - end - - # Override this method to handle lazy objects in a custom way. - # @param value [Object] an instance of a class registered with {.lazy_resolve} - # @return [Object] A GraphQL-ready (non-lazy) object - # @api private - def sync_lazy(value) - lazy_method = lazy_method_name(value) - if lazy_method - synced_value = value.public_send(lazy_method) - sync_lazy(synced_value) - else - value - end - end - - # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}. - def lazy_method_name(obj) - lazy_methods.get(obj) - end - - # @return [Boolean] True if this object should be lazily resolved - def lazy?(obj) - !!lazy_method_name(obj) - end - - # Return a lazy if any of `maybe_lazies` are lazy, - # otherwise, call the block eagerly and return the result. - # @param maybe_lazies [Array] - # @api private - def after_any_lazies(maybe_lazies) - if maybe_lazies.any? { |l| lazy?(l) } - GraphQL::Execution::Lazy.all(maybe_lazies).then do |result| - yield result - end - else - yield maybe_lazies - end - end - - # Returns `DidYouMean` if it's defined. - # Override this to return `nil` if you don't want to use `DidYouMean` - def did_you_mean(new_dym = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_dym) - if defined?(@did_you_mean) - @did_you_mean - else - find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil) - end - else - @did_you_mean = new_dym - end - end - - private - - def add_trace_options_for(mode, new_options) - if mode == :default - own_trace_modes.each do |mode_name, t_class| - if t_class <= DefaultTraceClass - t_opts = trace_options_for(mode_name) - t_opts.merge!(new_options) - end - end - else - t_opts = trace_options_for(mode) - t_opts.merge!(new_options) - end - nil - end - - # @param t [Module, Array] - # @return [void] - def add_type_and_traverse(t, root:) - if root - @root_types ||= [] - @root_types << t - end - new_types = Array(t) - addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types) - addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time - if (prev_entry = own_types[name]) - prev_entries = case prev_entry - when Array - prev_entry - when Module - own_types[name] = [prev_entry] - else - raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}" - end - - case types_entry - when Array - prev_entries.concat(types_entry) - prev_entries.uniq! # in case any are being re-visited - when Module - if !prev_entries.include?(types_entry) - prev_entries << types_entry - end - else - raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}" - end - else - if types_entry.is_a?(Array) - types_entry.uniq! - end - own_types[name] = types_entry - end - end - - own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val } - own_union_memberships.merge!(addition.union_memberships) - - addition.references.each { |thing, pointers| - prev_refs = own_references_to[thing] || [] - own_references_to[thing] = prev_refs | pointers.to_a - } - - addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class } - - addition.arguments_with_default_values.each do |arg| - arg.validate_default_value - end - end - - def lazy_methods - if !defined?(@lazy_methods) - if inherited_map = find_inherited_value(:lazy_methods) - # this isn't _completely_ inherited :S (Things added after `dup` won't work) - @lazy_methods = inherited_map.dup - else - @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new - @lazy_methods.set(GraphQL::Execution::Lazy, :value) - @lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning) - end - end - @lazy_methods - end - - def own_types - @own_types ||= {} - end - - def own_references_to - @own_references_to ||= {}.compare_by_identity - end - - def non_introspection_types - find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types) - end - - def own_plugins - @own_plugins ||= [] - end - - def own_orphan_types - @own_orphan_types ||= [] - end - - def own_possible_types - @own_possible_types ||= {}.compare_by_identity - end - - def own_union_memberships - @own_union_memberships ||= {} - end - - def own_directives - @own_directives ||= {} - end - - def own_instrumenters - @own_instrumenters ||= Hash.new { |h,k| h[k] = [] } - end - - def own_tracers - @own_tracers ||= [] - end - - def own_query_analyzers - @defined_query_analyzers ||= [] - end - - def own_multiplex_analyzers - @own_multiplex_analyzers ||= [] - end - - # This is overridden in subclasses to check the inheritance chain - def get_references_to(type_defn) - own_references_to[type_defn] - end - end - - module SubclassGetReferencesTo - def get_references_to(type_defn) - own_refs = own_references_to[type_defn] - inherited_refs = superclass.references_to(type_defn) - if inherited_refs&.any? - if own_refs&.any? - own_refs + inherited_refs - else - inherited_refs - end - else - own_refs - end - end - end - - # Install these here so that subclasses will also install it. - self.connections = GraphQL::Pagination::Connections.new(schema: self) - - # @api private - module DefaultTraceClass - end - end -end - -require "graphql/schema/loader" -require "graphql/schema/printer" diff --git a/vendor/gems/graphql/lib/graphql/schema/addition.rb b/vendor/gems/graphql/lib/graphql/schema/addition.rb deleted file mode 100644 index 2ffd289d42a..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/addition.rb +++ /dev/null @@ -1,282 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Addition - attr_reader :directives, :possible_types, :types, :union_memberships, :references, :arguments_with_default_values - - def initialize(schema:, own_types:, new_types:) - @schema = schema - @own_types = own_types - @directives = Set.new - @possible_types = {} - @types = {} - @union_memberships = {} - @references = Hash.new { |h, k| h[k] = Set.new } - @arguments_with_default_values = [] - add_type_and_traverse(new_types) - end - - private - - def references_to(thing, from:) - @references[thing].add(from) - end - - def get_type(name) - local_type = @types[name] - # This isn't really sophisticated, but - # I think it's good enough to support the current usage of LateBoundTypes - if local_type.is_a?(Array) - local_type = local_type.first - end - local_type || @schema.get_type(name) - end - - # Lookup using `own_types` here because it's ok to override - # inherited types by name - def get_local_type(name) - @types[name] || @own_types[name] - end - - def add_directives_from(owner) - if !(dir_instances = owner.directives).empty? - dirs = dir_instances.map(&:class) - @directives.merge(dirs) - add_type_and_traverse(dirs) - end - end - - def add_type_and_traverse(new_types) - late_types = [] - path = [] - new_types.each do |t| - path.push(t.graphql_name) - add_type(t, owner: nil, late_types: late_types, path: path) - path.pop - end - missed_late_types = 0 - while (late_type_vals = late_types.shift) - type_owner, lt = late_type_vals - if lt.is_a?(String) - type = Member::BuildType.constantize(lt) - # Reset the counter, since we might succeed next go-round - missed_late_types = 0 - update_type_owner(type_owner, type) - add_type(type, owner: type_owner, late_types: late_types, path: [type.graphql_name]) - elsif lt.is_a?(LateBoundType) - if (type = get_type(lt.name)) - # Reset the counter, since we might succeed next go-round - missed_late_types = 0 - update_type_owner(type_owner, type) - add_type(type, owner: type_owner, late_types: late_types, path: [type.graphql_name]) - else - missed_late_types += 1 - # Add it back to the list, maybe we'll be able to resolve it later. - late_types << [type_owner, lt] - if missed_late_types == late_types.size - # We've looked at all of them and haven't resolved one. - raise UnresolvedLateBoundTypeError.new(type: lt) - else - # Try the next one - end - end - else - raise ArgumentError, "Unexpected late type: #{lt.inspect}" - end - end - nil - end - - def update_type_owner(owner, type) - case owner - when Module - if owner.kind.union? - # It's a union with possible_types - # Replace the item by class name - owner.assign_type_membership_object_type(type) - @possible_types[owner] = owner.possible_types - elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?) - new_interfaces = [] - owner.interfaces.each do |int_t| - if int_t.is_a?(String) && int_t == type.graphql_name - new_interfaces << type - elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name - new_interfaces << type - else - # Don't re-add proper interface definitions, - # they were probably already added, maybe with options. - end - end - owner.implements(*new_interfaces) - new_interfaces.each do |int| - pt = @possible_types[int] ||= [] - if !pt.include?(owner) && owner.is_a?(Class) - pt << owner - end - int.interfaces.each do |indirect_int| - if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name)) - update_type_owner(owner, indirect_int_type) - end - end - end - end - when nil - # It's a root type - @types[type.graphql_name] = type - when GraphQL::Schema::Field, GraphQL::Schema::Argument - orig_type = owner.type - unwrapped_t = type - # Apply list/non-null wrapper as needed - if orig_type.respond_to?(:of_type) - transforms = [] - while (orig_type.respond_to?(:of_type)) - if orig_type.kind.non_null? - transforms << :to_non_null_type - elsif orig_type.kind.list? - transforms << :to_list_type - else - raise "Invariant: :of_type isn't non-null or list" - end - orig_type = orig_type.of_type - end - transforms.reverse_each { |t| type = type.public_send(t) } - end - owner.type = type - references_to(unwrapped_t, from: owner) - else - raise "Unexpected update: #{owner.inspect} #{type.inspect}" - end - end - - def add_type(type, owner:, late_types:, path:) - if type.is_a?(String) || type.is_a?(GraphQL::Schema::LateBoundType) - late_types << [owner, type] - return - end - - if owner.is_a?(Class) && owner < GraphQL::Schema::Union - um = @union_memberships[type.graphql_name] ||= [] - um << owner - end - - if (prev_type = get_local_type(type.graphql_name)) && (prev_type == type || (prev_type.is_a?(Array) && prev_type.include?(type))) - # No need to re-visit - elsif type.is_a?(Class) && type < GraphQL::Schema::Directive - @directives << type - type.all_argument_definitions.each do |arg| - arg_type = arg.type.unwrap - if !arg_type.is_a?(GraphQL::Schema::LateBoundType) - references_to(arg_type, from: arg) - end - path.push(arg.graphql_name) - add_type(arg_type, owner: arg, late_types: late_types, path: path) - path.pop - if arg.default_value? - @arguments_with_default_values << arg - end - end - else - prev_type = @types[type.graphql_name] - if prev_type.nil? - @types[type.graphql_name] = type - elsif prev_type.is_a?(Array) - prev_type << type - else - @types[type.graphql_name] = [prev_type, type] - end - - add_directives_from(type) - if type.kind.fields? - type.all_field_definitions.each do |field| - field.ensure_loaded - name = field.graphql_name - field_type = field.type.unwrap - if !field_type.is_a?(GraphQL::Schema::LateBoundType) - references_to(field_type, from: field) - end - path.push(name) - add_type(field_type, owner: field, late_types: late_types, path: path) - add_directives_from(field) - field.all_argument_definitions.each do |arg| - add_directives_from(arg) - arg_type = arg.type.unwrap - if !arg_type.is_a?(GraphQL::Schema::LateBoundType) - references_to(arg_type, from: arg) - end - path.push(arg.graphql_name) - add_type(arg_type, owner: arg, late_types: late_types, path: path) - path.pop - if arg.default_value? - @arguments_with_default_values << arg - end - end - path.pop - end - end - if type.kind.input_object? - type.all_argument_definitions.each do |arg| - add_directives_from(arg) - arg_type = arg.type.unwrap - if !arg_type.is_a?(GraphQL::Schema::LateBoundType) - references_to(arg_type, from: arg) - end - path.push(arg.graphql_name) - add_type(arg_type, owner: arg, late_types: late_types, path: path) - path.pop - if arg.default_value? - @arguments_with_default_values << arg - end - end - end - if type.kind.union? - @possible_types[type] = type.all_possible_types - path.push("possible_types") - type.all_possible_types.each do |t| - add_type(t, owner: type, late_types: late_types, path: path) - end - path.pop - end - if type.kind.interface? - path.push("orphan_types") - type.orphan_types.each do |t| - add_type(t, owner: type, late_types: late_types, path: path) - end - path.pop - end - if type.kind.object? - possible_types_for_this_name = @possible_types[type] ||= [] - possible_types_for_this_name << type - end - - if type.kind.object? || type.kind.interface? - path.push("implements") - type.interface_type_memberships.each do |interface_type_membership| - case interface_type_membership - when Schema::TypeMembership - interface_type = interface_type_membership.abstract_type - # We can get these now; we'll have to get late-bound types later - if interface_type.is_a?(Module) && type.is_a?(Class) - implementers = @possible_types[interface_type] ||= [] - implementers << type - end - when String, Schema::LateBoundType - interface_type = interface_type_membership - else - raise ArgumentError, "Invariant: unexpected type membership for #{type.graphql_name}: #{interface_type_membership.class} (#{interface_type_membership.inspect})" - end - add_type(interface_type, owner: type, late_types: late_types, path: path) - end - path.pop - end - - if type.kind.enum? - type.all_enum_value_definitions.each do |value_definition| - add_directives_from(value_definition) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/always_visible.rb b/vendor/gems/graphql/lib/graphql/schema/always_visible.rb deleted file mode 100644 index 20be78db4ad..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/always_visible.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - module AlwaysVisible - def self.use(schema, **opts) - schema.extend(self) - end - - def visible?(_member, _context) - true - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/argument.rb b/vendor/gems/graphql/lib/graphql/schema/argument.rb deleted file mode 100644 index fa43e5b0b56..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/argument.rb +++ /dev/null @@ -1,435 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Argument - include GraphQL::Schema::Member::HasPath - include GraphQL::Schema::Member::HasAstNode - include GraphQL::Schema::Member::HasDirectives - include GraphQL::Schema::Member::HasDeprecationReason - include GraphQL::Schema::Member::HasValidators - include GraphQL::EmptyObjects - - # @return [String] the GraphQL name for this argument, camelized unless `camelize: false` is provided - attr_reader :name - alias :graphql_name :name - - # @return [GraphQL::Schema::Field, Class] The field or input object this argument belongs to - attr_reader :owner - - # @param new_prepare [Method, Proc] - # @return [Symbol] A method or proc to call to transform this value before sending it to field resolution method - def prepare(new_prepare = NOT_CONFIGURED) - if new_prepare != NOT_CONFIGURED - @prepare = new_prepare - end - @prepare - end - - # @return [Symbol] This argument's name in Ruby keyword arguments - attr_reader :keyword - - # @return [Class, Module, nil] If this argument should load an application object, this is the type of object to load - attr_reader :loads - - # @return [Boolean] true if a resolver defined this argument - def from_resolver? - @from_resolver - end - - # @param arg_name [Symbol] - # @param type_expr - # @param desc [String] - # @param required [Boolean, :nullable] if true, this argument is non-null; if false, this argument is nullable. If `:nullable`, then the argument must be provided, though it may be `null`. - # @param description [String] - # @param default_value [Object] - # @param as [Symbol] Override the keyword name when passed to a method - # @param prepare [Symbol] A method to call to transform this argument's valuebefore sending it to field resolution - # @param camelize [Boolean] if true, the name will be camelized when building the schema - # @param from_resolver [Boolean] if true, a Resolver class defined this argument - # @param directives [Hash{Class => Hash}] - # @param deprecation_reason [String] - # @param validates [Hash, nil] Options for building validators, if any should be applied - # @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value` - def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, comment: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block) - arg_name ||= name - @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s) - NameValidator.validate!(@name) - @type_expr = type_expr || type - @description = desc || description - @comment = comment - @null = required != true - @default_value = default_value - if replace_null_with_default - if !default_value? - raise ArgumentError, "`replace_null_with_default: true` requires a default value, please provide one with `default_value: ...`" - end - @replace_null_with_default = true - end - - @owner = owner - @as = as - @loads = loads - @keyword = as || (arg_name.is_a?(Symbol) ? arg_name : Schema::Member::BuildType.underscore(@name).to_sym) - @prepare = prepare - @ast_node = ast_node - @from_resolver = from_resolver - self.deprecation_reason = deprecation_reason - - if directives - directives.each do |dir_class, dir_options| - directive(dir_class, **dir_options) - end - end - - if validates && !validates.empty? - self.validates(validates) - end - - if required == :nullable - self.owner.validates(required: { argument: arg_name }) - end - - if definition_block - # `self` will still be self, it will also be the first argument to the block: - instance_exec(self, &definition_block) - end - end - - def inspect - "#<#{self.class} #{path}: #{type.to_type_signature}#{description ? " @description=#{description.inspect}" : ""}>" - end - - # @param default_value [Object] The value to use when the client doesn't provide one - # @return [Object] the value used when the client doesn't provide a value for this argument - def default_value(new_default_value = NOT_CONFIGURED) - if new_default_value != NOT_CONFIGURED - @default_value = new_default_value - end - @default_value - end - - # @return [Boolean] True if this argument has a default value - def default_value? - @default_value != NOT_CONFIGURED - end - - def replace_null_with_default? - @replace_null_with_default - end - - attr_writer :description - - # @return [String] Documentation for this argument - def description(text = nil) - if text - @description = text - else - @description - end - end - - attr_writer :comment - - # @return [String] Comment for this argument - def comment(text = nil) - if text - @comment = text - else - @comment - end - end - - # @return [String] Deprecation reason for this argument - def deprecation_reason(text = nil) - if text - self.deprecation_reason = text - else - super() - end - end - - def deprecation_reason=(new_reason) - validate_deprecated_or_optional(null: @null, deprecation_reason: new_reason) - super - end - - def visible?(context) - true - end - - def authorized?(obj, value, ctx) - authorized_as_type?(obj, value, ctx, as_type: type) - end - - def authorized_as_type?(obj, value, ctx, as_type:) - if value.nil? - return true - end - - if as_type.kind.non_null? - as_type = as_type.of_type - end - - if as_type.kind.list? - value.each do |v| - if !authorized_as_type?(obj, v, ctx, as_type: as_type.of_type) - return false - end - end - elsif as_type.kind.input_object? - return as_type.authorized?(obj, value, ctx) - end - # None of the early-return conditions were activated, - # so this is authorized. - true - end - - def type=(new_type) - validate_input_type(new_type) - # This isn't true for LateBoundTypes, but we can assume those will - # be updated via this codepath later in schema setup. - if new_type.respond_to?(:non_null?) - validate_deprecated_or_optional(null: !new_type.non_null?, deprecation_reason: deprecation_reason) - end - @type = new_type - end - - def type - @type ||= begin - parsed_type = begin - Member::BuildType.parse_type(@type_expr, null: @null) - rescue StandardError => err - raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace - end - # Use the setter method to get validations - self.type = parsed_type - end - end - - def statically_coercible? - return @statically_coercible if defined?(@statically_coercible) - requires_parent_object = @prepare.is_a?(String) || @prepare.is_a?(Symbol) || @own_validators - @statically_coercible = !requires_parent_object - end - - # Apply the {prepare} configuration to `value`, using methods from `obj`. - # Used by the runtime. - # @api private - def prepare_value(obj, value, context: nil) - if type.unwrap.kind.input_object? - value = recursively_prepare_input_object(value, type) - end - - Schema::Validator.validate!(validators, obj, context, value) - - if @prepare.nil? - value - elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol) - if obj.nil? - # The problem here is, we _used to_ prepare while building variables. - # But now we don't have the runtime object there. - # - # This will have to be called later, when the runtime object _is_ available. - value - elsif obj.respond_to?(@prepare) - obj.public_send(@prepare, value) - elsif owner.respond_to?(@prepare) - owner.public_send(@prepare, value, context || obj.context) - else - raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}. "\ - "Could not find prepare method #{@prepare} on #{obj.class} or #{owner}." - end - elsif @prepare.respond_to?(:call) - @prepare.call(value, context || obj.context) - else - raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}" - end - end - - # @api private - def coerce_into_values(parent_object, values, context, argument_values) - arg_name = graphql_name - arg_key = keyword - default_used = false - - if values.key?(arg_name) - value = values[arg_name] - elsif values.key?(arg_key) - value = values[arg_key] - elsif default_value? - value = default_value - default_used = true - else - # no value at all - owner.validate_directive_argument(self, nil) - return - end - - if value.nil? && replace_null_with_default? - value = default_value - default_used = true - end - - loaded_value = nil - coerced_value = begin - type.coerce_input(value, context) - rescue StandardError => err - context.schema.handle_or_reraise(context, err) - end - - # If this isn't lazy, then the block returns eagerly and assigns the result here - # If it _is_ lazy, then we write the lazy to the hash, then update it later - argument_values[arg_key] = context.query.after_lazy(coerced_value) do |resolved_coerced_value| - owner.validate_directive_argument(self, resolved_coerced_value) - prepared_value = begin - prepare_value(parent_object, resolved_coerced_value, context: context) - rescue StandardError => err - context.schema.handle_or_reraise(context, err) - end - - if loads && !from_resolver? - loaded_value = begin - load_and_authorize_value(owner, prepared_value, context) - rescue StandardError => err - context.schema.handle_or_reraise(context, err) - end - end - - maybe_loaded_value = loaded_value || prepared_value - context.query.after_lazy(maybe_loaded_value) do |resolved_loaded_value| - # TODO code smell to access such a deeply-nested constant in a distant module - argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new( - value: resolved_loaded_value, - original_value: resolved_coerced_value, - definition: self, - default_used: default_used, - ) - end - end - end - - def load_and_authorize_value(load_method_owner, coerced_value, context) - if coerced_value.nil? - return nil - end - arg_load_method = "load_#{keyword}" - if load_method_owner.respond_to?(arg_load_method) - custom_loaded_value = if load_method_owner.is_a?(Class) - load_method_owner.public_send(arg_load_method, coerced_value, context) - else - load_method_owner.public_send(arg_load_method, coerced_value) - end - context.query.after_lazy(custom_loaded_value) do |custom_value| - if loads - if type.list? - loaded_values = [] - context.dataloader.run_isolated do - custom_value.each_with_index.map { |custom_val, idx| - id = coerced_value[idx] - context.dataloader.append_job do - loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val) - end - } - end - context.schema.after_any_lazies(loaded_values, &:itself) - else - load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value) - end - else - custom_value - end - end - elsif loads - if type.list? - loaded_values = [] - # We want to run these list items all together, - # but we also need to wait for the result so we can return it :S - context.dataloader.run_isolated do - coerced_value.each_with_index { |val, idx| - context.dataloader.append_job do - loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context) - end - } - end - context.schema.after_any_lazies(loaded_values, &:itself) - else - load_method_owner.load_and_authorize_application_object(self, coerced_value, context) - end - else - coerced_value - end - end - - # @api private - def validate_default_value - return unless default_value? - coerced_default_value = begin - # This is weird, but we should accept single-item default values for list-type arguments. - # If we used `coerce_isolated_input` below, it would do this for us, but it's not really - # the right thing here because we expect default values in application format (Ruby values) - # not GraphQL format (scalar values). - # - # But I don't think Schema::List#coerce_result should apply wrapping to single-item lists. - prepped_default_value = if default_value.nil? - nil - elsif (type.kind.list? || (type.kind.non_null? && type.of_type.list?)) && !default_value.respond_to?(:map) - [default_value] - else - default_value - end - - type.coerce_isolated_result(prepped_default_value) unless prepped_default_value.nil? - rescue GraphQL::Schema::Enum::UnresolvedValueError - # It raises this, which is helpful at runtime, but not here... - default_value - end - res = type.valid_isolated_input?(coerced_default_value) - if !res - raise InvalidDefaultValueError.new(self) - end - end - - class InvalidDefaultValueError < GraphQL::Error - def initialize(argument) - message = "`#{argument.path}` has an invalid default value: `#{argument.default_value.inspect}` isn't accepted by `#{argument.type.to_type_signature}`; update the default value or the argument type." - super(message) - end - end - - private - - def recursively_prepare_input_object(value, type) - if type.non_null? - type = type.of_type - end - - if type.list? && !value.nil? - inner_type = type.of_type - value.map { |v| recursively_prepare_input_object(v, inner_type) } - elsif value.is_a?(GraphQL::Schema::InputObject) - value.prepare - else - value - end - end - - def validate_input_type(input_type) - if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType) - # Do nothing; assume this will be validated later - elsif input_type.kind.non_null? || input_type.kind.list? - validate_input_type(input_type.unwrap) - elsif !input_type.kind.input? - raise ArgumentError, "Invalid input type for #{path}: #{input_type.graphql_name}. Must be scalar, enum, or input object, not #{input_type.kind.name}." - else - # It's an input type, we're OK - end - end - - def validate_deprecated_or_optional(null:, deprecation_reason:) - if deprecation_reason && !null - raise ArgumentError, "Required arguments cannot be deprecated: #{path}." - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/base_64_encoder.rb b/vendor/gems/graphql/lib/graphql/schema/base_64_encoder.rb deleted file mode 100644 index 3679465460e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/base_64_encoder.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true -require "base64" -module GraphQL - class Schema - # @api private - module Base64Encoder - def self.encode(unencoded_text, nonce: false) - Base64.urlsafe_encode64(unencoded_text, padding: false) - end - - def self.decode(encoded_text, nonce: false) - # urlsafe_decode64 is for forward compatibility - Base64.urlsafe_decode64(encoded_text) - rescue ArgumentError - raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/build_from_definition.rb b/vendor/gems/graphql/lib/graphql/schema/build_from_definition.rb deleted file mode 100644 index 1e725073fcf..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/build_from_definition.rb +++ /dev/null @@ -1,510 +0,0 @@ -# frozen_string_literal: true -require "graphql/schema/build_from_definition/resolve_map" - -module GraphQL - class Schema - module BuildFromDefinition - class << self - # @see {Schema.from_definition} - def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs) - if defined?(parser::SchemaParser) - parser = parser::SchemaParser - end - from_document(schema_superclass, parser.parse(definition_string), **kwargs) - end - - def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs) - if defined?(parser::SchemaParser) - parser = parser::SchemaParser - end - from_document(schema_superclass, parser.parse_file(definition_path), **kwargs) - end - - def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false) - Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using) - end - end - - # @api private - module Builder - include GraphQL::EmptyObjects - extend self - - def build(schema_superclass, document, default_resolve:, using: {}, relay:) - raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document) - - if default_resolve.is_a?(Hash) - default_resolve = ResolveMap.new(default_resolve) - end - - schema_defns = document.definitions.select { |d| d.is_a?(GraphQL::Language::Nodes::SchemaDefinition) } - if schema_defns.size > 1 - raise InvalidDocumentError.new('Must provide only one schema definition.') - end - schema_definition = schema_defns.first - types = {} - directives = schema_superclass.directives.dup - type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)}) - # Make a different type resolver because we need to coerce directive arguments - # _while_ building the schema. - # It will dig for a type if it encounters a custom type. This could be a problem if there are cycles. - directive_type_resolver = nil - directive_type_resolver = build_resolve_type(types, directives, ->(type_name) { - types[type_name] ||= begin - defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name } - if defn - build_definition_from_node(defn, directive_type_resolver, default_resolve) - elsif (built_in_defn = GraphQL::Schema::BUILT_IN_TYPES[type_name]) - built_in_defn - else - raise "No definition for #{type_name.inspect} found in schema document or built-in types. Add a definition for it or remove it." - end - end - }) - - directives.merge!(GraphQL::Schema.default_directives) - document.definitions.each do |definition| - if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition) - directives[definition.name] = build_directive(definition, directive_type_resolver) - end - end - - # In case any directives referenced built-in types for their arguments: - replace_late_bound_types_with_built_in(types) - - schema_extensions = nil - document.definitions.each do |definition| - case definition - when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition - nil # already handled - when GraphQL::Language::Nodes::SchemaExtension - schema_extensions ||= [] - schema_extensions << definition - else - # It's possible that this was already loaded by the directives - prev_type = types[definition.name] - if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType) - types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve) - end - end - end - - replace_late_bound_types_with_built_in(types) - - if schema_definition - if schema_definition.query - raise InvalidDocumentError.new("Specified query type \"#{schema_definition.query}\" not found in document.") unless types[schema_definition.query] - query_root_type = types[schema_definition.query] - end - - if schema_definition.mutation - raise InvalidDocumentError.new("Specified mutation type \"#{schema_definition.mutation}\" not found in document.") unless types[schema_definition.mutation] - mutation_root_type = types[schema_definition.mutation] - end - - if schema_definition.subscription - raise InvalidDocumentError.new("Specified subscription type \"#{schema_definition.subscription}\" not found in document.") unless types[schema_definition.subscription] - subscription_root_type = types[schema_definition.subscription] - end - - if schema_definition.query.nil? && - schema_definition.mutation.nil? && - schema_definition.subscription.nil? - # This schema may have been given with directives only, - # check for defaults: - query_root_type = types['Query'] - mutation_root_type = types['Mutation'] - subscription_root_type = types['Subscription'] - end - else - query_root_type = types['Query'] - mutation_root_type = types['Mutation'] - subscription_root_type = types['Subscription'] - end - - raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type - - builder = self - - found_types = types.values - object_types = found_types.select { |t| t.respond_to?(:kind) && t.kind.object? } - schema_class = Class.new(schema_superclass) do - begin - # Add these first so that there's some chance of resolving late-bound types - add_type_and_traverse(found_types, root: false) - orphan_types(object_types) - query query_root_type - mutation mutation_root_type - subscription subscription_root_type - rescue Schema::UnresolvedLateBoundTypeError => err - type_name = err.type.name - err_backtrace = err.backtrace - raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace - end - - object_types.each do |t| - t.interfaces.each do |int_t| - int_t.orphan_types(t) - end - end - - if default_resolve.respond_to?(:resolve_type) - def self.resolve_type(*args) - self.definition_default_resolve.resolve_type(*args) - end - else - def self.resolve_type(*args) - NullResolveType.call(*args) - end - end - - directives directives.values - - if schema_definition - ast_node(schema_definition) - builder.build_directives(self, schema_definition, type_resolver) - end - - using.each do |plugin, options| - if options - use(plugin, **options) - else - use(plugin) - end - end - - # Empty `orphan_types` -- this will make unreachable types ... unreachable. - own_orphan_types.clear - - class << self - attr_accessor :definition_default_resolve - end - - self.definition_default_resolve = default_resolve - - def definition_default_resolve - self.class.definition_default_resolve - end - - def self.inherited(child_class) - child_class.definition_default_resolve = self.definition_default_resolve - super - end - end - - if schema_extensions - schema_extensions.each do |ext| - build_directives(schema_class, ext, type_resolver) - end - end - - schema_class - end - - NullResolveType = ->(type, obj, ctx) { - raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.") - } - - def build_definition_from_node(definition, type_resolver, default_resolve) - case definition - when GraphQL::Language::Nodes::EnumTypeDefinition - build_enum_type(definition, type_resolver) - when GraphQL::Language::Nodes::ObjectTypeDefinition - build_object_type(definition, type_resolver) - when GraphQL::Language::Nodes::InterfaceTypeDefinition - build_interface_type(definition, type_resolver) - when GraphQL::Language::Nodes::UnionTypeDefinition - build_union_type(definition, type_resolver) - when GraphQL::Language::Nodes::ScalarTypeDefinition - build_scalar_type(definition, type_resolver, default_resolve: default_resolve) - when GraphQL::Language::Nodes::InputObjectTypeDefinition - build_input_object_type(definition, type_resolver) - end - end - - # Modify `types`, replacing any late-bound references to built-in types - # with their actual definitions. - # - # (Schema definitions are allowed to reference those built-ins without redefining them.) - # @return void - def replace_late_bound_types_with_built_in(types) - GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar| - existing_type = types[scalar_name] - if existing_type.is_a?(GraphQL::Schema::LateBoundType) - types[scalar_name] = built_in_scalar - end - end - end - - def build_directives(definition, ast_node, type_resolver) - dirs = prepare_directives(ast_node, type_resolver) - dirs.each do |(dir_class, options)| - if definition.respond_to?(:schema_directive) - # it's a schema - definition.schema_directive(dir_class, **options) - else - definition.directive(dir_class, **options) - end - end - end - - def prepare_directives(ast_node, type_resolver) - dirs = [] - ast_node.directives.each do |dir_node| - if dir_node.name == "deprecated" - # This is handled using `deprecation_reason` - next - else - dir_class = type_resolver.call(dir_node.name) - if dir_class.nil? - raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}" - end - options = args_to_kwargs(dir_class, dir_node) - dirs << [dir_class, options] - end - end - dirs - end - - def args_to_kwargs(arg_owner, node) - if node.respond_to?(:arguments) - kwargs = {} - node.arguments.each do |arg_node| - arg_defn = arg_owner.get_argument(arg_node.name) - kwargs[arg_defn.keyword] = args_to_kwargs(arg_defn.type.unwrap, arg_node.value) - end - kwargs - elsif node.is_a?(Array) - node.map { |n| args_to_kwargs(arg_owner, n) } - elsif node.is_a?(Language::Nodes::Enum) - node.name - else - # scalar - node - end - end - - def build_enum_type(enum_type_definition, type_resolver) - builder = self - Class.new(GraphQL::Schema::Enum) do - graphql_name(enum_type_definition.name) - builder.build_directives(self, enum_type_definition, type_resolver) - description(enum_type_definition.description) - ast_node(enum_type_definition) - enum_type_definition.values.each do |enum_value_definition| - value(enum_value_definition.name, - value: enum_value_definition.name, - deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives), - description: enum_value_definition.description, - directives: builder.prepare_directives(enum_value_definition, type_resolver), - ast_node: enum_value_definition, - ) - end - end - end - - def build_deprecation_reason(directives) - deprecated_directive = directives.find{ |d| d.name == 'deprecated' } - return unless deprecated_directive - - reason = deprecated_directive.arguments.find{ |a| a.name == 'reason' } - return GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON unless reason - - reason.value - end - - def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:) - builder = self - Class.new(GraphQL::Schema::Scalar) do - graphql_name(scalar_type_definition.name) - description(scalar_type_definition.description) - ast_node(scalar_type_definition) - builder.build_directives(self, scalar_type_definition, type_resolver) - - if default_resolve.respond_to?(:coerce_input) - # Put these method definitions in another method to avoid retaining `type_resolve` - # from this method's bindiing - builder.build_scalar_type_coerce_method(self, :coerce_input, default_resolve) - builder.build_scalar_type_coerce_method(self, :coerce_result, default_resolve) - end - end - end - - def build_scalar_type_coerce_method(scalar_class, method_name, default_definition_resolve) - scalar_class.define_singleton_method(method_name) do |val, ctx| - default_definition_resolve.public_send(method_name, self, val, ctx) - end - end - - def build_union_type(union_type_definition, type_resolver) - builder = self - Class.new(GraphQL::Schema::Union) do - graphql_name(union_type_definition.name) - description(union_type_definition.description) - possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) }) - ast_node(union_type_definition) - builder.build_directives(self, union_type_definition, type_resolver) - end - end - - def build_object_type(object_type_definition, type_resolver) - builder = self - - Class.new(GraphQL::Schema::Object) do - graphql_name(object_type_definition.name) - description(object_type_definition.description) - ast_node(object_type_definition) - builder.build_directives(self, object_type_definition, type_resolver) - - object_type_definition.interfaces.each do |interface_name| - interface_defn = type_resolver.call(interface_name) - implements(interface_defn) - end - - builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true) - end - end - - def build_input_object_type(input_object_type_definition, type_resolver) - builder = self - Class.new(GraphQL::Schema::InputObject) do - graphql_name(input_object_type_definition.name) - description(input_object_type_definition.description) - ast_node(input_object_type_definition) - builder.build_directives(self, input_object_type_definition, type_resolver) - builder.build_arguments(self, input_object_type_definition.fields, type_resolver) - end - end - - def build_default_value(default_value) - case default_value - when GraphQL::Language::Nodes::Enum - default_value.name - when GraphQL::Language::Nodes::NullValue - nil - when GraphQL::Language::Nodes::InputObject - default_value.to_h - when Array - default_value.map { |v| build_default_value(v) } - else - default_value - end - end - - def build_arguments(type_class, arguments, type_resolver) - builder = self - - arguments.each do |argument_defn| - default_value_kwargs = if !argument_defn.default_value.nil? - { default_value: builder.build_default_value(argument_defn.default_value) } - else - EMPTY_HASH - end - - type_class.argument( - argument_defn.name, - type: type_resolver.call(argument_defn.type), - required: false, - description: argument_defn.description, - deprecation_reason: builder.build_deprecation_reason(argument_defn.directives), - ast_node: argument_defn, - camelize: false, - directives: prepare_directives(argument_defn, type_resolver), - **default_value_kwargs - ) - end - end - - def build_directive(directive_definition, type_resolver) - builder = self - Class.new(GraphQL::Schema::Directive) do - graphql_name(directive_definition.name) - description(directive_definition.description) - repeatable(directive_definition.repeatable) - locations(*directive_definition.locations.map { |location| location.name.to_sym }) - ast_node(directive_definition) - builder.build_arguments(self, directive_definition.arguments, type_resolver) - end - end - - def build_interface_type(interface_type_definition, type_resolver) - builder = self - Module.new do - include GraphQL::Schema::Interface - graphql_name(interface_type_definition.name) - description(interface_type_definition.description) - interface_type_definition.interfaces.each do |interface_name| - interface_defn = type_resolver.call(interface_name) - implements(interface_defn) - end - ast_node(interface_type_definition) - builder.build_directives(self, interface_type_definition, type_resolver) - - builder.build_fields(self, interface_type_definition.fields, type_resolver, default_resolve: nil) - end - end - - def build_fields(owner, field_definitions, type_resolver, default_resolve:) - builder = self - - field_definitions.each do |field_definition| - resolve_method_name = -"resolve_field_#{field_definition.name}" - schema_field_defn = owner.field( - field_definition.name, - description: field_definition.description, - type: type_resolver.call(field_definition.type), - null: true, - connection_extension: nil, - deprecation_reason: build_deprecation_reason(field_definition.directives), - ast_node: field_definition, - method_conflict_warning: false, - camelize: false, - directives: prepare_directives(field_definition, type_resolver), - resolver_method: resolve_method_name, - ) - - builder.build_arguments(schema_field_defn, field_definition.arguments, type_resolver) - - # Don't do this for interfaces - if default_resolve - define_field_resolve_method(owner, resolve_method_name, field_definition.name) - end - end - end - - def define_field_resolve_method(owner, method_name, field_name) - owner.define_method(method_name) { |**args| - field_instance = self.class.get_field(field_name) - context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context) - } - end - - def build_resolve_type(lookup_hash, directives, missing_type_handler) - resolve_type_proc = nil - resolve_type_proc = ->(ast_node) { - case ast_node - when GraphQL::Language::Nodes::TypeName - type_name = ast_node.name - if lookup_hash.key?(type_name) - lookup_hash[type_name] - else - missing_type_handler.call(type_name) - end - when GraphQL::Language::Nodes::NonNullType - resolve_type_proc.call(ast_node.of_type).to_non_null_type - when GraphQL::Language::Nodes::ListType - resolve_type_proc.call(ast_node.of_type).to_list_type - when String - directives[ast_node] - else - raise "Unexpected ast_node: #{ast_node.inspect}" - end - } - resolve_type_proc - end - end - - private_constant :Builder - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map.rb b/vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map.rb deleted file mode 100644 index 4125d17a435..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true -require "graphql/schema/build_from_definition/resolve_map/default_resolve" - -module GraphQL - class Schema - module BuildFromDefinition - # Wrap a user-provided hash of resolution behavior for easy access at runtime. - # - # Coerce scalar values by: - # - Checking for a function in the map like `{ Date: { coerce_input: ->(val, ctx) { ... }, coerce_result: ->(val, ctx) { ... } } }` - # - Falling back to a passthrough - # - # Interface/union resolution can be provided as a `resolve_type:` key. - # - # @api private - class ResolveMap - module NullScalarCoerce - def self.call(val, _ctx) - val - end - end - - def initialize(user_resolve_hash) - @resolve_hash = Hash.new do |h, k| - # For each type name, provide a new hash if one wasn't given: - h[k] = Hash.new do |h2, k2| - if k2 == "coerce_input" || k2 == "coerce_result" - # This isn't an object field, it's a scalar coerce function. - # Use a passthrough - NullScalarCoerce - else - # For each field, provide a resolver that will - # make runtime checks & replace itself - h2[k2] = DefaultResolve.new(h2, k2) - end - end - end - @user_resolve_hash = user_resolve_hash - # User-provided resolve functions take priority over the default: - @user_resolve_hash.each do |type_name, fields| - type_name_s = type_name.to_s - case fields - when Hash - fields.each do |field_name, resolve_fn| - @resolve_hash[type_name_s][field_name.to_s] = resolve_fn - end - when Proc - # for example, "resolve_type" - @resolve_hash[type_name_s] = fields - else - raise ArgumentError, "Unexpected resolve hash value for #{type_name.inspect}: #{fields.inspect} (#{fields.class})" - end - end - - # Check the normalized hash, not the user input: - if @resolve_hash.key?("resolve_type") - define_singleton_method :resolve_type do |type, obj, ctx| - @resolve_hash.fetch("resolve_type").call(type, obj, ctx) - end - end - end - - def call(type, field, obj, args, ctx) - resolver = @resolve_hash[type.graphql_name][field.graphql_name] - resolver.call(obj, args, ctx) - end - - def coerce_input(type, value, ctx) - @resolve_hash[type.graphql_name]["coerce_input"].call(value, ctx) - end - - def coerce_result(type, value, ctx) - @resolve_hash[type.graphql_name]["coerce_result"].call(value, ctx) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb b/vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb deleted file mode 100644 index 8574e1469b6..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - module BuildFromDefinition - class ResolveMap - class DefaultResolve - def initialize(field_map, field_name) - @field_map = field_map - @field_name = field_name - end - - # Make some runtime checks about - # how `obj` implements the `field_name`. - # - # Create a new resolve function according to that implementation, then: - # - update `field_map` with this implementation - # - call the implementation now (to satisfy this field execution) - # - # If `obj` doesn't implement `field_name`, raise an error. - def call(obj, args, ctx) - method_name = @field_name - if !obj.respond_to?(method_name) - raise KeyError, "Can't resolve field #{method_name} on #{obj.inspect}" - else - method_arity = obj.method(method_name).arity - resolver = case method_arity - when 0, -1 - # -1 Handles method_missing, eg openstruct - ->(o, a, c) { o.public_send(method_name) } - when 1 - ->(o, a, c) { o.public_send(method_name, a) } - when 2 - ->(o, a, c) { o.public_send(method_name, a, c) } - else - raise "Unexpected resolve arity: #{method_arity}. Must be 0, 1, 2" - end - # Call the resolver directly next time - @field_map[method_name] = resolver - # Call through this time - resolver.call(obj, args, ctx) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/built_in_types.rb b/vendor/gems/graphql/lib/graphql/schema/built_in_types.rb deleted file mode 100644 index d2ecf259fec..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/built_in_types.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - BUILT_IN_TYPES = { - "Int" => GraphQL::Types::Int, - "String" => GraphQL::Types::String, - "Float" => GraphQL::Types::Float, - "Boolean" => GraphQL::Types::Boolean, - "ID" => GraphQL::Types::ID, - } - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive.rb b/vendor/gems/graphql/lib/graphql/schema/directive.rb deleted file mode 100644 index 1bed986dd73..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive.rb +++ /dev/null @@ -1,224 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # Subclasses of this can influence how {GraphQL::Execution::Interpreter} runs queries. - # - # - {.include?}: if it returns `false`, the field or fragment will be skipped altogether, as if it were absent - # - {.resolve}: Wraps field resolution (so it should call `yield` to continue) - class Directive < GraphQL::Schema::Member - extend GraphQL::Schema::Member::HasArguments - extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments - - class << self - # Directives aren't types, they don't have kinds. - undef_method :kind - - def path - "@#{super}" - end - - # Return a name based on the class name, - # but downcase the first letter. - def default_graphql_name - @default_graphql_name ||= begin - camelized_name = super.dup - camelized_name[0] = camelized_name[0].downcase - -camelized_name - end - end - - def locations(*new_locations) - if !new_locations.empty? - new_locations.each do |new_loc| - if !LOCATIONS.include?(new_loc.to_sym) - raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` " - end - end - @locations = new_locations - else - @locations ||= (superclass.respond_to?(:locations) ? superclass.locations : []) - end - end - - def default_directive(new_default_directive = nil) - if new_default_directive != nil - @default_directive = new_default_directive - elsif @default_directive.nil? - @default_directive = (superclass.respond_to?(:default_directive) ? superclass.default_directive : false) - else - !!@default_directive - end - end - - def default_directive? - default_directive - end - - # If false, this part of the query won't be evaluated - def include?(_object, arguments, context) - static_include?(arguments, context) - end - - # Determines whether {Execution::Lookahead} considers the field to be selected - def static_include?(_arguments, _context) - true - end - - # Continuing is passed as a block; `yield` to continue - def resolve(object, arguments, context) - yield - end - - # Continuing is passed as a block, yield to continue. - def resolve_each(object, arguments, context) - yield - end - - def on_field? - locations.include?(FIELD) - end - - def on_fragment? - locations.include?(FRAGMENT_SPREAD) && locations.include?(INLINE_FRAGMENT) - end - - def on_operation? - locations.include?(QUERY) && locations.include?(MUTATION) && locations.include?(SUBSCRIPTION) - end - - def repeatable? - !!@repeatable - end - - def repeatable(new_value) - @repeatable = new_value - end - - private - - def inherited(subclass) - super - subclass.class_exec do - @default_graphql_name ||= nil - end - end - end - - # @return [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class, Module] - attr_reader :owner - - # @return [GraphQL::Interpreter::Arguments] - attr_reader :arguments - - def initialize(owner, **arguments) - @owner = owner - assert_valid_owner - # It's be nice if we had the real context here, but we don't. What we _would_ get is: - # - error handling - # - lazy resolution - # Probably, those won't be needed here, since these are configuration arguments, - # not runtime arguments. - @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance) - end - - def graphql_name - self.class.graphql_name - end - - LOCATIONS = [ - QUERY = :QUERY, - MUTATION = :MUTATION, - SUBSCRIPTION = :SUBSCRIPTION, - FIELD = :FIELD, - FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION, - FRAGMENT_SPREAD = :FRAGMENT_SPREAD, - INLINE_FRAGMENT = :INLINE_FRAGMENT, - SCHEMA = :SCHEMA, - SCALAR = :SCALAR, - OBJECT = :OBJECT, - FIELD_DEFINITION = :FIELD_DEFINITION, - ARGUMENT_DEFINITION = :ARGUMENT_DEFINITION, - INTERFACE = :INTERFACE, - UNION = :UNION, - ENUM = :ENUM, - ENUM_VALUE = :ENUM_VALUE, - INPUT_OBJECT = :INPUT_OBJECT, - INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION, - VARIABLE_DEFINITION = :VARIABLE_DEFINITION, - ] - - DEFAULT_DEPRECATION_REASON = 'No longer supported' - LOCATION_DESCRIPTIONS = { - QUERY: 'Location adjacent to a query operation.', - MUTATION: 'Location adjacent to a mutation operation.', - SUBSCRIPTION: 'Location adjacent to a subscription operation.', - FIELD: 'Location adjacent to a field.', - FRAGMENT_DEFINITION: 'Location adjacent to a fragment definition.', - FRAGMENT_SPREAD: 'Location adjacent to a fragment spread.', - INLINE_FRAGMENT: 'Location adjacent to an inline fragment.', - SCHEMA: 'Location adjacent to a schema definition.', - SCALAR: 'Location adjacent to a scalar definition.', - OBJECT: 'Location adjacent to an object type definition.', - FIELD_DEFINITION: 'Location adjacent to a field definition.', - ARGUMENT_DEFINITION: 'Location adjacent to an argument definition.', - INTERFACE: 'Location adjacent to an interface definition.', - UNION: 'Location adjacent to a union definition.', - ENUM: 'Location adjacent to an enum definition.', - ENUM_VALUE: 'Location adjacent to an enum value definition.', - INPUT_OBJECT: 'Location adjacent to an input object type definition.', - INPUT_FIELD_DEFINITION: 'Location adjacent to an input object field definition.', - VARIABLE_DEFINITION: 'Location adjacent to a variable definition.', - } - - private - - def assert_valid_owner - case @owner - when Class - if @owner < GraphQL::Schema::Object - assert_has_location(OBJECT) - elsif @owner < GraphQL::Schema::Union - assert_has_location(UNION) - elsif @owner < GraphQL::Schema::Enum - assert_has_location(ENUM) - elsif @owner < GraphQL::Schema::InputObject - assert_has_location(INPUT_OBJECT) - elsif @owner < GraphQL::Schema::Scalar - assert_has_location(SCALAR) - elsif @owner < GraphQL::Schema - assert_has_location(SCHEMA) - elsif @owner < GraphQL::Schema::Resolver - assert_has_location(FIELD_DEFINITION) - else - raise "Unexpected directive owner class: #{@owner}" - end - when Module - assert_has_location(INTERFACE) - when GraphQL::Schema::Argument - if @owner.owner.is_a?(GraphQL::Schema::Field) - assert_has_location(ARGUMENT_DEFINITION) - else - assert_has_location(INPUT_FIELD_DEFINITION) - end - when GraphQL::Schema::Field - assert_has_location(FIELD_DEFINITION) - when GraphQL::Schema::EnumValue - assert_has_location(ENUM_VALUE) - else - raise "Unexpected directive owner: #{@owner.inspect}" - end - end - - def assert_has_location(location) - if !self.class.locations.include?(location) - raise ArgumentError, <<-MD -Directive `@#{self.class.graphql_name}` can't be attached to #{@owner.graphql_name} because #{location} isn't included in its locations (#{self.class.locations.join(", ")}). - -Use `locations(#{location})` to update this directive's definition, or remove it from #{@owner.graphql_name}. -MD - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/deprecated.rb b/vendor/gems/graphql/lib/graphql/schema/directive/deprecated.rb deleted file mode 100644 index 6cc8d12a167..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/deprecated.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - class Deprecated < GraphQL::Schema::Directive - description "Marks an element of a GraphQL schema as no longer supported." - locations(GraphQL::Schema::Directive::FIELD_DEFINITION, GraphQL::Schema::Directive::ENUM_VALUE, GraphQL::Schema::Directive::ARGUMENT_DEFINITION, GraphQL::Schema::Directive::INPUT_FIELD_DEFINITION) - - reason_description = "Explains why this element was deprecated, usually also including a "\ - "suggestion for how to access supported similar data. Formatted "\ - "in [Markdown](https://daringfireball.net/projects/markdown/)." - - argument :reason, String, reason_description, default_value: Directive::DEFAULT_DEPRECATION_REASON, required: false - default_directive true - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/feature.rb b/vendor/gems/graphql/lib/graphql/schema/directive/feature.rb deleted file mode 100644 index 8ab87c20651..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/feature.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - # An example directive to show how you might interact with the runtime. - # - # This directive might be used along with a server-side feature flag system like Flipper. - # - # With that system, you could use this directive to exclude parts of a query - # if the current viewer doesn't have certain flags enabled. - # (So, this flag would be for internal clients, like your iOS app, not third-party API clients.) - # - # To use it, you have to implement `.enabled?`, for example: - # - # @example Implementing the Feature directive - # # app/graphql/directives/feature.rb - # class Directives::Feature < GraphQL::Schema::Directive::Feature - # def self.enabled?(flag_name, _obj, context) - # # Translate some GraphQL data for Ruby: - # flag_key = flag_name.underscore - # current_user = context[:viewer] - # # Check the feature flag however your app does it: - # MyFeatureFlags.enabled?(current_user, flag_key) - # end - # end - # - # @example Flagging a part of the query - # viewer { - # # This field only runs if `.enabled?("recommendationEngine", obj, context)` - # # returns true. Otherwise, it's treated as if it didn't exist. - # recommendations @feature(flag: "recommendationEngine") { - # name - # rating - # } - # } - class Feature < Schema::Directive - description "Directs the executor to run this only if a certain server-side feature is enabled." - - locations( - GraphQL::Schema::Directive::FIELD, - GraphQL::Schema::Directive::FRAGMENT_SPREAD, - GraphQL::Schema::Directive::INLINE_FRAGMENT - ) - - argument :flag, String, - description: "The name of the feature to check before continuing" - - # Implement the Directive API - def self.include?(object, arguments, context) - flag_name = arguments[:flag] - self.enabled?(flag_name, object, context) - end - - # Override this method in your app's subclass of this directive. - # - # @param flag_name [String] The client-provided string of a feature to check - # @param object [GraphQL::Schema::Objct] The currently-evaluated GraphQL object instance - # @param context [GraphQL::Query::Context] - # @return [Boolean] If truthy, execution will continue - def self.enabled?(flag_name, object, context) - raise GraphQL::RequiredImplementationMissingError, "Implement `.enabled?(flag_name, object, context)` to return true or false for the feature flag (#{flag_name.inspect})" - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/flagged.rb b/vendor/gems/graphql/lib/graphql/schema/directive/flagged.rb deleted file mode 100644 index 3fe43510224..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/flagged.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - # This is _similar_ to {Directive::Feature}, except it's prescribed by the server, not the client. - # - # In this case, the server hides types and fields _entirely_, unless the current context has certain `:flags` present. - class Flagged < GraphQL::Schema::Directive - def initialize(target, **options) - if target.is_a?(Module) - # This is type class of some kind, `include` will put this module - # in between the type class itself and its super class, so `super` will work fine - target.include(VisibleByFlag) - elsif !target.is_a?(VisibleByFlag) - # This is an instance of a base class. `include` won't put this in front of the - # base class implementation, so we need to `.prepend`. - # `#visible?` could probably be moved to a module and then this could use `include` instead. - target.class.prepend(VisibleByFlag) - end - super - end - - description "Hides this part of the schema unless the named flag is present in context[:flags]" - - locations( - GraphQL::Schema::Directive::FIELD_DEFINITION, - GraphQL::Schema::Directive::OBJECT, - GraphQL::Schema::Directive::SCALAR, - GraphQL::Schema::Directive::ENUM, - GraphQL::Schema::Directive::UNION, - GraphQL::Schema::Directive::INTERFACE, - GraphQL::Schema::Directive::INPUT_OBJECT, - GraphQL::Schema::Directive::ENUM_VALUE, - GraphQL::Schema::Directive::ARGUMENT_DEFINITION, - GraphQL::Schema::Directive::INPUT_FIELD_DEFINITION, - ) - - argument :by, [String], "Flags to check for this schema member" - - module VisibleByFlag - def self.included(schema_class) - schema_class.extend(self) - end - - def visible?(context) - if dir = self.directives.find { |d| d.is_a?(Flagged) } - relevant_flags = (f = context[:flags]) && dir.arguments[:by] & f # rubocop:disable Development/ContextIsPassedCop -- definition-related - relevant_flags && !relevant_flags.empty? && super - else - super - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/include.rb b/vendor/gems/graphql/lib/graphql/schema/directive/include.rb deleted file mode 100644 index ab6d83b1246..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/include.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - class Include < GraphQL::Schema::Directive - description "Directs the executor to include this field or fragment only when the `if` argument is true." - - locations( - GraphQL::Schema::Directive::FIELD, - GraphQL::Schema::Directive::FRAGMENT_SPREAD, - GraphQL::Schema::Directive::INLINE_FRAGMENT - ) - - argument :if, Boolean, - description: "Included when true." - - default_directive true - - def self.static_include?(args, ctx) - !!args[:if] - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/one_of.rb b/vendor/gems/graphql/lib/graphql/schema/directive/one_of.rb deleted file mode 100644 index 39355037fa7..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/one_of.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - class OneOf < GraphQL::Schema::Directive - description "Requires that exactly one field must be supplied and that field must not be `null`." - locations(GraphQL::Schema::Directive::INPUT_OBJECT) - default_directive true - - def initialize(...) - super - - owner.extend(IsOneOf) - end - - module IsOneOf - def one_of? - true - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/skip.rb b/vendor/gems/graphql/lib/graphql/schema/directive/skip.rb deleted file mode 100644 index 312dac992bf..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/skip.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - class Skip < Schema::Directive - description "Directs the executor to skip this field or fragment when the `if` argument is true." - - locations( - GraphQL::Schema::Directive::FIELD, - GraphQL::Schema::Directive::FRAGMENT_SPREAD, - GraphQL::Schema::Directive::INLINE_FRAGMENT - ) - - argument :if, Boolean, - description: "Skipped when true." - - default_directive true - - def self.static_include?(args, ctx) - !args[:if] - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/specified_by.rb b/vendor/gems/graphql/lib/graphql/schema/directive/specified_by.rb deleted file mode 100644 index 73fe77ee9ed..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/specified_by.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - class SpecifiedBy < GraphQL::Schema::Directive - description "Exposes a URL that specifies the behavior of this scalar." - locations(GraphQL::Schema::Directive::SCALAR) - default_directive true - - argument :url, String, description: "The URL that specifies the behavior of this scalar." - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/directive/transform.rb b/vendor/gems/graphql/lib/graphql/schema/directive/transform.rb deleted file mode 100644 index 007d70b3a34..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/directive/transform.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Directive < GraphQL::Schema::Member - # An example directive to show how you might interact with the runtime. - # - # This directive takes the return value of the tagged part of the query, - # and if the named transform is whitelisted and applies to the return value, - # it's applied by calling a method with that name. - # - # @example Installing the directive - # class MySchema < GraphQL::Schema - # directive(GraphQL::Schema::Directive::Transform) - # end - # - # @example Transforming strings - # viewer { - # username @transform(by: "upcase") - # } - class Transform < Schema::Directive - description "Directs the executor to run named transform on the return value." - - locations( - GraphQL::Schema::Directive::FIELD, - ) - - argument :by, String, - description: "The name of the transform to run if applicable" - - TRANSFORMS = [ - "upcase", - "downcase", - # ?? - ] - # Implement the Directive API - def self.resolve(object, arguments, context) - path = context.namespace(:interpreter)[:current_path] - return_value = yield - transform_name = arguments[:by] - if TRANSFORMS.include?(transform_name) && return_value.respond_to?(transform_name) - return_value = return_value.public_send(transform_name) - response = context.namespace(:interpreter_runtime)[:runtime].final_result - *keys, last = path - keys.each do |key| - if response && (response = response[key]) - next - else - break - end - end - if response - response[last] = return_value - end - nil - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/enum.rb b/vendor/gems/graphql/lib/graphql/schema/enum.rb deleted file mode 100644 index 4be001fe92c..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/enum.rb +++ /dev/null @@ -1,266 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # Extend this class to define GraphQL enums in your schema. - # - # By default, GraphQL enum values are translated into Ruby strings. - # You can provide a custom value with the `value:` keyword. - # - # @example - # # equivalent to - # # enum PizzaTopping { - # # MUSHROOMS - # # ONIONS - # # PEPPERS - # # } - # class PizzaTopping < GraphQL::Schema::Enum - # value :MUSHROOMS - # value :ONIONS - # value :PEPPERS - # end - class Enum < GraphQL::Schema::Member - extend GraphQL::Schema::Member::ValidatesInput - - # This is raised when either: - # - # - A resolver returns a value which doesn't match any of the enum's configured values; - # - Or, the resolver returns a value which matches a value, but that value's `authorized?` check returns false. - # - # In either case, the field should be modified so that the invalid value isn't returned. - # - # {GraphQL::Schema::Enum} subclasses get their own subclass of this error, so that bug trackers can better show where they came from. - class UnresolvedValueError < GraphQL::Error - def initialize(value:, enum:, context:, authorized:) - fix_message = if authorized == false - ", but this value was unauthorized. Update the field or resolver to return a different value in this case (or return `nil`)." - else - ", but this isn't a valid value for `#{enum.graphql_name}`. Update the field or resolver to return one of `#{enum.graphql_name}`'s values instead." - end - message = if (cp = context[:current_path]) && (cf = context[:current_field]) - "`#{cf.path}` returned `#{value.inspect}` at `#{cp.join(".")}`#{fix_message}" - else - "`#{value.inspect}` was returned for `#{enum.graphql_name}`#{fix_message}" - end - super(message) - end - end - - # Raised when a {GraphQL::Schema::Enum} is defined to have no values. - # This can also happen when all values return false for `.visible?`. - class MissingValuesError < GraphQL::Error - def initialize(enum_type) - @enum_type = enum_type - super("Enum types require at least one value, but #{enum_type.graphql_name} didn't provide any for this query. Make sure at least one value is defined and visible for this query.") - end - end - - class << self - # Define a value for this enum - # @option kwargs [String, Symbol] :graphql_name the GraphQL value for this, usually `SCREAMING_CASE` - # @option kwargs [String] :description, the GraphQL description for this value, present in documentation - # @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation - # @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`) - # @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`) - # @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here - # @param value_method [Symbol, false] A method to generate for this value, or `false` to skip generation - # @return [void] - # @see {Schema::EnumValue} which handles these inputs by default - def value(*args, value_method: nil, **kwargs, &block) - kwargs[:owner] = self - value = enum_value_class.new(*args, **kwargs, &block) - - if value_method || (value_methods && value_method != false) - generate_value_method(value, value_method) - end - - key = value.graphql_name - prev_value = own_values[key] - case prev_value - when nil - own_values[key] = value - when GraphQL::Schema::EnumValue - own_values[key] = [prev_value, value] - when Array - prev_value << value - else - raise "Invariant: Unexpected enum value for #{key.inspect}: #{prev_value.inspect}" - end - value - end - - # @return [Array] Possible values of this enum - def enum_values(context = GraphQL::Query::NullContext.instance) - inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil - visible_values = [] - types = Warden.types_from_context(context) - own_values.each do |key, values_entry| - visible_value = nil - if values_entry.is_a?(Array) - values_entry.each do |v| - if types.visible_enum_value?(v, context) - if visible_value.nil? - visible_value = v - visible_values << v - else - raise DuplicateNamesError.new( - duplicated_name: v.path, duplicated_definition_1: visible_value.inspect, duplicated_definition_2: v.inspect - ) - end - end - end - elsif types.visible_enum_value?(values_entry, context) - visible_values << values_entry - end - end - - if inherited_values - # Local values take precedence over inherited ones - inherited_values.each do |i_val| - if !visible_values.any? { |v| v.graphql_name == i_val.graphql_name } - visible_values << i_val - end - end - end - - visible_values - end - - # @return [Array] An unfiltered list of all definitions - def all_enum_value_definitions - all_defns = if superclass.respond_to?(:all_enum_value_definitions) - superclass.all_enum_value_definitions - else - [] - end - - @own_values && @own_values.each do |_key, value| - if value.is_a?(Array) - all_defns.concat(value) - else - all_defns << value - end - end - - all_defns - end - - # @return [Hash GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name. - def values(context = GraphQL::Query::NullContext.instance) - enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val } - end - - # @return [Class] for handling `value(...)` inputs and building `GraphQL::Enum::EnumValue`s out of them - def enum_value_class(new_enum_value_class = nil) - if new_enum_value_class - @enum_value_class = new_enum_value_class - elsif defined?(@enum_value_class) && @enum_value_class - @enum_value_class - else - superclass <= GraphQL::Schema::Enum ? superclass.enum_value_class : nil - end - end - - def value_methods(new_value = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_value) - if @value_methods != nil - @value_methods - else - find_inherited_value(:value_methods, false) - end - else - @value_methods = new_value - end - end - - def kind - GraphQL::TypeKinds::ENUM - end - - def validate_non_null_input(value_name, ctx, max_errors: nil) - allowed_values = ctx.types.enum_values(self) - matching_value = allowed_values.find { |v| v.graphql_name == value_name } - - if matching_value.nil? - GraphQL::Query::InputValidationResult.from_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}") - else - nil - end - # rescue MissingValuesError - # nil - end - - # Called by the runtime when a field returns a value to give back to the client. - # This method checks that the incoming {value} matches one of the enum's defined values. - # @param value [Object] Any value matching the values for this enum. - # @param ctx [GraphQL::Query::Context] - # @raise [GraphQL::Schema::Enum::UnresolvedValueError] if {value} doesn't match a configured value or if the matching value isn't authorized. - # @return [String] The GraphQL-ready string for {value} - def coerce_result(value, ctx) - types = ctx.types - all_values = types ? types.enum_values(self) : values.each_value - enum_value = all_values.find { |val| val.value == value } - if enum_value && (was_authed = enum_value.authorized?(ctx)) - enum_value.graphql_name - else - raise self::UnresolvedValueError.new(enum: self, value: value, context: ctx, authorized: was_authed) - end - end - - # Called by the runtime with incoming string representations from a query. - # It will match the string to a configured by name or by Ruby value. - # @param value_name [String, Object] A string from a GraphQL query, or a Ruby value matching a `value(..., value: ...)` configuration - # @param ctx [GraphQL::Query::Context] - # @raise [GraphQL::UnauthorizedEnumValueError] if an {EnumValue} matches but returns false for `.authorized?`. Goes to {Schema.unauthorized_object}. - # @return [Object] The Ruby value for the matched {GraphQL::Schema::EnumValue} - def coerce_input(value_name, ctx) - all_values = ctx.types ? ctx.types.enum_values(self) : values.each_value - - # This tries matching by incoming GraphQL string, then checks Ruby-defined values - if v = (all_values.find { |val| val.graphql_name == value_name } || all_values.find { |val| val.value == value_name }) - if v.authorized?(ctx) - v.value - else - raise GraphQL::UnauthorizedEnumValueError.new(type: self, enum_value: v, context: ctx) - end - else - nil - end - end - - def inherited(child_class) - if child_class.name - # Don't assign a custom error class to anonymous classes - # because they would end up with names like `#::UnresolvedValueError` which messes up bug trackers - child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError)) - end - child_class.class_exec { @value_methods = nil } - super - end - - private - - def own_values - @own_values ||= {} - end - - def generate_value_method(value, configured_value_method) - return if configured_value_method == false - - value_method_name = configured_value_method || value.graphql_name.downcase - - if respond_to?(value_method_name.to_sym) - warn "Failed to define value method for :#{value_method_name}, because " \ - "#{value.owner.name || value.owner.graphql_name} already responds to that method. Use `value_method:` to override the method name " \ - "or `value_method: false` to disable Enum value method generation." - return - end - - define_singleton_method(value_method_name) { value.graphql_name } - end - end - - enum_value_class(GraphQL::Schema::EnumValue) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/enum_value.rb b/vendor/gems/graphql/lib/graphql/schema/enum_value.rb deleted file mode 100644 index 74484f60cb9..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/enum_value.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # A possible value for an {Enum}. - # - # You can extend this class to customize enum values in your schema. - # - # @example custom enum value class - # # define a custom class: - # class CustomEnumValue < GraphQL::Schema::EnumValue - # def initialize(*args) - # # arguments to `value(...)` in Enum classes are passed here - # super - # end - # end - # - # class BaseEnum < GraphQL::Schema::Enum - # # use it for these enums: - # enum_value_class CustomEnumValue - # end - class EnumValue < GraphQL::Schema::Member - include GraphQL::Schema::Member::HasPath - include GraphQL::Schema::Member::HasAstNode - include GraphQL::Schema::Member::HasDirectives - include GraphQL::Schema::Member::HasDeprecationReason - - attr_reader :graphql_name - - # @return [Class] The enum type that owns this value - attr_reader :owner - - def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, comment: nil, value: NOT_CONFIGURED, deprecation_reason: nil, &block) - @graphql_name = graphql_name.to_s - GraphQL::NameValidator.validate!(@graphql_name) - @description = desc || description - @comment = comment - @value = value == NOT_CONFIGURED ? @graphql_name : value - if deprecation_reason - self.deprecation_reason = deprecation_reason - end - @owner = owner - @ast_node = ast_node - if directives - directives.each do |dir_class, dir_options| - directive(dir_class, **dir_options) - end - end - - if block_given? - instance_exec(self, &block) - end - end - - def description(new_desc = nil) - if new_desc - @description = new_desc - end - @description - end - - def comment(new_comment = nil) - if new_comment - @comment = new_comment - end - @comment - end - - def value(new_val = nil) - unless new_val.nil? - @value = new_val - end - @value - end - - def inspect - "#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}#{deprecation_reason ? " @deprecation_reason=#{deprecation_reason.inspect}" : ""}>" - end - - def visible?(_ctx); true; end - def authorized?(_ctx); true; end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/field.rb b/vendor/gems/graphql/lib/graphql/schema/field.rb deleted file mode 100644 index ed7a1aba335..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/field.rb +++ /dev/null @@ -1,957 +0,0 @@ -# frozen_string_literal: true -require "graphql/schema/field/connection_extension" -require "graphql/schema/field/scope_extension" - -module GraphQL - class Schema - class Field - include GraphQL::Schema::Member::HasArguments - include GraphQL::Schema::Member::HasArguments::FieldConfigured - include GraphQL::Schema::Member::HasAstNode - include GraphQL::Schema::Member::HasPath - include GraphQL::Schema::Member::HasValidators - extend GraphQL::Schema::FindInheritedValue - include GraphQL::EmptyObjects - include GraphQL::Schema::Member::HasDirectives - include GraphQL::Schema::Member::HasDeprecationReason - - class FieldImplementationFailed < GraphQL::Error; end - - # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided - attr_reader :name - alias :graphql_name :name - - attr_writer :description - - # @return [Symbol] Method or hash key on the underlying object to look up - attr_reader :method_sym - - # @return [String] Method or hash key on the underlying object to look up - attr_reader :method_str - - attr_reader :hash_key - attr_reader :dig_keys - - # @return [Symbol] The method on the type to look up - def resolver_method - if @resolver_class - @resolver_class.resolver_method - else - @resolver_method - end - end - - def directives - if @resolver_class && !(r_dirs = @resolver_class.directives).empty? - if !(own_dirs = super).empty? - own_dirs + r_dirs - else - r_dirs - end - else - super - end - end - - # @return [Class] The thing this field was defined on (type, mutation, resolver) - attr_accessor :owner - - # @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type) - def owner_type - @owner_type ||= if owner.nil? - raise GraphQL::InvariantError, "Field #{original_name.inspect} (graphql name: #{graphql_name.inspect}) has no owner, but all fields should have an owner. How did this happen?!" - elsif owner < GraphQL::Schema::Mutation - owner.payload_type - else - owner - end - end - - # @return [Symbol] the original name of the field, passed in by the user - attr_reader :original_name - - # @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one - def resolver - @resolver_class - end - - # @return [Boolean] Is this field a predefined introspection field? - def introspection? - @introspection - end - - def inspect - "#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>" - end - - alias :mutation :resolver - - # @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value) - attr_reader :trace - - # @return [String, nil] - def subscription_scope - @subscription_scope || (@resolver_class.respond_to?(:subscription_scope) ? @resolver_class.subscription_scope : nil) - end - attr_writer :subscription_scope - - # Create a field instance from a list of arguments, keyword arguments, and a block. - # - # This method implements prioritization between the `resolver` or `mutation` defaults - # and the local overrides via other keywords. - # - # It also normalizes positional arguments into keywords for {Schema::Field#initialize}. - # @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration - # @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration - # @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration - # @return [GraphQL::Schema:Field] an instance of `self` - # @see {.initialize} for other options - def self.from_options(name = nil, type = nil, desc = nil, comment: nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block) - if (resolver_class = resolver || mutation || subscription) - # Add a reference to that parent class - kwargs[:resolver_class] = resolver_class - end - - if name - kwargs[:name] = name - end - - if comment - kwargs[:comment] = comment - end - - if !type.nil? - if desc - if kwargs[:description] - raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})" - end - - kwargs[:description] = desc - kwargs[:type] = type - elsif (resolver || mutation) && type.is_a?(String) - # The return type should be copied from the resolver, and the second positional argument is the description - kwargs[:description] = type - else - kwargs[:type] = type - end - if type.is_a?(Class) && type < GraphQL::Schema::Mutation - raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead" - end - end - new(**kwargs, &block) - end - - # Can be set with `connection: true|false` or inferred from a type name ending in `*Connection` - # @return [Boolean] if true, this field will be wrapped with Relay connection behavior - def connection? - if @connection.nil? - # Provide default based on type name - return_type_name = if @return_type_expr - Member::BuildType.to_type_name(@return_type_expr) - elsif @resolver_class && @resolver_class.type - Member::BuildType.to_type_name(@resolver_class.type) - elsif type - # As a last ditch, try to force loading the return type: - type.unwrap.name - end - if return_type_name - @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection" - else - # TODO set this when type is set by method - false # not loaded yet? - end - else - @connection - end - end - - # @return [Boolean] if true, the return type's `.scope_items` method will be applied to this field's return value - def scoped? - if !@scope.nil? - # The default was overridden - @scope - elsif @return_type_expr - # Detect a list return type, but don't call `type` since that may eager-load an otherwise lazy-loaded type - @return_type_expr.is_a?(Array) || - (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || - connection? - elsif @resolver_class - resolver_type = @resolver_class.type_expr - resolver_type.is_a?(Array) || - (resolver_type.is_a?(String) && resolver_type.include?("[")) || - connection? - else - false - end - end - - # This extension is applied to fields when {#connection?} is true. - # - # You can override it in your base field definition. - # @return [Class] A {FieldExtension} subclass for implementing pagination behavior. - # @example Configuring a custom extension - # class Types::BaseField < GraphQL::Schema::Field - # connection_extension(MyCustomExtension) - # end - def self.connection_extension(new_extension_class = nil) - if new_extension_class - @connection_extension = new_extension_class - else - @connection_extension ||= find_inherited_value(:connection_extension, ConnectionExtension) - end - end - - # @return Boolean - attr_reader :relay_node_field - # @return Boolean - attr_reader :relay_nodes_field - - # @return [Boolean] Should we warn if this field's name conflicts with a built-in method? - def method_conflict_warning? - @method_conflict_warning - end - - # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API) - # @param type [Class, GraphQL::BaseType, Array] The return type of this field - # @param owner [Class] The type that this field belongs to - # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null` - # @param description [String] Field description - # @param comment [String] Field comment - # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message - # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`) - # @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`) - # @param dig [Array] The nested hash keys to lookup on the underlying hash to resolve this field using dig - # @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`) - # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name - # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added. - # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results. - # @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results. - # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__` - # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver. - # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also) - # @param camelize [Boolean] If true, the field name will be camelized when building the schema - # @param complexity [Numeric] When provided, set the complexity for this field - # @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value - # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads - # @param extensions [Array Object>>] Named extensions to apply to this field (see also {#extension}) - # @param directives [Hash{Class => Hash}] Directives to apply to this field - # @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field - # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts - # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field - # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method - # @param validates [Array] Configurations for validating this field - # @param fallback_value [Object] A fallback value if the method is not defined - def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block) - if name.nil? - raise ArgumentError, "missing first `name` argument or keyword `name:`" - end - if !(resolver_class) - if type.nil? && !block_given? - raise ArgumentError, "missing second `type` argument, keyword `type:`, or a block containing `type(...)`" - end - end - @original_name = name - name_s = -name.to_s - - @underscored_name = -Member::BuildType.underscore(name_s) - @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s) - NameValidator.validate!(@name) - @description = description - @comment = comment - @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary - - self.deprecation_reason = deprecation_reason - - if method && hash_key && dig - raise ArgumentError, "Provide `method:`, `hash_key:` _or_ `dig:`, not multiple. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}, dig: #{dig.inspect}`)" - end - - if resolver_method - if method - raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)" - end - - if hash_key || dig - raise ArgumentError, "Provide `hash_key:`, `dig:`, _or_ `resolver_method:`, not multiple. (called with: `hash_key: #{hash_key.inspect}, dig: #{dig.inspect}, resolver_method: #{resolver_method.inspect}`)" - end - end - - method_name = method || hash_key || name_s - @dig_keys = dig - if hash_key - @hash_key = hash_key - @hash_key_str = hash_key.to_s - else - @hash_key = NOT_CONFIGURED - @hash_key_str = NOT_CONFIGURED - end - - @method_str = -method_name.to_s - @method_sym = method_name.to_sym - @resolver_method = (resolver_method || name_s).to_sym - @complexity = complexity - @dynamic_introspection = dynamic_introspection - @return_type_expr = type - @return_type_null = if !null.nil? - null - elsif resolver_class - nil - else - true - end - @connection = connection - @max_page_size = max_page_size - @default_page_size = default_page_size - @introspection = introspection - @extras = extras - @broadcastable = broadcastable - @resolver_class = resolver_class - @scope = scope - @trace = trace - @relay_node_field = relay_node_field - @relay_nodes_field = relay_nodes_field - @ast_node = ast_node - @method_conflict_warning = method_conflict_warning - @fallback_value = fallback_value - @definition_block = definition_block - - arguments.each do |name, arg| - case arg - when Hash - argument(name: name, **arg) - when GraphQL::Schema::Argument - add_argument(arg) - when Array - arg.each { |a| add_argument(a) } - else - raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}" - end - end - - @owner = owner - @subscription_scope = subscription_scope - - @extensions = EMPTY_ARRAY - @call_after_define = false - set_pagination_extensions(connection_extension: connection_extension) - # Do this last so we have as much context as possible when initializing them: - if !extensions.empty? - self.extensions(extensions) - end - - if resolver_class && !resolver_class.extensions.empty? - self.extensions(resolver_class.extensions) - end - - if !directives.empty? - directives.each do |(dir_class, options)| - self.directive(dir_class, **options) - end - end - - if !validates.empty? - self.validates(validates) - end - - if @definition_block.nil? - self.extensions.each(&:after_define_apply) - @call_after_define = true - end - end - - # Calls the definition block, if one was given. - # This is deferred so that references to the return type - # can be lazily evaluated, reducing Rails boot time. - # @return [self] - # @api private - def ensure_loaded - if @definition_block - if @definition_block.arity == 1 - @definition_block.call(self) - else - instance_exec(self, &@definition_block) - end - self.extensions.each(&:after_define_apply) - @call_after_define = true - @definition_block = nil - end - self - end - - attr_accessor :dynamic_introspection - - # If true, subscription updates with this field can be shared between viewers - # @return [Boolean, nil] - # @see GraphQL::Subscriptions::BroadcastAnalyzer - def broadcastable? - if !NOT_CONFIGURED.equal?(@broadcastable) - @broadcastable - elsif @resolver_class - @resolver_class.broadcastable? - else - nil - end - end - - # @param text [String] - # @return [String] - def description(text = nil) - if text - @description = text - elsif !NOT_CONFIGURED.equal?(@description) - @description - elsif @resolver_class - @resolver_class.description - else - nil - end - end - - # @param text [String] - # @return [String, nil] - def comment(text = nil) - if text - @comment = text - elsif !NOT_CONFIGURED.equal?(@comment) - @comment - elsif @resolver_class - @resolver_class.comment - else - nil - end - end - - # Read extension instances from this field, - # or add new classes/options to be initialized on this field. - # Extensions are executed in the order they are added. - # - # @example adding an extension - # extensions([MyExtensionClass]) - # - # @example adding multiple extensions - # extensions([MyExtensionClass, AnotherExtensionClass]) - # - # @example adding an extension with options - # extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }]) - # - # @param extensions [Array Hash>>] Add extensions to this field. For hash elements, only the first key/value is used. - # @return [Array] extensions to apply to this field - def extensions(new_extensions = nil) - if new_extensions - new_extensions.each do |extension_config| - if extension_config.is_a?(Hash) - extension_class, options = *extension_config.to_a[0] - self.extension(extension_class, **options) - else - self.extension(extension_config) - end - end - end - @extensions - end - - # Add `extension` to this field, initialized with `options` if provided. - # - # @example adding an extension - # extension(MyExtensionClass) - # - # @example adding an extension with options - # extension(MyExtensionClass, filter: true) - # - # @param extension_class [Class] subclass of {Schema::FieldExtension} - # @param options [Hash] if provided, given as `options:` when initializing `extension`. - # @return [void] - def extension(extension_class, **options) - extension_inst = extension_class.new(field: self, options: options) - if @extensions.frozen? - @extensions = @extensions.dup - end - if @call_after_define - extension_inst.after_define_apply - end - @extensions << extension_inst - nil - end - - # Read extras (as symbols) from this field, - # or add new extras to be opted into by this field's resolver. - # - # @param new_extras [Array] Add extras to this field - # @return [Array] - def extras(new_extras = nil) - if new_extras.nil? - # Read the value - field_extras = @extras - if @resolver_class && !@resolver_class.extras.empty? - field_extras + @resolver_class.extras - else - field_extras - end - else - if @extras.frozen? - @extras = @extras.dup - end - # Append to the set of extras on this field - @extras.concat(new_extras) - end - end - - def calculate_complexity(query:, nodes:, child_complexity:) - if respond_to?(:complexity_for) - lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner) - complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead) - elsif connection? - arguments = query.arguments_for(nodes.first, self) - max_possible_page_size = nil - if arguments.respond_to?(:[]) # It might have been an error - if arguments[:first] - max_possible_page_size = arguments[:first] - end - - if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size) - max_possible_page_size = arguments[:last] - end - elsif arguments.is_a?(GraphQL::ExecutionError) || arguments.is_a?(GraphQL::UnauthorizedError) - raise arguments - end - - if max_possible_page_size.nil? - max_possible_page_size = default_page_size || query.schema.default_page_size || max_page_size || query.schema.default_max_page_size - end - - if max_possible_page_size.nil? - raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `default_page_size`, `max_page_size` or `default_max_page_size`" - else - metadata_complexity = 0 - lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner) - - lookahead.selections.each do |next_lookahead| - # this includes `pageInfo`, `nodes` and `edges` and any custom fields - # TODO this doesn't support procs yet -- unlikely to need it. - metadata_complexity += next_lookahead.field.complexity - if next_lookahead.name != :nodes && next_lookahead.name != :edges - # subfields, eg, for pageInfo -- assumes no subselections - metadata_complexity += next_lookahead.selections.size - end - end - - # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be? - items_complexity = child_complexity - metadata_complexity - subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity - # Apply this field's own complexity - apply_own_complexity_to(subfields_complexity, query, nodes) - end - else - apply_own_complexity_to(child_complexity, query, nodes) - end - end - - def complexity(new_complexity = nil) - case new_complexity - when Proc - if new_complexity.parameters.size != 3 - fail( - "A complexity proc should always accept 3 parameters: ctx, args, child_complexity. "\ - "E.g.: complexity ->(ctx, args, child_complexity) { child_complexity * args[:limit] }" - ) - else - @complexity = new_complexity - end - when Numeric - @complexity = new_complexity - when nil - if @resolver_class - @complexity || @resolver_class.complexity || 1 - else - @complexity || 1 - end - else - raise("Invalid complexity: #{new_complexity.inspect} on #{@name}") - end - end - - # @return [Boolean] True if this field's {#max_page_size} should override the schema default. - def has_max_page_size? - !NOT_CONFIGURED.equal?(@max_page_size) || (@resolver_class && @resolver_class.has_max_page_size?) - end - - # @return [Integer, nil] Applied to connections if {#has_max_page_size?} - def max_page_size - if !NOT_CONFIGURED.equal?(@max_page_size) - @max_page_size - elsif @resolver_class && @resolver_class.has_max_page_size? - @resolver_class.max_page_size - else - nil - end - end - - # @return [Boolean] True if this field's {#default_page_size} should override the schema default. - def has_default_page_size? - !NOT_CONFIGURED.equal?(@default_page_size) || (@resolver_class && @resolver_class.has_default_page_size?) - end - - # @return [Integer, nil] Applied to connections if {#has_default_page_size?} - def default_page_size - if !NOT_CONFIGURED.equal?(@default_page_size) - @default_page_size - elsif @resolver_class && @resolver_class.has_default_page_size? - @resolver_class.default_page_size - else - nil - end - end - - class MissingReturnTypeError < GraphQL::Error; end - attr_writer :type - - # Get or set the return type of this field. - # - # It may return nil if no type was configured or if the given definition block wasn't called yet. - # @param new_type [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List] A GraphQL return type - # @return [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List, nil] the configured type for this field - def type(new_type = NOT_CONFIGURED) - if NOT_CONFIGURED.equal?(new_type) - if @resolver_class - return_type = @return_type_expr || @resolver_class.type_expr - if return_type.nil? - raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)" - end - nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null - Member::BuildType.parse_type(return_type, null: nullable) - elsif !@return_type_expr.nil? - @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null) - end - else - @return_type_expr = new_type - # If `type` is set in the definition block, then the `connection_extension: ...` given as a keyword won't be used, hmm... - # Also, arguments added by `connection_extension` will clobber anything previously defined, - # so `type(...)` should go first. - set_pagination_extensions(connection_extension: self.class.connection_extension) - end - rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err - # Let this propagate up - raise err - rescue StandardError => err - raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace - end - - def visible?(context) - if @resolver_class - @resolver_class.visible?(context) - else - true - end - end - - def authorized?(object, args, context) - if @resolver_class - # The resolver _instance_ will check itself during `resolve()` - @resolver_class.authorized?(object, context) - else - if args.size > 0 - if (arg_values = context[:current_arguments]) - # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not. - using_arg_values = true - arg_values = arg_values.argument_values - else - arg_values = args - using_arg_values = false - end - - args = context.types.arguments(self) - args.each do |arg| - arg_key = arg.keyword - if arg_values.key?(arg_key) - arg_value = arg_values[arg_key] - if using_arg_values - if arg_value.default_used? - # pass -- no auth required for default used - next - else - application_arg_value = arg_value.value - if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments) - application_arg_value.keyword_arguments - end - end - else - application_arg_value = arg_value - end - - if !arg.authorized?(object, application_arg_value, context) - return false - end - end - end - end - true - end - end - - # This method is called by the interpreter for each field. - # You can extend it in your base field classes. - # @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object - # @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args) - # @param ctx [GraphQL::Query::Context] - def resolve(object, args, query_ctx) - # Unwrap the GraphQL object to get the application object. - application_object = object.object - method_receiver = nil - method_to_call = nil - method_args = nil - - @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args) - - query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized| - if is_authorized - with_extensions(object, args, query_ctx) do |obj, ruby_kwargs| - method_args = ruby_kwargs - if @resolver_class - if obj.is_a?(GraphQL::Schema::Object) - obj = obj.object - end - obj = @resolver_class.new(object: obj, context: query_ctx, field: self) - end - - inner_object = obj.object - - if !NOT_CONFIGURED.equal?(@hash_key) - hash_value = if inner_object.is_a?(Hash) - inner_object.key?(@hash_key) ? inner_object[@hash_key] : inner_object[@hash_key_str] - elsif inner_object.respond_to?(:[]) - inner_object[@hash_key] - else - nil - end - if hash_value == false - hash_value - else - hash_value || (@fallback_value != NOT_CONFIGURED ? @fallback_value : nil) - end - elsif obj.respond_to?(resolver_method) - method_to_call = resolver_method - method_receiver = obj - # Call the method with kwargs, if there are any - if !ruby_kwargs.empty? - obj.public_send(resolver_method, **ruby_kwargs) - else - obj.public_send(resolver_method) - end - elsif inner_object.is_a?(Hash) - if @dig_keys - inner_object.dig(*@dig_keys) - elsif inner_object.key?(@method_sym) - inner_object[@method_sym] - elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil? - inner_object[@method_str] - elsif @fallback_value != NOT_CONFIGURED - @fallback_value - else - nil - end - elsif inner_object.respond_to?(@method_sym) - method_to_call = @method_sym - method_receiver = obj.object - if !ruby_kwargs.empty? - inner_object.public_send(@method_sym, **ruby_kwargs) - else - inner_object.public_send(@method_sym) - end - elsif @fallback_value != NOT_CONFIGURED - @fallback_value - else - raise <<-ERR - Failed to implement #{@owner.graphql_name}.#{@name}, tried: - - - `#{obj.class}##{resolver_method}`, which did not exist - - `#{inner_object.class}##{@method_sym}`, which did not exist - - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{inner_object}`, but it wasn't a Hash - - To implement this field, define one of the methods above (and check for typos), or supply a `fallback_value`. - ERR - end - end - else - raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self) - end - end - rescue GraphQL::UnauthorizedFieldError => err - err.field ||= self - begin - query_ctx.schema.unauthorized_field(err) - rescue GraphQL::ExecutionError => err - err - end - rescue GraphQL::UnauthorizedError => err - begin - query_ctx.schema.unauthorized_object(err) - rescue GraphQL::ExecutionError => err - err - end - rescue ArgumentError - if method_receiver && method_to_call - assert_satisfactory_implementation(method_receiver, method_to_call, method_args) - end - # if the line above doesn't raise, re-raise - raise - rescue GraphQL::ExecutionError => err - err - end - - # @param ctx [GraphQL::Query::Context] - def fetch_extra(extra_name, ctx) - if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name) - self.public_send(extra_name) - elsif ctx.respond_to?(extra_name) - ctx.public_send(extra_name) - else - raise GraphQL::RequiredImplementationMissingError, "Unknown field extra for #{self.path}: #{extra_name.inspect}" - end - end - - private - - def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs) - method_defn = receiver.method(method_name) - unsatisfied_ruby_kwargs = ruby_kwargs.dup - unsatisfied_method_params = [] - encountered_keyrest = false - method_defn.parameters.each do |(param_type, param_name)| - case param_type - when :key - unsatisfied_ruby_kwargs.delete(param_name) - when :keyreq - if unsatisfied_ruby_kwargs.key?(param_name) - unsatisfied_ruby_kwargs.delete(param_name) - else - unsatisfied_method_params << "- `#{param_name}:` is required by Ruby, but not by GraphQL. Consider `#{param_name}: nil` instead, or making this argument required in GraphQL." - end - when :keyrest - encountered_keyrest = true - when :req - unsatisfied_method_params << "- `#{param_name}` is required by Ruby, but GraphQL doesn't pass positional arguments. If it's meant to be a GraphQL argument, use `#{param_name}:` instead. Otherwise, remove it." - when :opt, :rest - # This is fine, although it will never be present - end - end - - if encountered_keyrest - unsatisfied_ruby_kwargs.clear - end - - if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty? - raise FieldImplementationFailed.new, <<-ERR -Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments: - -#{ unsatisfied_ruby_kwargs - .map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." } - .concat(unsatisfied_method_params) - .join("\n") } -ERR - end - end - - class ExtendedState - def initialize(args, object) - @arguments = args - @object = object - @memos = nil - @added_extras = nil - end - - attr_accessor :arguments, :object, :memos, :added_extras - end - - # Wrap execution with hooks. - # Written iteratively to avoid big stack traces. - # @return [Object] Whatever the - def with_extensions(obj, args, ctx) - if @extensions.empty? - yield(obj, args) - else - # This is a hack to get the _last_ value for extended obj and args, - # in case one of the extensions doesn't `yield`. - # (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays) - extended = ExtendedState.new(args, obj) - value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args| - if (added_extras = extended.added_extras) - args = args.dup - added_extras.each { |e| args.delete(e) } - end - yield(obj, args) - end - - extended_obj = extended.object - extended_args = extended.arguments # rubocop:disable Development/ContextIsPassedCop - memos = extended.memos || EMPTY_HASH - - ctx.query.after_lazy(value) do |resolved_value| - idx = 0 - @extensions.each do |ext| - memo = memos[idx] - # TODO after_lazy? - resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo) - idx += 1 - end - resolved_value - end - end - end - - def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0) - extension = @extensions[idx] - if extension - extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo| - if memo - memos = extended.memos ||= {} - memos[idx] = memo - end - - if (extras = extension.added_extras) - ae = extended.added_extras ||= [] - ae.concat(extras) - end - - extended.object = extended_obj - extended.arguments = extended_args - run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) } - end - else - yield(obj, args) - end - end - - def apply_own_complexity_to(child_complexity, query, nodes) - case (own_complexity = complexity) - when Numeric - own_complexity + child_complexity - when Proc - arguments = query.arguments_for(nodes.first, self) - if arguments.is_a?(GraphQL::ExecutionError) - return child_complexity - elsif arguments.respond_to?(:keyword_arguments) - arguments = arguments.keyword_arguments - end - - own_complexity.call(query.context, arguments, child_complexity) - else - raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}" - end - end - - def set_pagination_extensions(connection_extension:) - # This should run before connection extension, - # but should it run after the definition block? - if scoped? - self.extension(ScopeExtension, call_after_define: false) - end - - # The problem with putting this after the definition_block - # is that it would override arguments - if connection? && connection_extension - self.extension(connection_extension, call_after_define: false) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/field/connection_extension.rb b/vendor/gems/graphql/lib/graphql/schema/field/connection_extension.rb deleted file mode 100644 index 30463997572..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/field/connection_extension.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Field - class ConnectionExtension < GraphQL::Schema::FieldExtension - def apply - field.argument :after, "String", "Returns the elements in the list that come after the specified cursor.", required: false - field.argument :before, "String", "Returns the elements in the list that come before the specified cursor.", required: false - field.argument :first, "Int", "Returns the first _n_ elements from the list.", required: false - field.argument :last, "Int", "Returns the last _n_ elements from the list.", required: false - end - - # Remove pagination args before passing it to a user method - def resolve(object:, arguments:, context:) - next_args = arguments.dup - next_args.delete(:first) - next_args.delete(:last) - next_args.delete(:before) - next_args.delete(:after) - yield(object, next_args, arguments) - end - - def after_resolve(value:, object:, arguments:, context:, memo:) - original_arguments = memo - # rename some inputs to avoid conflicts inside the block - maybe_lazy = value - value = nil - context.query.after_lazy(maybe_lazy) do |resolved_value| - value = resolved_value - if value.is_a? GraphQL::ExecutionError - # This isn't even going to work because context doesn't have ast_node anymore - context.add_error(value) - nil - elsif value.nil? - nil - elsif value.is_a?(GraphQL::Pagination::Connection) - # update the connection with some things that may not have been provided - value.context ||= context - value.parent ||= object.object - value.first_value ||= original_arguments[:first] - value.after_value ||= original_arguments[:after] - value.last_value ||= original_arguments[:last] - value.before_value ||= original_arguments[:before] - value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method - value.field ||= field - if field.has_max_page_size? && !value.has_max_page_size_override? - value.max_page_size = field.max_page_size - end - if field.has_default_page_size? && !value.has_default_page_size_override? - value.default_page_size = field.default_page_size - end - if (custom_t = context.schema.connections.edge_class_for_field(@field)) - value.edge_class = custom_t - end - value - else - context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers - context.schema.connections.wrap(field, object.object, value, original_arguments, context) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/field/scope_extension.rb b/vendor/gems/graphql/lib/graphql/schema/field/scope_extension.rb deleted file mode 100644 index f031164f3da..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/field/scope_extension.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Field - class ScopeExtension < GraphQL::Schema::FieldExtension - def after_resolve(object:, arguments:, context:, value:, memo:) - if value.nil? - value - else - ret_type = @field.type.unwrap - if ret_type.respond_to?(:scope_items) - scoped_items = ret_type.scope_items(value, context) - if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects - if (current_runtime_state = Fiber[:__graphql_runtime_info]) && - (query_runtime_state = current_runtime_state[context.query]) - query_runtime_state.was_authorized_by_scope_items = true - end - end - scoped_items - else - value - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/field_extension.rb b/vendor/gems/graphql/lib/graphql/schema/field_extension.rb deleted file mode 100644 index 0b264af625f..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/field_extension.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - # Extend this class to make field-level customizations to resolve behavior. - # - # When a extension is added to a field with `extension(MyExtension)`, a `MyExtension` instance - # is created, and its hooks are applied whenever that field is called. - # - # The instance is frozen so that instance variables aren't modified during query execution, - # which could cause all kinds of issues due to race conditions. - class FieldExtension - # @return [GraphQL::Schema::Field] - attr_reader :field - - # @return [Object] - attr_reader :options - - # @return [Array, nil] `default_argument`s added, if any were added (otherwise, `nil`) - attr_reader :added_default_arguments - - # Called when the extension is mounted with `extension(name, options)`. - # The instance will be frozen to avoid improper use of state during execution. - # @param field [GraphQL::Schema::Field] The field where this extension was mounted - # @param options [Object] The second argument to `extension`, or `{}` if nothing was passed. - def initialize(field:, options:) - @field = field - @options = options || {} - @added_default_arguments = nil - apply - end - - class << self - # @return [Array(Array, Hash), nil] A list of default argument configs, or `nil` if there aren't any - def default_argument_configurations - args = superclass.respond_to?(:default_argument_configurations) ? superclass.default_argument_configurations : nil - if @own_default_argument_configurations - if args - args.concat(@own_default_argument_configurations) - else - args = @own_default_argument_configurations.dup - end - end - args - end - - # @see Argument#initialize - # @see HasArguments#argument - def default_argument(*argument_args, **argument_kwargs) - configs = @own_default_argument_configurations ||= [] - configs << [argument_args, argument_kwargs] - end - - # If configured, these `extras` will be added to the field if they aren't already present, - # but removed by from `arguments` before the field's `resolve` is called. - # (The extras _will_ be present for other extensions, though.) - # - # @param new_extras [Array] If provided, assign extras used by this extension - # @return [Array] any extras assigned to this extension - def extras(new_extras = nil) - if new_extras - @own_extras = new_extras - end - - inherited_extras = self.superclass.respond_to?(:extras) ? superclass.extras : nil - if @own_extras - if inherited_extras - inherited_extras + @own_extras - else - @own_extras - end - elsif inherited_extras - inherited_extras - else - GraphQL::EmptyObjects::EMPTY_ARRAY - end - end - end - - # Called when this extension is attached to a field. - # The field definition may be extended during this method. - # @return [void] - def apply - end - - # Called after the field's definition block has been executed. - # (Any arguments from the block are present on `field`) - # @return [void] - def after_define - end - - # @api private - def after_define_apply - after_define - if (configs = self.class.default_argument_configurations) - existing_keywords = field.all_argument_definitions.map(&:keyword) - existing_keywords.uniq! - @added_default_arguments = [] - configs.each do |config| - argument_args, argument_kwargs = config - arg_name = argument_args[0] - if !existing_keywords.include?(arg_name) - @added_default_arguments << arg_name - field.argument(*argument_args, **argument_kwargs) - end - end - end - if !(extras = self.class.extras).empty? - @added_extras = extras - field.extras - field.extras(@added_extras) - else - @added_extras = nil - end - freeze - end - - # @api private - attr_reader :added_extras - - # Called before resolving {#field}. It should either: - # - # - `yield` values to continue execution; OR - # - return something else to shortcut field execution. - # - # Whatever this method returns will be used for execution. - # - # @param object [Object] The object the field is being resolved on - # @param arguments [Hash] Ruby keyword arguments for resolving this field - # @param context [Query::Context] the context for this query - # @yieldparam object [Object] The object to continue resolving the field on - # @yieldparam arguments [Hash] The keyword arguments to continue resolving with - # @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later - # @return [Object] The return value for this field. - def resolve(object:, arguments:, context:) - yield(object, arguments, nil) - end - - # Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced, - # but before the value was added to the GraphQL response. - # - # Whatever this hook returns will be used as the return value. - # - # @param object [Object] The object the field is being resolved on - # @param arguments [Hash] Ruby keyword arguments for resolving this field - # @param context [Query::Context] the context for this query - # @param value [Object] Whatever the field previously returned - # @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one - # @return [Object] The return value for this field. - def after_resolve(object:, arguments:, context:, value:, memo:) - value - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/find_inherited_value.rb b/vendor/gems/graphql/lib/graphql/schema/find_inherited_value.rb deleted file mode 100644 index 08a4fab9b9e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/find_inherited_value.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - module FindInheritedValue - def self.extended(child_cls) - child_cls.singleton_class.include(GraphQL::EmptyObjects) - end - - def self.included(child_cls) - child_cls.include(GraphQL::EmptyObjects) - end - - private - - def find_inherited_value(method_name, default_value = nil) - if self.is_a?(Class) - superclass.respond_to?(method_name, true) ? superclass.send(method_name) : default_value - else - ancestors_except_self = ancestors - ancestors_except_self.delete(self) - ancestors_except_self.each do |ancestor| - if ancestor.respond_to?(method_name, true) - return ancestor.send(method_name) - end - end - default_value - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/finder.rb b/vendor/gems/graphql/lib/graphql/schema/finder.rb deleted file mode 100644 index 6982986263e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/finder.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # Find schema members using string paths - # - # @example Finding object types - # MySchema.find("SomeObjectType") - # - # @example Finding fields - # MySchema.find("SomeObjectType.myField") - # - # @example Finding arguments - # MySchema.find("SomeObjectType.myField.anArgument") - # - # @example Finding directives - # MySchema.find("@include") - # - class Finder - class MemberNotFoundError < ArgumentError; end - - def initialize(schema) - @schema = schema - end - - def find(path) - path = path.split(".") - type_or_directive = path.shift - - if type_or_directive.start_with?("@") - directive = schema.directives[type_or_directive[1..-1]] - - if directive.nil? - raise MemberNotFoundError, "Could not find directive `#{type_or_directive}` in schema." - end - - return directive if path.empty? - - find_in_directive(directive, path: path) - else - type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time - - if type.nil? - raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema." - end - - return type if path.empty? - - find_in_type(type, path: path) - end - end - - private - - attr_reader :schema - - def find_in_directive(directive, path:) - argument_name = path.shift - argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time - - if argument.nil? - raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}." - end - - argument - end - - def find_in_type(type, path:) - case type.kind.name - when "OBJECT" - find_in_fields_type(type, kind: "object", path: path) - when "INTERFACE" - find_in_fields_type(type, kind: "interface", path: path) - when "INPUT_OBJECT" - find_in_input_object(type, path: path) - when "UNION" - # Error out if path that was provided is too long - # i.e UnionType.PossibleType.aField - # Use PossibleType.aField instead. - if invalid = path.first - raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead." - end - when "ENUM" - find_in_enum_type(type, path: path) - else - raise "Unexpected find_in_type: #{type.inspect} (#{path})" - end - end - - def find_in_fields_type(type, kind:, path:) - field_name = path.shift - field = schema.get_field(type, field_name) - - if field.nil? - raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`." - end - - return field if path.empty? - - find_in_field(field, path: path) - end - - def find_in_field(field, path:) - argument_name = path.shift - argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time - - if argument.nil? - raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`." - end - - # Error out if path that was provided is too long - # i.e Type.field.argument.somethingBad - if invalid = path.first - raise MemberNotFoundError, "Cannot select member `#{invalid}` on a field." - end - - argument - end - - def find_in_input_object(input_object, path:) - field_name = path.shift - input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time - - if input_field.nil? - raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`." - end - - # Error out if path that was provided is too long - # i.e InputType.inputField.bad - if invalid = path.first - raise MemberNotFoundError, "Cannot select member `#{invalid}` on an input field." - end - - input_field - end - - def find_in_enum_type(enum_type, path:) - value_name = path.shift - enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime - - if enum_value.nil? - raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`." - end - - # Error out if path that was provided is too long - # i.e Enum.VALUE.wat - if invalid = path.first - raise MemberNotFoundError, "Cannot select member `#{invalid}` on an enum value." - end - - enum_value - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/has_single_input_argument.rb b/vendor/gems/graphql/lib/graphql/schema/has_single_input_argument.rb deleted file mode 100644 index c58a8e94b88..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/has_single_input_argument.rb +++ /dev/null @@ -1,160 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - module HasSingleInputArgument - def resolve_with_support(**inputs) - if inputs[:input].is_a?(InputObject) - input = inputs[:input].to_kwargs - else - input = inputs[:input] - end - - new_extras = field ? field.extras : [] - all_extras = self.class.extras + new_extras - - # Transfer these from the top-level hash to the - # shortcutted `input:` object - all_extras.each do |ext| - # It's possible that the `extra` was not passed along by this point, - # don't re-add it if it wasn't given here. - if inputs.key?(ext) - input[ext] = inputs[ext] - end - end - - if input - # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are. - input_kwargs = input.to_h - else - # Relay Classic Mutations with no `argument`s - # don't require `input:` - input_kwargs = {} - end - - if !input_kwargs.empty? - super(**input_kwargs) - else - super() - end - end - - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def dummy - @dummy ||= begin - d = Class.new(GraphQL::Schema::Resolver) - d.graphql_name "#{self.graphql_name}DummyResolver" - d.argument_class(self.argument_class) - # TODO make this lazier? - d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}") - d - end - end - - def field_arguments(context = GraphQL::Query::NullContext.instance) - dummy.arguments(context) - end - - def get_field_argument(name, context = GraphQL::Query::NullContext.instance) - dummy.get_argument(name, context) - end - - def own_field_arguments - dummy.own_arguments - end - - def any_field_arguments? - dummy.any_arguments? - end - - def all_field_argument_definitions - dummy.all_argument_definitions - end - - # Also apply this argument to the input type: - def argument(*args, own_argument: false, **kwargs, &block) - it = input_type # make sure any inherited arguments are already added to it - arg = super(*args, **kwargs, &block) - - # This definition might be overriding something inherited; - # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions - prev_args = it.own_arguments[arg.graphql_name] - case prev_args - when GraphQL::Schema::Argument - if prev_args.owner != self - it.own_arguments.delete(arg.graphql_name) - end - when Array - prev_args.reject! { |a| a.owner != self } - if prev_args.empty? - it.own_arguments.delete(arg.graphql_name) - end - end - - it.add_argument(arg) - arg - end - - # The base class for generated input object types - # @param new_class [Class] The base class to use for generating input object definitions - # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject}) - def input_object_class(new_class = nil) - if new_class - @input_object_class = new_class - end - @input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject) - end - - # @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type - # @return [Class] The generated {Schema::InputObject} class for this mutation's `input` - def input_type(new_input_type = nil) - if new_input_type - @input_type = new_input_type - end - @input_type ||= generate_input_type - end - - private - - # Generate the input type for the `input:` argument - # To customize how input objects are generated, override this method - # @return [Class] a subclass of {.input_object_class} - def generate_input_type - mutation_args = all_argument_definitions - mutation_class = self - Class.new(input_object_class) do - class << self - def default_graphql_name - "#{self.mutation.graphql_name}Input" - end - - def description(new_desc = nil) - super || "Autogenerated input type of #{self.mutation.graphql_name}" - end - end - # For compatibility, in case no arguments are defined: - has_no_arguments(true) - mutation(mutation_class) - # these might be inherited: - mutation_args.each do |arg| - add_argument(arg) - end - end - end - end - - private - - def authorize_arguments(args, values) - # remove the `input` wrapper to match values - input_type = args.find { |a| a.graphql_name == "input" }.type.unwrap - input_args = context.types.arguments(input_type) - super(input_args, values) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/input_object.rb b/vendor/gems/graphql/lib/graphql/schema/input_object.rb deleted file mode 100644 index 76eff204e08..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/input_object.rb +++ /dev/null @@ -1,308 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class InputObject < GraphQL::Schema::Member - extend Forwardable - extend GraphQL::Schema::Member::HasArguments - extend GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader - extend GraphQL::Schema::Member::ValidatesInput - extend GraphQL::Schema::Member::HasValidators - - include GraphQL::Dig - - # Raised when an InputObject doesn't have any arguments defined and hasn't explicitly opted out of this requirement - class ArgumentsAreRequiredError < GraphQL::Error - def initialize(input_object_type) - message = "Input Object types must have arguments, but #{input_object_type.graphql_name} doesn't have any. Define an argument for this type, remove it from your schema, or add `has_no_arguments(true)` to its definition." - super(message) - end - end - - # @return [GraphQL::Query::Context] The context for this query - attr_reader :context - # @return [GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance - attr_reader :arguments - - # Ruby-like hash behaviors, read-only - def_delegators :@ruby_style_hash, :keys, :values, :each, :map, :any?, :empty? - - def initialize(arguments, ruby_kwargs:, context:, defaults_used:) - @context = context - @ruby_style_hash = ruby_kwargs - @arguments = arguments - # Apply prepares, not great to have it duplicated here. - arg_defns = context ? context.types.arguments(self.class) : self.class.arguments(context).each_value - arg_defns.each do |arg_defn| - ruby_kwargs_key = arg_defn.keyword - if @ruby_style_hash.key?(ruby_kwargs_key) - # Weirdly, procs are applied during coercion, but not methods. - # Probably because these methods require a `self`. - if arg_defn.prepare.is_a?(Symbol) || context.nil? - prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key]) - overwrite_argument(ruby_kwargs_key, prepared_value) - end - end - end - end - - def to_h - unwrap_value(@ruby_style_hash) - end - - def to_hash - to_h - end - - def deconstruct_keys(keys = nil) - if keys.nil? - @ruby_style_hash - else - new_h = {} - keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] } - new_h - end - end - - def prepare - if @context - object = @context[:current_object] - # Pass this object's class with `as` so that messages are rendered correctly from inherited validators - Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class) - self - else - self - end - end - - def unwrap_value(value) - case value - when Array - value.map { |item| unwrap_value(item) } - when Hash - value.reduce({}) do |h, (key, value)| - h.merge!(key => unwrap_value(value)) - end - when InputObject - value.to_h - else - value - end - end - - # Lookup a key on this object, it accepts new-style underscored symbols - # Or old-style camelized identifiers. - # @param key [Symbol, String] - def [](key) - if @ruby_style_hash.key?(key) - @ruby_style_hash[key] - elsif @arguments - @arguments[key] - else - nil - end - end - - def key?(key) - @ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key)) || false - end - - # A copy of the Ruby-style hash - def to_kwargs - @ruby_style_hash.dup - end - - class << self - def authorized?(obj, value, ctx) - # Authorize each argument (but this doesn't apply if `prepare` is implemented): - if value.respond_to?(:key?) - ctx.types.arguments(self).each do |input_obj_arg| - if value.key?(input_obj_arg.keyword) && - !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx) - return false - end - end - end - # It didn't early-return false: - true - end - - def one_of - if !one_of? - if all_argument_definitions.any? { |arg| arg.type.non_null? } - raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`" - end - directive(GraphQL::Schema::Directive::OneOf) - end - end - - def one_of? - false # Re-defined when `OneOf` is added - end - - def argument(*args, **kwargs, &block) - argument_defn = super(*args, **kwargs, &block) - if one_of? - if argument_defn.type.non_null? - raise ArgumentError, "Argument '#{argument_defn.path}' must be nullable because it is part of a OneOf type, add `required: false`." - end - if argument_defn.default_value? - raise ArgumentError, "Argument '#{argument_defn.path}' cannot have a default value because it is part of a OneOf type, remove `default_value: ...`." - end - end - # Add a method access - suppress_redefinition_warning do - define_accessor_method(argument_defn.keyword) - end - argument_defn - end - - def kind - GraphQL::TypeKinds::INPUT_OBJECT - end - - # @api private - INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object." - - def validate_non_null_input(input, ctx, max_errors: nil) - types = ctx.types - - if input.is_a?(Array) - return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) }) - end - - if !(input.respond_to?(:to_h) || input.respond_to?(:to_unsafe_h)) - # We're not sure it'll act like a hash, so reject it: - return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) }) - end - - # Inject missing required arguments - missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)| - if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name) - m[argument.graphql_name] = nil - end - - m - end - - result = nil - [input, missing_required_inputs].each do |args_to_validate| - args_to_validate.each do |argument_name, value| - argument = types.argument(self, argument_name) - # Items in the input that are unexpected - if argument.nil? - result ||= Query::InputValidationResult.new - result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name]) - else - # Items in the input that are expected, but have invalid values - argument_result = argument.type.validate_input(value, ctx) - result ||= Query::InputValidationResult.new - if !argument_result.valid? - result.merge_result!(argument_name, argument_result) - end - end - end - end - - if one_of? - if input.size == 1 - input.each do |name, value| - if value.nil? - result ||= Query::InputValidationResult.new - result.add_problem("'#{graphql_name}' requires exactly one argument, but '#{name}' was `null`.") - end - end - else - result ||= Query::InputValidationResult.new - result.add_problem("'#{graphql_name}' requires exactly one argument, but #{input.size} were provided.") - end - end - - result - end - - def coerce_input(value, ctx) - if value.nil? - return nil - end - - arguments = coerce_arguments(nil, value, ctx) - - ctx.query.after_lazy(arguments) do |resolved_arguments| - if resolved_arguments.is_a?(GraphQL::Error) - raise resolved_arguments - else - self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil) - end - end - end - - # It's funny to think of a _result_ of an input object. - # This is used for rendering the default value in introspection responses. - def coerce_result(value, ctx) - # Allow the application to provide values as :snake_symbols, and convert them to the camelStrings - value = value.reduce({}) { |memo, (k, v)| memo[Member::BuildType.camelize(k.to_s)] = v; memo } - - result = {} - - arguments(ctx).each do |input_key, input_field_defn| - input_value = value[input_key] - if value.key?(input_key) - result[input_key] = if input_value.nil? - nil - else - input_field_defn.type.coerce_result(input_value, ctx) - end - end - end - - result - end - - # @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments. - # @return [void] - def has_no_arguments(new_has_no_arguments) - @has_no_arguments = new_has_no_arguments - nil - end - - # @return [Boolean] `true` if `has_no_arguments(true)` was configued - def has_no_arguments? - @has_no_arguments - end - - def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true) - if require_defined_arguments && !has_no_arguments? && !any_arguments? - warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.") - end - super(context, false) - end - - private - - # Suppress redefinition warning for objectId arguments - def suppress_redefinition_warning - verbose = $VERBOSE - $VERBOSE = nil - yield - ensure - $VERBOSE = verbose - end - - def define_accessor_method(method_name) - define_method(method_name) { self[method_name] } - alias_method(method_name, method_name) - end - end - - private - - def overwrite_argument(key, value) - # Argument keywords come in frozen from the interpreter, dup them before modifying them. - if @ruby_style_hash.frozen? - @ruby_style_hash = @ruby_style_hash.dup - end - @ruby_style_hash[key] = value - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/interface.rb b/vendor/gems/graphql/lib/graphql/schema/interface.rb deleted file mode 100644 index 160d51693a2..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/interface.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - module Interface - include GraphQL::Schema::Member::GraphQLTypeNames - module DefinitionMethods - include GraphQL::Schema::Member::BaseDSLMethods - # ConfigurationExtension's responsibilities are in `def included` below - include GraphQL::Schema::Member::TypeSystemHelpers - include GraphQL::Schema::Member::HasFields - include GraphQL::Schema::Member::HasPath - include GraphQL::Schema::Member::RelayShortcuts - include GraphQL::Schema::Member::Scoped - include GraphQL::Schema::Member::HasAstNode - include GraphQL::Schema::Member::HasUnresolvedTypeError - include GraphQL::Schema::Member::HasDataloader - include GraphQL::Schema::Member::HasDirectives - include GraphQL::Schema::Member::HasInterfaces - - # Methods defined in this block will be: - # - Added as class methods to this interface - # - Added as class methods to all child interfaces - def definition_methods(&block) - # Use an instance variable to tell whether it's been included previously or not; - # You can't use constant detection because constants are brought into scope - # by `include`, which has already happened at this point. - if !defined?(@_definition_methods) - defn_methods_module = Module.new - @_definition_methods = defn_methods_module - const_set(:DefinitionMethods, defn_methods_module) - extend(self::DefinitionMethods) - end - self::DefinitionMethods.module_exec(&block) - end - - # @see {Schema::Warden} hides interfaces without visible implementations - def visible?(context) - true - end - - def type_membership_class(membership_class = nil) - if membership_class - @type_membership_class = membership_class - else - @type_membership_class || find_inherited_value(:type_membership_class, GraphQL::Schema::TypeMembership) - end - end - - # Here's the tricky part. Make sure behavior keeps making its way down the inheritance chain. - def included(child_class) - if !child_class.is_a?(Class) - # In this case, it's been included into another interface. - # This is how interface inheritance is implemented - - # We need this before we can call `own_interfaces` - child_class.extend(Schema::Interface::DefinitionMethods) - - child_class.type_membership_class(self.type_membership_class) - child_class.ancestors.reverse_each do |ancestor| - if ancestor.const_defined?(:DefinitionMethods) && ancestor != child_class - child_class.extend(ancestor::DefinitionMethods) - end - end - - child_class.introspection(introspection) - child_class.description(description) - child_class.comment(nil) - # If interfaces are mixed into each other, only define this class once - if !child_class.const_defined?(:UnresolvedTypeError, false) - add_unresolved_type_error(child_class) - end - elsif child_class < GraphQL::Schema::Object - # This is being included into an object type, make sure it's using `implements(...)` - backtrace_line = caller_locations(0, 10).find do |location| - location.base_label == "implements" && - location.path.end_with?("schema/member/has_interfaces.rb") - end - - if !backtrace_line - raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`" - end - end - - super - end - - # Register other Interface or Object types as implementers of this Interface. - # - # When those Interfaces or Objects aren't used as the return values of fields, - # they may have to be registered using this method so that GraphQL-Ruby can find them. - # @param types [Class, Module] - # @return [Array] Implementers of this interface, if they're registered - def orphan_types(*types) - if !types.empty? - @orphan_types ||= [] - @orphan_types.concat(types) - else - if defined?(@orphan_types) - all_orphan_types = @orphan_types.dup - if defined?(super) - all_orphan_types += super - all_orphan_types.uniq! - end - all_orphan_types - elsif defined?(super) - super - else - EmptyObjects::EMPTY_ARRAY - end - end - end - - def kind - GraphQL::TypeKinds::INTERFACE - end - end - - extend DefinitionMethods - - def unwrap - self - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/introspection_system.rb b/vendor/gems/graphql/lib/graphql/schema/introspection_system.rb deleted file mode 100644 index 93529d81c9e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/introspection_system.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class IntrospectionSystem - attr_reader :types, :possible_types - - def initialize(schema) - @schema = schema - @class_based = !!@schema.is_a?(Class) - @built_in_namespace = GraphQL::Introspection - @custom_namespace = if @class_based - schema.introspection || @built_in_namespace - else - schema.introspection_namespace || @built_in_namespace - end - - type_defns = [ - load_constant(:SchemaType), - load_constant(:TypeType), - load_constant(:FieldType), - load_constant(:DirectiveType), - load_constant(:EnumValueType), - load_constant(:InputValueType), - load_constant(:TypeKindEnum), - load_constant(:DirectiveLocationEnum) - ] - @types = {} - @possible_types = {}.compare_by_identity - type_defns.each do |t| - @types[t.graphql_name] = t - @possible_types[t] = [t] - end - @entry_point_fields = - if schema.disable_introspection_entry_points? - {} - else - entry_point_fields = get_fields_from_class(class_sym: :EntryPoints) - entry_point_fields.delete('__schema') if schema.disable_schema_introspection_entry_point? - entry_point_fields.delete('__type') if schema.disable_type_introspection_entry_point? - entry_point_fields - end - @entry_point_fields.each { |k, v| v.dynamic_introspection = true } - @dynamic_fields = get_fields_from_class(class_sym: :DynamicFields) - @dynamic_fields.each { |k, v| v.dynamic_introspection = true } - end - - def entry_points - @entry_point_fields.values - end - - def entry_point(name:) - @entry_point_fields[name] - end - - def dynamic_fields - @dynamic_fields.values - end - - def dynamic_field(name:) - @dynamic_fields[name] - end - - # The introspection system is prepared with a bunch of LateBoundTypes. - # Replace those with the objects that they refer to, since LateBoundTypes - # aren't handled at runtime. - # - # @api private - # @return void - def resolve_late_bindings - @types.each do |name, t| - if t.kind.fields? - t.all_field_definitions.each do |field_defn| - field_defn.type = resolve_late_binding(field_defn.type) - end - end - end - - @entry_point_fields.each do |name, f| - f.type = resolve_late_binding(f.type) - end - - @dynamic_fields.each do |name, f| - f.type = resolve_late_binding(f.type) - end - nil - end - - private - - def resolve_late_binding(late_bound_type) - case late_bound_type - when GraphQL::Schema::LateBoundType - type_name = late_bound_type.name - @types[type_name] || @schema.get_type(type_name) - when GraphQL::Schema::List - resolve_late_binding(late_bound_type.of_type).to_list_type - when GraphQL::Schema::NonNull - resolve_late_binding(late_bound_type.of_type).to_non_null_type - when Module - # It's a normal type -- no change required - late_bound_type - else - raise "Invariant: unexpected type: #{late_bound_type} (#{late_bound_type.class})" - end - end - - def load_constant(class_name) - const = @custom_namespace.const_get(class_name) - dup_type_class(const) - rescue NameError - # Dup the built-in so that the cached fields aren't shared - dup_type_class(@built_in_namespace.const_get(class_name)) - end - - def get_fields_from_class(class_sym:) - object_type_defn = load_constant(class_sym) - object_type_defn.fields - end - - # This is probably not 100% robust -- but it has to be good enough to avoid modifying the built-in introspection types - def dup_type_class(type_class) - type_name = type_class.graphql_name - Class.new(type_class) do - # This won't be inherited like other things will - graphql_name(type_name) - - if type_class.kind.fields? - type_class.fields.each do |_name, field_defn| - dup_field = field_defn.dup - dup_field.owner = self - add_field(dup_field) - end - end - end - end - - class PerFieldProxyResolve - def initialize(object_class:, inner_resolve:) - @object_class = object_class - @inner_resolve = inner_resolve - end - - def call(obj, args, ctx) - query_ctx = ctx.query.context - # Remove the QueryType wrapper - if obj.is_a?(GraphQL::Schema::Object) - obj = obj.object - end - wrapped_object = @object_class.wrap(obj, query_ctx) - @inner_resolve.call(wrapped_object, args, ctx) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/late_bound_type.rb b/vendor/gems/graphql/lib/graphql/schema/late_bound_type.rb deleted file mode 100644 index e8ec59660af..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/late_bound_type.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - # A stand-in for a type which will be resolved in a given schema, by name. - # TODO: support argument types too, make this a public API somehow - # @api Private - class LateBoundType - attr_reader :name - alias :graphql_name :name - def initialize(local_name) - @name = local_name - @to_non_null_type = nil - @to_list_type = nil - end - - def unwrap - self - end - - def to_non_null_type - @to_non_null_type ||= GraphQL::Schema::NonNull.new(self) - end - - def to_list_type - @to_list_type ||= GraphQL::Schema::List.new(self) - end - - def to_type_signature - name - end - - def inspect - "#" - end - - def non_null? - false - end - - alias :to_s :inspect - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/list.rb b/vendor/gems/graphql/lib/graphql/schema/list.rb deleted file mode 100644 index 8c89d3d6fe2..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/list.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # Represents a list type in the schema. - # Wraps a {Schema::Member} as a list type. - # @see {Schema::Member::TypeSystemHelpers#to_list_type} - class List < GraphQL::Schema::Wrapper - include Schema::Member::ValidatesInput - - # @return [GraphQL::TypeKinds::LIST] - def kind - GraphQL::TypeKinds::LIST - end - - # @return [true] - def list? - true - end - - def to_type_signature - "[#{@of_type.to_type_signature}]" - end - - # This is for introspection, where it's expected the name will be `null` - def graphql_name - nil - end - - # Also for implementing introspection - def description - nil - end - - def coerce_result(value, ctx) - value.map { |i| i.nil? ? nil : of_type.coerce_result(i, ctx) } - end - - def coerce_input(value, ctx) - if value.nil? - nil - else - coerced = ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) } - ctx.schema.after_any_lazies(coerced, &:itself) - end - end - - def validate_non_null_input(value, ctx, max_errors: nil) - result = GraphQL::Query::InputValidationResult.new - ensure_array(value).each_with_index do |item, index| - item_result = of_type.validate_input(item, ctx) - unless item_result.valid? - if max_errors - if max_errors == 0 - add_max_errors_reached_message(result) - break - end - - max_errors -= 1 - end - - result.merge_result!(index, item_result) - end - end - result.valid? ? nil : result - end - - private - - def ensure_array(value) - # `Array({ a: 1 })` makes `[[:a, 1]]`, so do it manually - if value.is_a?(Array) - value - else - [value] - end - end - - def add_max_errors_reached_message(result) - message = "Too many errors processing list variable, max validation error limit reached. Execution aborted" - item_result = GraphQL::Query::InputValidationResult.from_problem(message) - result.merge_result!(nil, item_result) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/loader.rb b/vendor/gems/graphql/lib/graphql/schema/loader.rb deleted file mode 100644 index 228cb5161b0..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/loader.rb +++ /dev/null @@ -1,227 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - # You can use the result of {GraphQL::Introspection::INTROSPECTION_QUERY} - # to make a schema. This schema is missing some important details like - # `resolve` functions, but it does include the full type system, - # so you can use it to validate queries. - # - # @see GraphQL::Schema.from_introspection for a public API - module Loader - extend self - - # Create schema with the result of an introspection query. - # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY} - # @return [Class] the schema described by `input` - def load(introspection_result) - schema = introspection_result.fetch("data").fetch("__schema") - - types = {} - type_resolver = ->(type) { resolve_type(types, type) } - - schema.fetch("types").each do |type| - next if type.fetch("name").start_with?("__") - type_object = define_type(type, type_resolver) - types[type["name"]] = type_object - end - - directives = [] - schema.fetch("directives", []).each do |directive| - next if GraphQL::Schema.default_directives.include?(directive.fetch("name")) - directives << define_directive(directive, type_resolver) - end - - Class.new(GraphQL::Schema) do - add_type_and_traverse(types.values, root: false) - orphan_types(types.values.select { |t| t.kind.object? }) - directives(directives) - description(schema["description"]) - - def self.resolve_type(*) - raise(GraphQL::RequiredImplementationMissingError, "This schema was loaded from string, so it can't resolve types for objects") - end - - [:query, :mutation, :subscription].each do |root| - type = schema["#{root}Type"] - if type - type_defn = types.fetch(type.fetch("name")) - self.public_send(root, type_defn) - end - end - end - end - - NullScalarCoerce = ->(val, _ctx) { val } - - class << self - private - - def resolve_type(types, type) - case kind = type.fetch("kind") - when "ENUM", "INTERFACE", "INPUT_OBJECT", "OBJECT", "SCALAR", "UNION" - type_name = type.fetch("name") - type = types[type_name] || Schema::BUILT_IN_TYPES[type_name] - if type.nil? - GraphQL::Schema::LateBoundType.new(type_name) - else - type - end - when "LIST" - Schema::List.new(resolve_type(types, type.fetch("ofType"))) - when "NON_NULL" - Schema::NonNull.new(resolve_type(types, type.fetch("ofType"))) - else - fail GraphQL::RequiredImplementationMissingError, "#{kind} not implemented" - end - end - - def extract_default_value(default_value_str, input_value_ast) - case input_value_ast - when String, Integer, Float, TrueClass, FalseClass - input_value_ast - when GraphQL::Language::Nodes::Enum - input_value_ast.name - when GraphQL::Language::Nodes::NullValue - nil - when GraphQL::Language::Nodes::InputObject - input_value_ast.to_h - when Array - input_value_ast.map { |element| extract_default_value(default_value_str, element) } - else - raise( - "Encountered unexpected type when loading default value. "\ - "input_value_ast.class is #{input_value_ast.class} "\ - "default_value is #{default_value_str}" - ) - end - end - - def define_type(type, type_resolver) - loader = self - case type.fetch("kind") - when "ENUM" - Class.new(GraphQL::Schema::Enum) do - graphql_name(type["name"]) - description(type["description"]) - type["enumValues"].each do |enum_value| - value( - enum_value["name"], - description: enum_value["description"], - deprecation_reason: enum_value["deprecationReason"], - ) - end - end - when "INTERFACE" - Module.new do - include GraphQL::Schema::Interface - graphql_name(type["name"]) - description(type["description"]) - loader.build_fields(self, type["fields"] || [], type_resolver) - end - when "INPUT_OBJECT" - Class.new(GraphQL::Schema::InputObject) do - graphql_name(type["name"]) - description(type["description"]) - loader.build_arguments(self, type["inputFields"], type_resolver) - end - when "OBJECT" - Class.new(GraphQL::Schema::Object) do - graphql_name(type["name"]) - description(type["description"]) - if type["interfaces"] - type["interfaces"].each do |interface_type| - implements(type_resolver.call(interface_type)) - end - end - loader.build_fields(self, type["fields"], type_resolver) - end - when "SCALAR" - type_name = type.fetch("name") - if (builtin = GraphQL::Schema::BUILT_IN_TYPES[type_name]) - builtin - else - Class.new(GraphQL::Schema::Scalar) do - graphql_name(type["name"]) - description(type["description"]) - specified_by_url(type["specifiedByURL"]) - end - end - when "UNION" - Class.new(GraphQL::Schema::Union) do - graphql_name(type["name"]) - description(type["description"]) - possible_types(*(type["possibleTypes"].map { |pt| type_resolver.call(pt) })) - end - else - fail GraphQL::RequiredImplementationMissingError, "#{type["kind"]} not implemented" - end - end - - def define_directive(directive, type_resolver) - loader = self - Class.new(GraphQL::Schema::Directive) do - graphql_name(directive["name"]) - description(directive["description"]) - locations(*directive["locations"].map(&:to_sym)) - repeatable(directive["isRepeatable"]) - loader.build_arguments(self, directive["args"], type_resolver) - end - end - - public - - def build_fields(type_defn, fields, type_resolver) - loader = self - fields.each do |field_hash| - unwrapped_field_hash = field_hash - while (of_type = unwrapped_field_hash["ofType"]) - unwrapped_field_hash = of_type - end - - type_defn.field( - field_hash["name"], - type: type_resolver.call(field_hash["type"]), - description: field_hash["description"], - deprecation_reason: field_hash["deprecationReason"], - null: true, - camelize: false, - connection_extension: nil, - ) do - if !field_hash["args"].empty? - loader.build_arguments(self, field_hash["args"], type_resolver) - end - end - end - end - - def build_arguments(arg_owner, args, type_resolver) - args.each do |arg| - kwargs = { - type: type_resolver.call(arg["type"]), - description: arg["description"], - deprecation_reason: arg["deprecationReason"], - required: false, - camelize: false, - } - - if arg["defaultValue"] - default_value_str = arg["defaultValue"] - - dummy_query_str = "query getStuff($var: InputObj = #{default_value_str}) { __typename }" - - # Returns a `GraphQL::Language::Nodes::Document`: - dummy_query_ast = GraphQL.parse(dummy_query_str) - - # Reach into the AST for the default value: - input_value_ast = dummy_query_ast.definitions.first.variables.first.default_value - - kwargs[:default_value] = extract_default_value(default_value_str, input_value_ast) - end - - arg_owner.argument(arg["name"], **kwargs) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member.rb b/vendor/gems/graphql/lib/graphql/schema/member.rb deleted file mode 100644 index 285991c59dc..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -require 'graphql/schema/member/base_dsl_methods' -require 'graphql/schema/member/graphql_type_names' -require 'graphql/schema/member/has_ast_node' -require 'graphql/schema/member/has_dataloader' -require 'graphql/schema/member/has_directives' -require 'graphql/schema/member/has_deprecation_reason' -require 'graphql/schema/member/has_interfaces' -require 'graphql/schema/member/has_path' -require 'graphql/schema/member/has_unresolved_type_error' -require 'graphql/schema/member/has_validators' -require 'graphql/schema/member/relay_shortcuts' -require 'graphql/schema/member/scoped' -require 'graphql/schema/member/type_system_helpers' -require 'graphql/schema/member/validates_input' - -module GraphQL - class Schema - # The base class for things that make up the schema, - # eg objects, enums, scalars. - # - # @api private - class Member - include GraphQLTypeNames - extend BaseDSLMethods - extend BaseDSLMethods::ConfigurationExtension - introspection(false) - extend TypeSystemHelpers - extend Scoped - extend RelayShortcuts - extend HasPath - extend HasAstNode - extend HasDirectives - end - end -end - -require 'graphql/schema/member/has_arguments' -require 'graphql/schema/member/has_fields' -require 'graphql/schema/member/build_type' diff --git a/vendor/gems/graphql/lib/graphql/schema/member/base_dsl_methods.rb b/vendor/gems/graphql/lib/graphql/schema/member/base_dsl_methods.rb deleted file mode 100644 index ea197ff8dd5..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/base_dsl_methods.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -require "graphql/schema/find_inherited_value" - -module GraphQL - class Schema - class Member - # DSL methods shared by lots of things in the GraphQL Schema. - # @api private - # @see Classes that extend this, eg {GraphQL::Schema::Object} - module BaseDSLMethods - include GraphQL::Schema::FindInheritedValue - - # Call this with a new name to override the default name for this schema member; OR - # call it without an argument to get the name of this schema member - # - # The default name is implemented in default_graphql_name - # @param new_name [String] - # @return [String] - def graphql_name(new_name = nil) - if new_name - GraphQL::NameValidator.validate!(new_name) - @graphql_name = new_name - else - @graphql_name ||= default_graphql_name - end - end - - # Just a convenience method to point out that people should use graphql_name instead - def name(new_name = nil) - return super() if new_name.nil? - - fail( - "The new name override method is `graphql_name`, not `name`. Usage: "\ - "graphql_name \"#{new_name}\"" - ) - end - - # Call this method to provide a new description; OR - # call it without an argument to get the description - # @param new_description [String] - # @return [String] - def description(new_description = nil) - if new_description - @description = new_description - elsif defined?(@description) - @description - else - @description = nil - end - end - - # Call this method to provide a new comment; OR - # call it without an argument to get the comment - # @param new_comment [String] - # @return [String, nil] - def comment(new_comment = NOT_CONFIGURED) - if !NOT_CONFIGURED.equal?(new_comment) - @comment = new_comment - elsif defined?(@comment) - @comment - else - nil - end - end - - # This pushes some configurations _down_ the inheritance tree, - # in order to prevent repetitive lookups at runtime. - module ConfigurationExtension - def inherited(child_class) - child_class.introspection(introspection) - child_class.description(description) - child_class.comment(nil) - child_class.default_graphql_name = nil - - if defined?(@graphql_name) && @graphql_name && (self.name.nil? || graphql_name != default_graphql_name) - child_class.graphql_name(graphql_name) - else - child_class.graphql_name = nil - end - super - end - end - - # @return [Boolean] If true, this object is part of the introspection system - def introspection(new_introspection = nil) - if !new_introspection.nil? - @introspection = new_introspection - elsif defined?(@introspection) - @introspection - else - false - end - end - - def introspection? - !!@introspection - end - - # The mutation this type was derived from, if it was derived from a mutation - # @return [Class] - def mutation(mutation_class = nil) - if mutation_class - @mutation = mutation_class - elsif defined?(@mutation) - @mutation - else - nil - end - end - - alias :unwrap :itself - - # Creates the default name for a schema member. - # The default name is the Ruby constant name, - # without any namespaces and with any `-Type` suffix removed - def default_graphql_name - @default_graphql_name ||= begin - raise GraphQL::RequiredImplementationMissingError, 'Anonymous class should declare a `graphql_name`' if name.nil? - g_name = -name.split("::").last - g_name.end_with?("Type") ? g_name.sub(/Type\Z/, "") : g_name - end - end - - def visible?(context) - true - end - - def authorized?(object, context) - true - end - - def default_relay - false - end - - protected - - attr_writer :default_graphql_name, :graphql_name - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/build_type.rb b/vendor/gems/graphql/lib/graphql/schema/member/build_type.rb deleted file mode 100644 index 3f8dcec16b4..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/build_type.rb +++ /dev/null @@ -1,186 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Member - # @api private - module BuildType - LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported" - - module_function - # @param type_expr [String, Class, GraphQL::BaseType] - # @return [GraphQL::BaseType] - def parse_type(type_expr, null:) - list_type = false - - return_type = case type_expr - when String - case type_expr - when "String" - GraphQL::Types::String - when "Int", "Integer" - GraphQL::Types::Int - when "Float" - GraphQL::Types::Float - when "Boolean" - GraphQL::Types::Boolean - when "ID" - GraphQL::Types::ID - when /\A\[.*\]\Z/ - list_type = true - # List members are required by default - parse_type(type_expr[1..-2], null: false) - when /.*!\Z/ - null = false - parse_type(type_expr[0..-2], null: true) - else - maybe_type = constantize(type_expr) - case maybe_type - when Module - # This is a way to check that it's the right kind of module: - if maybe_type.respond_to?(:kind) - maybe_type - else - raise ArgumentError, "Unexpected class/module found for GraphQL type: #{type_expr} (must be type definition class/module)" - end - end - end - when GraphQL::Schema::LateBoundType - type_expr - when Array - case type_expr.length - when 1 - list_type = true - # List members are required by default - parse_type(type_expr.first, null: false) - when 2 - inner_type, nullable_option = type_expr - if nullable_option.keys != [:null] || (nullable_option[:null] != true && nullable_option[:null] != false) - raise ArgumentError, LIST_TYPE_ERROR - end - list_type = true - parse_type(inner_type, null: nullable_option[:null]) - else - raise ArgumentError, LIST_TYPE_ERROR - end - when GraphQL::Schema::NonNull, GraphQL::Schema::List - type_expr - when Module - # This is a way to check that it's the right kind of module: - if type_expr.respond_to?(:kind) - type_expr - else - # Eg `String` => GraphQL::Types::String - parse_type(type_expr.name, null: true) - end - when Proc - parse_type(type_expr.call, null: true) - when false - raise ArgumentError, "Received `false` instead of a type, maybe a `!` should be replaced with `null: true` (for fields) or `required: true` (for arguments)" - end - - if return_type.nil? - raise "Unexpected type input: #{type_expr.inspect} (#{type_expr.class})" - end - - # Apply list_type first, that way the - # .to_non_null_type applies to the list type, not the inner type - if list_type - return_type = return_type.to_list_type - end - - if !null - return_type = return_type.to_non_null_type - end - - - return_type - end - - def to_type_name(something) - case something - when GraphQL::Schema::LateBoundType - something.unwrap.name - when Array - to_type_name(something.first) - when Module - if something.respond_to?(:graphql_name) - something.graphql_name - else - to_type_name(something.name) - end - when String - if something.include?("]") || - something.include?("[") || - something.include?("!") || - something.include?("::") - something.gsub(/\]\[\!/, "").split("::").last - else - something - end - when GraphQL::Schema::NonNull, GraphQL::Schema::List - to_type_name(something.unwrap) - else - raise "Unhandled to_type_name input: #{something} (#{something.class})" - end - end - - def camelize(string) - return string if string == '_' - return string unless string.include?("_") - camelized = string.split('_').each(&:capitalize!).join - camelized[0] = camelized[0].downcase - if string.start_with?("_") - match_data = string.match(/\A(_+)/) - camelized = "#{match_data[0]}#{camelized}" - end - camelized - end - - # Resolves constant from string (based on Rails `ActiveSupport::Inflector.constantize`) - def constantize(string) - names = string.split('::') - - # Trigger a built-in NameError exception including the ill-formed constant in the message. - Object.const_get(string) if names.empty? - - # Remove the first blank element in case of '::ClassName' notation. - names.shift if names.size > 1 && names.first.empty? - - names.inject(Object) do |constant, name| - if constant == Object - constant.const_get(name) - else - candidate = constant.const_get(name) - next candidate if constant.const_defined?(name, false) - next candidate unless Object.const_defined?(name) - - # Go down the ancestors to check if it is owned directly. The check - # stops when we reach Object or the end of ancestors tree. - constant = constant.ancestors.inject do |const, ancestor| - break const if ancestor == Object - break ancestor if ancestor.const_defined?(name, false) - const - end - - # Owner is in Object, so raise. - constant.const_get(name, false) - end - end - end - - def underscore(string) - if string.match?(/\A[a-z_]+\Z/) - return string - end - string2 = string.dup - - string2.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') # URLDecoder -> URL_Decoder - string2.gsub!(/([a-z\d])([A-Z])/,'\1_\2') # someThing -> some_Thing - string2.downcase! - - string2 - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/graphql_type_names.rb b/vendor/gems/graphql/lib/graphql/schema/member/graphql_type_names.rb deleted file mode 100644 index 85f81a99987..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/graphql_type_names.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - # These constants are interpreted as GraphQL types when defining fields or arguments - # - # @example - # field :is_draft, Boolean, null: false - # field :id, ID, null: false - # field :score, Int, null: false - # - # @api private - module GraphQLTypeNames - Boolean = "Boolean" - ID = "ID" - Int = "Int" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_arguments.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_arguments.rb deleted file mode 100644 index 323850b997e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_arguments.rb +++ /dev/null @@ -1,428 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Member - module HasArguments - def self.included(cls) - cls.extend(ArgumentClassAccessor) - cls.include(ArgumentObjectLoader) - end - - def self.extended(cls) - cls.extend(ArgumentClassAccessor) - cls.include(ArgumentObjectLoader) - cls.extend(ClassConfigured) - end - - # @see {GraphQL::Schema::Argument#initialize} for parameters - # @return [GraphQL::Schema::Argument] An instance of {argument_class}, created from `*args` - def argument(*args, **kwargs, &block) - kwargs[:owner] = self - loads = kwargs[:loads] - if loads - name = args[0] - name_as_string = name.to_s - - inferred_arg_name = case name_as_string - when /_id$/ - name_as_string.sub(/_id$/, "").to_sym - when /_ids$/ - name_as_string.sub(/_ids$/, "") - .sub(/([^s])$/, "\\1s") - .to_sym - else - name - end - - kwargs[:as] ||= inferred_arg_name - end - arg_defn = self.argument_class.new(*args, **kwargs, &block) - add_argument(arg_defn) - arg_defn - end - - # Register this argument with the class. - # @param arg_defn [GraphQL::Schema::Argument] - # @return [GraphQL::Schema::Argument] - def add_argument(arg_defn) - @own_arguments ||= {} - prev_defn = @own_arguments[arg_defn.name] - case prev_defn - when nil - @own_arguments[arg_defn.name] = arg_defn - when Array - prev_defn << arg_defn - when GraphQL::Schema::Argument - @own_arguments[arg_defn.name] = [prev_defn, arg_defn] - else - raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}" - end - arg_defn - end - - def remove_argument(arg_defn) - prev_defn = @own_arguments[arg_defn.name] - case prev_defn - when nil - # done - when Array - prev_defn.delete(arg_defn) - when GraphQL::Schema::Argument - @own_arguments.delete(arg_defn.name) - else - raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}" - end - nil - end - - # @return [Hash GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions - def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil) - if !own_arguments.empty? - own_arguments_that_apply = {} - own_arguments.each do |name, args_entry| - if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context)) - own_arguments_that_apply[visible_defn.graphql_name] = visible_defn - end - end - end - # might be nil if there are actually no arguments - own_arguments_that_apply || own_arguments - end - - def any_arguments? - !own_arguments.empty? - end - - module ClassConfigured - def inherited(child_class) - super - child_class.extend(InheritedArguments) - end - - module InheritedArguments - def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true) - own_arguments = super(context, require_defined_arguments) - inherited_arguments = superclass.arguments(context, false) - - if !own_arguments.empty? - if !inherited_arguments.empty? - # Local definitions override inherited ones - inherited_arguments.merge(own_arguments) - else - own_arguments - end - else - inherited_arguments - end - end - - def any_arguments? - super || superclass.any_arguments? - end - - def all_argument_definitions - all_defns = {} - ancestors.reverse_each do |ancestor| - if ancestor.respond_to?(:own_arguments) - all_defns.merge!(ancestor.own_arguments) - end - end - all_defns = all_defns.values - all_defns.flatten! - all_defns - end - - - def get_argument(argument_name, context = GraphQL::Query::NullContext.instance) - warden = Warden.from_context(context) - skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile) - for ancestor in ancestors - if ancestor.respond_to?(:own_arguments) && - (a = ancestor.own_arguments[argument_name]) && - (skip_visible || (a = Warden.visible_entry?(:visible_argument?, a, context, warden))) - return a - end - end - nil - end - end - end - - module FieldConfigured - def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil) - own_arguments = super - if @resolver_class - inherited_arguments = @resolver_class.field_arguments(context) - if !own_arguments.empty? - if !inherited_arguments.empty? - inherited_arguments.merge(own_arguments) - else - own_arguments - end - else - inherited_arguments - end - else - own_arguments - end - end - - def any_arguments? - super || (@resolver_class && @resolver_class.any_field_arguments?) - end - - def all_argument_definitions - if @resolver_class - all_defns = {} - @resolver_class.all_field_argument_definitions.each do |arg_defn| - key = arg_defn.graphql_name - case (current_value = all_defns[key]) - when nil - all_defns[key] = arg_defn - when Array - current_value << arg_defn - when GraphQL::Schema::Argument - all_defns[key] = [current_value, arg_defn] - else - raise "Invariant: Unexpected argument definition, #{current_value.class}: #{current_value.inspect}" - end - end - all_defns.merge!(own_arguments) - all_defns = all_defns.values - all_defns.flatten! - all_defns - else - super - end - end - end - - def all_argument_definitions - if !own_arguments.empty? - all_defns = own_arguments.values - all_defns.flatten! - all_defns - else - EmptyObjects::EMPTY_ARRAY - end - end - - # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name. - def get_argument(argument_name, context = GraphQL::Query::NullContext.instance) - warden = Warden.from_context(context) - if (arg_config = own_arguments[argument_name]) && ((context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)) || (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))) - visible_arg || arg_config - elsif defined?(@resolver_class) && @resolver_class - @resolver_class.get_field_argument(argument_name, context) - else - nil - end - end - - # @param new_arg_class [Class] A class to use for building argument definitions - def argument_class(new_arg_class = nil) - self.class.argument_class(new_arg_class) - end - - # @api private - # If given a block, it will eventually yield the loaded args to the block. - # - # If no block is given, it will immediately dataload (but might return a Lazy). - # - # @param values [Hash] - # @param context [GraphQL::Query::Context] - # @yield [Interpreter::Arguments, Execution::Lazy] - # @return [Interpreter::Arguments, Execution::Lazy] - def coerce_arguments(parent_object, values, context, &block) - # Cache this hash to avoid re-merging it - arg_defns = context.types.arguments(self) - total_args_count = arg_defns.size - - finished_args = nil - prepare_finished_args = -> { - if total_args_count == 0 - finished_args = GraphQL::Execution::Interpreter::Arguments::EMPTY - if block_given? - block.call(finished_args) - end - else - argument_values = {} - resolved_args_count = 0 - raised_error = false - arg_defns.each do |arg_defn| - context.dataloader.append_job do - begin - arg_defn.coerce_into_values(parent_object, values, context, argument_values) - rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err - raised_error = true - finished_args = err - if block_given? - block.call(finished_args) - end - end - - resolved_args_count += 1 - if resolved_args_count == total_args_count && !raised_error - finished_args = context.schema.after_any_lazies(argument_values.values) { - GraphQL::Execution::Interpreter::Arguments.new( - argument_values: argument_values, - ) - } - if block_given? - block.call(finished_args) - end - end - end - end - end - } - - if block_given? - prepare_finished_args.call - nil - else - # This API returns eagerly, gotta run it now - context.dataloader.run_isolated(&prepare_finished_args) - finished_args - end - end - - # Usually, this is validated statically by RequiredArgumentsArePresent, - # but not for directives. - # TODO apply static validations on schema definitions? - def validate_directive_argument(arg_defn, value) - # this is only implemented on directives. - nil - end - - module HasDirectiveArguments - def validate_directive_argument(arg_defn, value) - if value.nil? && arg_defn.type.non_null? - raise ArgumentError, "#{arg_defn.path} is required, but no value was given" - end - end - end - - def arguments_statically_coercible? - if defined?(@arguments_statically_coercible) && !@arguments_statically_coercible.nil? - @arguments_statically_coercible - else - @arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?) - end - end - - module ArgumentClassAccessor - def argument_class(new_arg_class = nil) - if new_arg_class - @argument_class = new_arg_class - elsif defined?(@argument_class) && @argument_class - @argument_class - else - superclass.respond_to?(:argument_class) ? superclass.argument_class : GraphQL::Schema::Argument - end - end - end - - module ArgumentObjectLoader - # Look up the corresponding object for a provided ID. - # By default, it uses Relay-style {Schema.object_from_id}, - # override this to find objects another way. - # - # @param type [Class, Module] A GraphQL type definition - # @param id [String] A client-provided to look up - # @param context [GraphQL::Query::Context] the current context - def object_from_id(type, id, context) - context.schema.object_from_id(id, context) - end - - def load_application_object(argument, id, context) - # See if any object can be found for this ID - if id.nil? - return nil - end - object_from_id(argument.loads, id, context) - end - - def load_and_authorize_application_object(argument, id, context) - loaded_application_object = load_application_object(argument, id, context) - authorize_application_object(argument, id, context, loaded_application_object) - end - - def authorize_application_object(argument, id, context, loaded_application_object) - context.query.after_lazy(loaded_application_object) do |application_object| - if application_object.nil? - err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object) - application_object = load_application_object_failed(err) - end - # Double-check that the located object is actually of this type - # (Don't want to allow arbitrary access to objects this way) - if application_object.nil? - nil - else - arg_loads_type = argument.loads - maybe_lazy_resolve_type = context.schema.resolve_type(arg_loads_type, application_object, context) - context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result| - if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2 - application_object_type, application_object = resolve_type_result - else - application_object_type = resolve_type_result - # application_object is already assigned - end - - passes_possible_types_check = if context.types.loadable?(arg_loads_type, context) - if arg_loads_type.kind.union? - # This union is used in `loads:` but not otherwise visible to this query - context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type) - else - true - end - else - context.types.possible_types(arg_loads_type).include?(application_object_type) - end - if !passes_possible_types_check - err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object) - application_object = load_application_object_failed(err) - end - - if application_object.nil? - nil - else - # This object was loaded successfully - # and resolved to the right type, - # now apply the `.authorized?` class method if there is one - context.query.after_lazy(application_object_type.authorized?(application_object, context)) do |authed| - if authed - application_object - else - err = GraphQL::UnauthorizedError.new( - object: application_object, - type: application_object_type, - context: context, - ) - if self.respond_to?(:unauthorized_object) - err.set_backtrace(caller) - unauthorized_object(err) - else - raise err - end - end - end - end - end - end - end - end - - def load_application_object_failed(err) - raise err - end - end - - NO_ARGUMENTS = GraphQL::EmptyObjects::EMPTY_HASH - def own_arguments - @own_arguments || NO_ARGUMENTS - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_ast_node.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_ast_node.rb deleted file mode 100644 index 2457a6a7281..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_ast_node.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Member - module HasAstNode - def self.extended(child_cls) - super - child_cls.ast_node = nil - end - - def inherited(child_cls) - super - child_cls.ast_node = nil - end - - # If this schema was parsed from a `.graphql` file (or other SDL), - # this is the AST node that defined this part of the schema. - def ast_node(new_ast_node = nil) - if new_ast_node - @ast_node = new_ast_node - elsif defined?(@ast_node) - @ast_node - else - nil - end - end - - attr_writer :ast_node - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_dataloader.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_dataloader.rb deleted file mode 100644 index b5736d8cea3..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_dataloader.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module HasDataloader - # @return [GraphQL::Dataloader] The dataloader for the currently-running query - def dataloader - context.dataloader - end - - # A shortcut method for loading a key from a source. - # Identical to `dataloader.with(source_class, *source_args).load(load_key)` - # @param source_class [Class] - # @param source_args [Array] Any extra parameters defined in `source_class`'s `initialize` method - # @param load_key [Object] The key to look up using `def fetch` - def dataload(source_class, *source_args, load_key) - dataloader.with(source_class, *source_args).load(load_key) - end - - # Find an object with ActiveRecord via {Dataloader::ActiveRecordSource}. - # @param model [Class] - # @param find_by_value [Object] Usually an `id`, might be another value if `find_by:` is also provided - # @param find_by [Symbol, String] A column name to look the record up by. (Defaults to the model's primary key.) - # @return [ActiveRecord::Base, nil] - # @example Finding a record by ID - # dataload_record(Post, 5) # Like `Post.find(5)`, but dataloaded - # @example Finding a record by another attribute - # dataload_record(User, "matz", find_by: :handle) # Like `User.find_by(handle: "matz")`, but dataloaded - def dataload_record(model, find_by_value, find_by: nil) - source = if find_by - dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by) - else - dataloader.with(Dataloader::ActiveRecordSource, model) - end - - source.load(find_by_value) - end - - # Look up an associated record using a Rails association. - # @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.) - # @param record [ActiveRecord::Base] The object that the association belongs to. - # @param scope [ActiveRecord::Relation] A scope to look up the associated record in - # @return [ActiveRecord::Base, nil] The associated record, if there is one - # @example Looking up a belongs_to on the current object - # dataload_association(:parent) # Equivalent to `object.parent`, but dataloaded - # @example Looking up an associated record on some other object - # dataload_association(:post, comment) # Equivalent to `comment.post`, but dataloaded - def dataload_association(record = object, association_name, scope: nil) - source = if scope - dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope) - else - dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name) - end - source.load(record) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_deprecation_reason.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_deprecation_reason.rb deleted file mode 100644 index 64b9f0243d4..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_deprecation_reason.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module HasDeprecationReason - # @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection) - attr_reader :deprecation_reason - - # Set the deprecation reason for this member, or remove it by assigning `nil` - # @param text [String, nil] - def deprecation_reason=(text) - @deprecation_reason = text - if text.nil? - remove_directive(GraphQL::Schema::Directive::Deprecated) - else - # This removes a previously-attached directive, if there is one: - directive(GraphQL::Schema::Directive::Deprecated, reason: text) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_directives.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_directives.rb deleted file mode 100644 index 651c5cdfb7f..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_directives.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module HasDirectives - def self.extended(child_cls) - super - child_cls.module_exec { self.own_directives = nil } - end - - def inherited(child_cls) - super - child_cls.own_directives = nil - end - - # Create an instance of `dir_class` for `self`, using `options`. - # - # It removes a previously-attached instance of `dir_class`, if there is one. - # - # @return [void] - def directive(dir_class, **options) - @own_directives ||= [] - HasDirectives.add_directive(self, @own_directives, dir_class, options) - nil - end - - # Remove an attached instance of `dir_class`, if there is one - # @param dir_class [Class] - # @return [viod] - def remove_directive(dir_class) - HasDirectives.remove_directive(@own_directives, dir_class) - nil - end - - def directives - HasDirectives.get_directives(self, @own_directives, :directives) - end - - class << self - def add_directive(schema_member, directives, directive_class, directive_options) - remove_directive(directives, directive_class) unless directive_class.repeatable? - directives << directive_class.new(schema_member, **directive_options) - end - - def remove_directive(directives, directive_class) - directives && directives.reject! { |d| d.is_a?(directive_class) } - end - - def get_directives(schema_member, directives, directives_method) - case schema_member - when Class - inherited_directives = if schema_member.superclass.respond_to?(directives_method) - get_directives(schema_member.superclass, schema_member.superclass.public_send(directives_method), directives_method) - else - GraphQL::EmptyObjects::EMPTY_ARRAY - end - if !inherited_directives.empty? && directives - dirs = [] - merge_directives(dirs, inherited_directives) - merge_directives(dirs, directives) - dirs - elsif directives - directives - elsif !inherited_directives.empty? - inherited_directives - else - GraphQL::EmptyObjects::EMPTY_ARRAY - end - when Module - dirs = nil - schema_member.ancestors.reverse_each do |ancestor| - if ancestor.respond_to?(:own_directives) && - !(anc_dirs = ancestor.own_directives).empty? - dirs ||= [] - merge_directives(dirs, anc_dirs) - end - end - if directives - dirs ||= [] - merge_directives(dirs, directives) - end - dirs || GraphQL::EmptyObjects::EMPTY_ARRAY - when HasDirectives - directives || GraphQL::EmptyObjects::EMPTY_ARRAY - else - raise "Invariant: how could #{schema_member} not be a Class, Module, or instance of HasDirectives?" - end - end - - private - - # Modify `target` by adding items from `dirs` such that: - # - Any name conflict is overridden by the incoming member of `dirs` - # - Any other member of `dirs` is appended - # @param target [Array] - # @param dirs [Array] - # @return [void] - def merge_directives(target, dirs) - dirs.each do |dir| - if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name }) - target.slice!(idx) - target.insert(idx, dir) - else - target << dir - end - end - nil - end - end - - protected - - attr_accessor :own_directives - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_fields.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_fields.rb deleted file mode 100644 index 8d289181b8e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_fields.rb +++ /dev/null @@ -1,243 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - # Shared code for Objects, Interfaces, Mutations, Subscriptions - module HasFields - # Add a field to this object or interface with the given definition - # @see {GraphQL::Schema::Field#initialize} for method signature - # @return [GraphQL::Schema::Field] - def field(*args, **kwargs, &block) - field_defn = field_class.from_options(*args, owner: self, **kwargs, &block) - add_field(field_defn) - field_defn - end - - # A list of Ruby keywords. - # - # @api private - RUBY_KEYWORDS = [:class, :module, :def, :undef, :begin, :rescue, :ensure, :end, :if, :unless, :then, :elsif, :else, :case, :when, :while, :until, :for, :break, :next, :redo, :retry, :in, :do, :return, :yield, :super, :self, :nil, :true, :false, :and, :or, :not, :alias, :defined?, :BEGIN, :END, :__LINE__, :__FILE__] - - # A list of GraphQL-Ruby keywords. - # - # @api private - GRAPHQL_RUBY_KEYWORDS = [:context, :object, :raw_value] - - # A list of field names that we should advise users to pick a different - # resolve method name. - # - # @api private - CONFLICT_FIELD_NAMES = Set.new(GRAPHQL_RUBY_KEYWORDS + RUBY_KEYWORDS + Object.instance_methods) - - # Register this field with the class, overriding a previous one if needed. - # @param field_defn [GraphQL::Schema::Field] - # @return [void] - def add_field(field_defn, method_conflict_warning: field_defn.method_conflict_warning?) - # Check that `field_defn.original_name` equals `resolver_method` and `method_sym` -- - # that shows that no override value was given manually. - if method_conflict_warning && - CONFLICT_FIELD_NAMES.include?(field_defn.resolver_method) && - field_defn.original_name == field_defn.resolver_method && - field_defn.original_name == field_defn.method_sym && - field_defn.hash_key == NOT_CONFIGURED && - field_defn.dig_keys.nil? - warn(conflict_field_name_warning(field_defn)) - end - prev_defn = own_fields[field_defn.name] - - case prev_defn - when nil - own_fields[field_defn.name] = field_defn - when Array - prev_defn << field_defn - when GraphQL::Schema::Field - own_fields[field_defn.name] = [prev_defn, field_defn] - else - raise "Invariant: unexpected previous field definition for #{field_defn.name.inspect}: #{prev_defn.inspect}" - end - - nil - end - - # @return [Class] The class to initialize when adding fields to this kind of schema member - def field_class(new_field_class = nil) - if new_field_class - @field_class = new_field_class - elsif defined?(@field_class) && @field_class - @field_class - else - find_inherited_value(:field_class, GraphQL::Schema::Field) - end - end - - def global_id_field(field_name, **kwargs) - type = self - field field_name, "ID", **kwargs, null: false - define_method(field_name) do - context.schema.id_from_object(object, type, context) - end - end - - # @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields. - # @return [void] - def has_no_fields(new_has_no_fields) - @has_no_fields = new_has_no_fields - nil - end - - # @return [Boolean] `true` if `has_no_fields(true)` was configued - def has_no_fields? - @has_no_fields - end - - # @return [Hash GraphQL::Schema::Field, Array>] Fields defined on this class _specifically_, not parent classes - def own_fields - @own_fields ||= {} - end - - def all_field_definitions - all_fields = {} - ancestors.reverse_each do |ancestor| - if ancestor.respond_to?(:own_fields) - all_fields.merge!(ancestor.own_fields) - end - end - all_fields = all_fields.values - all_fields.flatten! - all_fields - end - - module InterfaceMethods - def get_field(field_name, context = GraphQL::Query::NullContext.instance) - warden = Warden.from_context(context) - skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile) - for ancestor in ancestors - if ancestor.respond_to?(:own_fields) && - (f_entry = ancestor.own_fields[field_name]) && - (skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden))) - return f_entry - end - end - nil - end - - # @return [Hash GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields - def fields(context = GraphQL::Query::NullContext.instance) - warden = Warden.from_context(context) - # Local overrides take precedence over inherited fields - visible_fields = {} - for ancestor in ancestors - if ancestor.respond_to?(:own_fields) - ancestor.own_fields.each do |field_name, fields_entry| - # Choose the most local definition that passes `.visible?` -- - # stop checking for fields by name once one has been found. - if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden)) - visible_fields[field_name] = f.ensure_loaded - end - end - end - end - visible_fields - end - end - - module ObjectMethods - def get_field(field_name, context = GraphQL::Query::NullContext.instance) - # Objects need to check that the interface implementation is visible, too - warden = Warden.from_context(context) - ancs = ancestors - skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile) - i = 0 - while (ancestor = ancs[i]) - if ancestor.respond_to?(:own_fields) && - visible_interface_implementation?(ancestor, context, warden) && - (f_entry = ancestor.own_fields[field_name]) && - (skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden))) - return (skip_visible ? f_entry : f_entry.ensure_loaded) - end - i += 1 - end - nil - end - - # @return [Hash GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields - def fields(context = GraphQL::Query::NullContext.instance) - # Objects need to check that the interface implementation is visible, too - warden = Warden.from_context(context) - # Local overrides take precedence over inherited fields - visible_fields = {} - had_any_fields_at_all = false - for ancestor in ancestors - if ancestor.respond_to?(:own_fields) && visible_interface_implementation?(ancestor, context, warden) - ancestor.own_fields.each do |field_name, fields_entry| - had_any_fields_at_all = true - # Choose the most local definition that passes `.visible?` -- - # stop checking for fields by name once one has been found. - if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden)) - visible_fields[field_name] = f.ensure_loaded - end - end - end - end - if !had_any_fields_at_all && !has_no_fields? - warn(GraphQL::Schema::Object::FieldsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.") - end - visible_fields - end - end - - def self.included(child_class) - # Included in an interface definition methods module - child_class.include(InterfaceMethods) - super - end - - def self.extended(child_class) - child_class.extend(ObjectMethods) - super - end - - private - - def inherited(subclass) - super - subclass.class_exec do - @own_fields ||= nil - @field_class ||= nil - @has_no_fields ||= false - end - end - - # If `type` is an interface, and `self` has a type membership for `type`, then make sure it's visible. - def visible_interface_implementation?(type, context, warden) - if type.respond_to?(:kind) && type.kind.interface? - implements_this_interface = false - implementation_is_visible = false - warden.interface_type_memberships(self, context).each do |tm| - if tm.abstract_type == type - implements_this_interface ||= true - if warden.visible_type_membership?(tm, context) - implementation_is_visible = true - break - end - end - end - # It's possible this interface came by way of `include` in another interface which this - # object type _does_ implement, and that's ok - implements_this_interface ? implementation_is_visible : true - else - # If there's no implementation, then we're looking at Ruby-style inheritance instead - true - end - end - - # @param [GraphQL::Schema::Field] - # @return [String] A warning to give when this field definition might conflict with a built-in method - def conflict_field_name_warning(field_defn) - "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.resolver_method}` and `def resolve_#{field_defn.resolver_method}`). Or use `method_conflict_warning: false` to suppress this warning." - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_interfaces.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_interfaces.rb deleted file mode 100644 index fb15bc1de7b..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_interfaces.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module HasInterfaces - def implements(*new_interfaces, **options) - new_memberships = [] - new_interfaces.each do |int| - if int.is_a?(Module) - unless int.include?(GraphQL::Schema::Interface) - raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules." - end - - new_memberships << int.type_membership_class.new(int, self, **options) - - # Include the methods here, - # `.fields` will use the inheritance chain - # to find inherited fields - include(int) - - # If this interface has interfaces of its own, add those, too - int.interfaces.each do |next_interface| - implements(next_interface) - end - elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType) - if !options.empty? - raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature." - end - new_memberships << int - else - raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})" - end - end - - # Remove any String or late-bound interfaces which are being replaced - own_interface_type_memberships.reject! { |old_i_m| - if !(old_i_m.respond_to?(:abstract_type) && old_i_m.abstract_type.is_a?(Module)) - old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m - old_name = Schema::Member::BuildType.to_type_name(old_int_type) - - new_memberships.any? { |new_i_m| - new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m - new_name = Schema::Member::BuildType.to_type_name(new_int_type) - - new_name == old_name - } - end - } - own_interface_type_memberships.concat(new_memberships) - end - - def own_interface_type_memberships - @own_interface_type_memberships ||= [] - end - - def interface_type_memberships - own_interface_type_memberships - end - - module ClassConfigured - # This combination of extended -> inherited -> extended - # means that the base class (`Schema::Object`) *won't* - # have the superclass-related code in `InheritedInterfaces`, - # but child classes of `Schema::Object` will have it. - # That way, we don't need a `superclass.respond_to?(...)` check. - def inherited(child_class) - super - child_class.extend(InheritedInterfaces) - end - - module InheritedInterfaces - def interfaces(context = GraphQL::Query::NullContext.instance) - visible_interfaces = super - inherited_interfaces = superclass.interfaces(context) - if !visible_interfaces.empty? - if !inherited_interfaces.empty? - visible_interfaces.concat(inherited_interfaces) - visible_interfaces.uniq! - end - visible_interfaces - elsif !inherited_interfaces.empty? - inherited_interfaces - else - EmptyObjects::EMPTY_ARRAY - end - end - - def interface_type_memberships - own_tms = super - inherited_tms = superclass.interface_type_memberships - if inherited_tms.size > 0 - own_tms + inherited_tms - else - own_tms - end - end - end - end - - # param context [Query::Context] If omitted, skip filtering. - def interfaces(context = GraphQL::Query::NullContext.instance) - warden = Warden.from_context(context) - visible_interfaces = nil - own_interface_type_memberships.each do |type_membership| - case type_membership - when Schema::TypeMembership - if warden.visible_type_membership?(type_membership, context) - visible_interfaces ||= [] - visible_interfaces << type_membership.abstract_type - end - when String, Schema::LateBoundType - # During initialization, `type_memberships` can hold late-bound types - visible_interfaces ||= [] - visible_interfaces << type_membership - else - raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}" - end - end - if visible_interfaces - visible_interfaces.uniq! - visible_interfaces - else - EmptyObjects::EMPTY_ARRAY - end - end - - private - - def self.extended(child_class) - child_class.extend(ClassConfigured) - end - - def inherited(subclass) - super - subclass.class_exec do - @own_interface_type_memberships ||= nil - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_path.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_path.rb deleted file mode 100644 index a004023b093..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_path.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module HasPath - # @return [String] A description of this member's place in the GraphQL schema - def path - path_str = if self.respond_to?(:graphql_name) - self.graphql_name - elsif self.class.respond_to?(:graphql_name) - # Instances of resolvers - self.class.graphql_name - end - - if self.respond_to?(:owner) && owner.respond_to?(:path) - path_str = "#{owner.path}.#{path_str}" - end - - path_str - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_unresolved_type_error.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_unresolved_type_error.rb deleted file mode 100644 index a812049480d..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_unresolved_type_error.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - # Set up a type-specific error to make debugging & bug tracker integration better - module HasUnresolvedTypeError - private - def add_unresolved_type_error(child_class) - if child_class.name # Don't set this for anonymous classes - child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError)) - else - child_class.const_set(:UnresolvedTypeError, UnresolvedTypeError) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/has_validators.rb b/vendor/gems/graphql/lib/graphql/schema/member/has_validators.rb deleted file mode 100644 index 7e877f93259..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/has_validators.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Member - module HasValidators - include GraphQL::EmptyObjects - - # Build {GraphQL::Schema::Validator}s based on the given configuration - # and use them for this schema member - # @param validation_config [Hash{Symbol => Hash}] - # @return [void] - def validates(validation_config) - new_validators = GraphQL::Schema::Validator.from_config(self, validation_config) - @own_validators ||= [] - @own_validators.concat(new_validators) - nil - end - - # @return [Array] - def validators - @own_validators || EMPTY_ARRAY - end - - module ClassConfigured - def inherited(child_cls) - super - child_cls.extend(ClassValidators) - end - - module ClassValidators - include GraphQL::EmptyObjects - - def validators - inherited_validators = superclass.validators - if !inherited_validators.empty? - if @own_validators.nil? - inherited_validators - else - inherited_validators + @own_validators - end - elsif @own_validators.nil? - EMPTY_ARRAY - else - @own_validators - end - end - end - end - - def self.extended(child_cls) - super - child_cls.extend(ClassConfigured) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/relay_shortcuts.rb b/vendor/gems/graphql/lib/graphql/schema/member/relay_shortcuts.rb deleted file mode 100644 index a4dfccbd609..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/relay_shortcuts.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module RelayShortcuts - def edge_type_class(new_edge_type_class = nil) - if new_edge_type_class - initialize_relay_metadata - @edge_type_class = new_edge_type_class - else - # Don't call `ancestor.edge_type_class` - # because we don't want a fallback from any ancestors -- - # only apply the fallback if _no_ ancestor has a configured value! - for ancestor in self.ancestors - if ancestor.respond_to?(:configured_edge_type_class, true) && (etc = ancestor.configured_edge_type_class) - return etc - end - end - Types::Relay::BaseEdge - end - end - - def connection_type_class(new_connection_type_class = nil) - if new_connection_type_class - initialize_relay_metadata - @connection_type_class = new_connection_type_class - else - # Don't call `ancestor.connection_type_class` - # because we don't want a fallback from any ancestors -- - # only apply the fallback if _no_ ancestor has a configured value! - for ancestor in self.ancestors - if ancestor.respond_to?(:configured_connection_type_class, true) && (ctc = ancestor.configured_connection_type_class) - return ctc - end - end - Types::Relay::BaseConnection - end - end - - def edge_type - initialize_relay_metadata - @edge_type ||= begin - edge_name = self.graphql_name + "Edge" - node_type_class = self - Class.new(edge_type_class) do - graphql_name(edge_name) - node_type(node_type_class) - end - end - end - - def connection_type - initialize_relay_metadata - @connection_type ||= begin - conn_name = self.graphql_name + "Connection" - edge_type_class = self.edge_type - Class.new(connection_type_class) do - graphql_name(conn_name) - edge_type(edge_type_class) - end - end - end - - protected - - def configured_connection_type_class - @connection_type_class - end - - def configured_edge_type_class - @edge_type_class - end - - attr_writer :edge_type, :connection_type, :connection_type_class, :edge_type_class - - private - - # If one of these values is accessed, initialize all the instance variables to retain - # a consistent object shape. - def initialize_relay_metadata - if !defined?(@connection_type) - @connection_type = nil - @edge_type = nil - @connection_type_class = nil - @edge_type_class = nil - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/scoped.rb b/vendor/gems/graphql/lib/graphql/schema/member/scoped.rb deleted file mode 100644 index 1e676cdbe02..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/scoped.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module Scoped - # This is called when a field has `scope: true`. - # The field's return type class receives this call. - # - # By default, it's a no-op. Override it to scope your objects. - # - # @param items [Object] Some list-like object (eg, Array, ActiveRecord::Relation) - # @param context [GraphQL::Query::Context] - # @return [Object] Another list-like object, scoped to the current context - def scope_items(items, context) - items - end - - def reauthorize_scoped_objects(new_value = nil) - if new_value.nil? - if @reauthorize_scoped_objects != nil - @reauthorize_scoped_objects - else - find_inherited_value(:reauthorize_scoped_objects, true) - end - else - @reauthorize_scoped_objects = new_value - end - end - - def inherited(subclass) - super - subclass.class_exec do - @reauthorize_scoped_objects = nil - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/type_system_helpers.rb b/vendor/gems/graphql/lib/graphql/schema/member/type_system_helpers.rb deleted file mode 100644 index 8203f8043f1..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/type_system_helpers.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module TypeSystemHelpers - def initialize(...) - super - @to_non_null_type ||= nil - @to_list_type ||= nil - end - - # @return [Schema::NonNull] Make a non-null-type representation of this type - def to_non_null_type - @to_non_null_type ||= GraphQL::Schema::NonNull.new(self) - end - - # @return [Schema::List] Make a list-type representation of this type - def to_list_type - @to_list_type ||= GraphQL::Schema::List.new(self) - end - - # @return [Boolean] true if this is a non-nullable type. A nullable list of non-nullables is considered nullable. - def non_null? - false - end - - # @return [Boolean] true if this is a list type. A non-nullable list is considered a list. - def list? - false - end - - def to_type_signature - graphql_name - end - - # @return [GraphQL::TypeKinds::TypeKind] - def kind - raise GraphQL::RequiredImplementationMissingError, "No `.kind` defined for #{self}" - end - - private - - def inherited(subclass) - subclass.class_exec do - @to_non_null_type ||= nil - @to_list_type ||= nil - end - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/member/validates_input.rb b/vendor/gems/graphql/lib/graphql/schema/member/validates_input.rb deleted file mode 100644 index 80113863fe5..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/member/validates_input.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Member - module ValidatesInput - def valid_input?(val, ctx) - validate_input(val, ctx).valid? - end - - def validate_input(val, ctx, max_errors: nil) - if val.nil? - Query::InputValidationResult::VALID - else - validate_non_null_input(val, ctx, max_errors: max_errors) || Query::InputValidationResult::VALID - end - end - - def valid_isolated_input?(v) - valid_input?(v, GraphQL::Query::NullContext.instance) - end - - def coerce_isolated_input(v) - coerce_input(v, GraphQL::Query::NullContext.instance) - end - - def coerce_isolated_result(v) - coerce_result(v, GraphQL::Query::NullContext.instance) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/mutation.rb b/vendor/gems/graphql/lib/graphql/schema/mutation.rb deleted file mode 100644 index b0ba8f3f5ef..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/mutation.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # This base class accepts configuration for a mutation root field, - # then it can be hooked up to your mutation root object type. - # - # If you want to customize how this class generates types, in your base class, - # override the various `generate_*` methods. - # - # @see {GraphQL::Schema::RelayClassicMutation} for an extension of this class with some conventions built-in. - # - # @example Creating a comment - # # Define the mutation: - # class Mutations::CreateComment < GraphQL::Schema::Mutation - # argument :body, String, required: true - # argument :post_id, ID, required: true - # - # field :comment, Types::Comment, null: true - # field :errors, [String], null: false - # - # def resolve(body:, post_id:) - # post = Post.find(post_id) - # comment = post.comments.build(body: body, author: context[:current_user]) - # if comment.save - # # Successful creation, return the created object with no errors - # { - # comment: comment, - # errors: [], - # } - # else - # # Failed save, return the errors to the client - # { - # comment: nil, - # errors: comment.errors.full_messages - # } - # end - # end - # end - # - # # Hook it up to your mutation: - # class Types::Mutation < GraphQL::Schema::Object - # field :create_comment, mutation: Mutations::CreateComment - # end - # - # # Call it from GraphQL: - # result = MySchema.execute <<-GRAPHQL - # mutation { - # createComment(postId: "1", body: "Nice Post!") { - # errors - # comment { - # body - # author { - # login - # } - # } - # } - # } - # GRAPHQL - # - class Mutation < GraphQL::Schema::Resolver - extend GraphQL::Schema::Member::HasFields - extend GraphQL::Schema::Resolver::HasPayloadType - - # @api private - def call_resolve(_args_hash) - # Clear any cached values from `loads` or authorization: - dataloader.clear_cache - super - end - - class << self - def visible?(context) - true - end - - private - - def conflict_field_name_warning(field_defn) - "#{self.graphql_name}'s `field :#{field_defn.name}` conflicts with a built-in method, use `hash_key:` or `method:` to pick a different resolve behavior for this field (for example, `hash_key: :#{field_defn.resolver_method}_value`, and modify the return hash). Or use `method_conflict_warning: false` to suppress this warning." - end - - # Override this to attach self as `mutation` - def generate_payload_type - payload_class = super - payload_class.mutation(self) - payload_class - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/non_null.rb b/vendor/gems/graphql/lib/graphql/schema/non_null.rb deleted file mode 100644 index 455a065ae48..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/non_null.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # Represents a non null type in the schema. - # Wraps a {Schema::Member} when it is required. - # @see {Schema::Member::TypeSystemHelpers#to_non_null_type} - class NonNull < GraphQL::Schema::Wrapper - include Schema::Member::ValidatesInput - - # @return [GraphQL::TypeKinds::NON_NULL] - def kind - GraphQL::TypeKinds::NON_NULL - end - - # @return [true] - def non_null? - true - end - - # @return [Boolean] True if this type wraps a list type - def list? - @of_type.list? - end - - def to_type_signature - "#{@of_type.to_type_signature}!" - end - - def inspect - "#<#{self.class.name} @of_type=#{@of_type.inspect}>" - end - - def validate_input(value, ctx, max_errors: nil) - if value.nil? - result = GraphQL::Query::InputValidationResult.new - result.add_problem("Expected value to not be null") - result - else - of_type.validate_input(value, ctx, max_errors: max_errors) - end - end - - # This is for introspection, where it's expected the name will be `null` - def graphql_name - nil - end - - def coerce_input(value, ctx) - # `.validate_input` above is used for variables, but this method is used for arguments - if value.nil? - raise GraphQL::ExecutionError, "`null` is not a valid input for `#{to_type_signature}`, please provide a value for this argument." - end - of_type.coerce_input(value, ctx) - end - - def coerce_result(value, ctx) - of_type.coerce_result(value, ctx) - end - - # This is for implementing introspection - def description - nil - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/object.rb b/vendor/gems/graphql/lib/graphql/schema/object.rb deleted file mode 100644 index e1f5a99f095..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/object.rb +++ /dev/null @@ -1,145 +0,0 @@ -# frozen_string_literal: true - -require "graphql/query/null_context" - -module GraphQL - class Schema - class Object < GraphQL::Schema::Member - extend GraphQL::Schema::Member::HasFields - extend GraphQL::Schema::Member::HasInterfaces - include Member::HasDataloader - - # Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement - class FieldsAreRequiredError < GraphQL::Error - def initialize(object_type) - message = "Object types must have fields, but #{object_type.graphql_name} doesn't have any. Define a field for this type, remove it from your schema, or add `has_no_fields(true)` to its definition." - super(message) - end - end - - # @return [Object] the application object this type is wrapping - attr_reader :object - - # @return [GraphQL::Query::Context] the context instance for this query - attr_reader :context - - # @return [GraphQL::Dataloader] - def dataloader - context.dataloader - end - - # Call this in a field method to return a value that should be returned to the client - # without any further handling by GraphQL. - def raw_value(obj) - GraphQL::Execution::Interpreter::RawValue.new(obj) - end - - class << self - # This is protected so that we can be sure callers use the public method, {.authorized_new} - # @see authorized_new to make instances - protected :new - - def wrap_scoped(object, context) - scoped_new(object, context) - end - - # This is called by the runtime to return an object to call methods on. - def wrap(object, context) - authorized_new(object, context) - end - - # Make a new instance of this type _if_ the auth check passes, - # otherwise, raise an error. - # - # Probably only the framework should call this method. - # - # This might return a {GraphQL::Execution::Lazy} if the user-provided `.authorized?` - # hook returns some lazy value (like a Promise). - # - # The reason that the auth check is in this wrapper method instead of {.new} is because - # of how it might return a Promise. It would be weird if `.new` returned a promise; - # It would be a headache to try to maintain Promise-y state inside a {Schema::Object} - # instance. So, hopefully this wrapper method will do the job. - # - # @param object [Object] The thing wrapped by this object - # @param context [GraphQL::Query::Context] - # @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy] - # @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false` - def authorized_new(object, context) - context.query.current_trace.begin_authorized(self, object, context) - begin - maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do - begin - authorized?(object, context) - rescue GraphQL::UnauthorizedError => err - context.schema.unauthorized_object(err) - rescue StandardError => err - context.query.handle_or_reraise(err) - end - end - ensure - context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val) - end - - auth_val = if context.schema.lazy?(maybe_lazy_auth_val) - GraphQL::Execution::Lazy.new do - context.query.current_trace.begin_authorized(self, object, context) - context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do - res = context.schema.sync_lazy(maybe_lazy_auth_val) - context.query.current_trace.end_authorized(self, object, context, res) - res - end - end - else - maybe_lazy_auth_val - end - - context.query.after_lazy(auth_val) do |is_authorized| - if is_authorized - self.new(object, context) - else - # It failed the authorization check, so go to the schema's authorized object hook - err = GraphQL::UnauthorizedError.new(object: object, type: self, context: context) - # If a new value was returned, wrap that instead of the original value - begin - new_obj = context.schema.unauthorized_object(err) - if new_obj - self.new(new_obj, context) - else - nil - end - end - end - end - end - - def scoped_new(object, context) - self.new(object, context) - end - end - - def initialize(object, context) - @object = object - @context = context - end - - class << self - # Set up a type-specific invalid null error to use when this object's non-null fields wrongly return `nil`. - # It should help with debugging and bug tracker integrations. - def const_missing(name) - if name == :InvalidNullError - custom_err_class = GraphQL::InvalidNullError.subclass_for(self) - const_set(:InvalidNullError, custom_err_class) - custom_err_class - else - super - end - end - - def kind - GraphQL::TypeKinds::OBJECT - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/printer.rb b/vendor/gems/graphql/lib/graphql/schema/printer.rb deleted file mode 100644 index bfd8c53d2cb..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/printer.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - # Used to convert your {GraphQL::Schema} to a GraphQL schema string - # - # @example print your schema to standard output (via helper) - # puts GraphQL::Schema::Printer.print_schema(MySchema) - # - # @example print your schema to standard output - # puts GraphQL::Schema::Printer.new(MySchema).print_schema - # - # @example print a single type to standard output - # class Types::Query < GraphQL::Schema::Object - # description "The query root of this schema" - # - # field :post, Types::Post, null: true - # end - # - # class Types::Post < GraphQL::Schema::Object - # description "A blog post" - # - # field :id, ID, null: false - # field :title, String, null: false - # field :body, String, null: false - # end - # - # class MySchema < GraphQL::Schema - # query(Types::Query) - # end - # - # printer = GraphQL::Schema::Printer.new(MySchema) - # puts printer.print_type(Types::Post) - # - class Printer < GraphQL::Language::Printer - attr_reader :schema, :warden - - # @param schema [GraphQL::Schema] - # @param context [Hash] - # @param introspection [Boolean] Should include the introspection types in the string? - def initialize(schema, context: nil, introspection: false) - @document_from_schema = GraphQL::Language::DocumentFromSchemaDefinition.new( - schema, - context: context, - include_introspection_types: introspection, - ) - - @document = @document_from_schema.document - @schema = schema - end - - # Return the GraphQL schema string for the introspection type system - def self.print_introspection_schema - query_root = Class.new(GraphQL::Schema::Object) do - graphql_name "Root" - field :throwaway_field, String - def self.visible?(ctx) - false - end - end - schema = Class.new(GraphQL::Schema) { - use GraphQL::Schema::Visibility - query(query_root) - def self.visible?(member, _ctx) - member.graphql_name != "Root" - end - } - - introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new( - schema, - include_introspection_types: true, - include_built_in_directives: true, - ).document - - introspection_schema_ast.to_query_string(printer: IntrospectionPrinter.new) - end - - # Return a GraphQL schema string for the defined types in the schema - # @param schema [GraphQL::Schema] - # @param context [Hash] - # @param only [<#call(member, ctx)>] - # @param except [<#call(member, ctx)>] - def self.print_schema(schema, **args) - printer = new(schema, **args) - printer.print_schema - end - - # Return a GraphQL schema string for the defined types in the schema - def print_schema - print(@document) + "\n" - end - - def print_type(type) - node = @document_from_schema.build_type_definition_node(type) - print(node) - end - - class IntrospectionPrinter < GraphQL::Language::Printer - def print_schema_definition(schema) - print_string("schema {\n query: Root\n}") - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/relay_classic_mutation.rb b/vendor/gems/graphql/lib/graphql/schema/relay_classic_mutation.rb deleted file mode 100644 index dca743e0e48..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/relay_classic_mutation.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # Mutations that extend this base class get some conventions added for free: - # - # - An argument called `clientMutationId` is _always_ added, but it's not passed - # to the resolve method. The value is re-inserted to the response. (It's for - # client libraries to manage optimistic updates.) - # - The returned object type always has a field called `clientMutationId` to support that. - # - The mutation accepts one argument called `input`, `argument`s defined in the mutation - # class are added to that input object, which is generated by the mutation. - # - # These conventions were first specified by Relay Classic, but they come in handy: - # - # - `clientMutationId` supports optimistic updates and cache rollbacks on the client - # - using a single `input:` argument makes it easy to post whole JSON objects to the mutation - # using one GraphQL variable (`$input`) instead of making a separate variable for each argument. - # - # @see {GraphQL::Schema::Mutation} for an example, it's basically the same. - # - class RelayClassicMutation < GraphQL::Schema::Mutation - include GraphQL::Schema::HasSingleInputArgument - - argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false - - # The payload should always include this field - field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.") - # Relay classic default: - null(true) - - # Override {GraphQL::Schema::Resolver#resolve_with_support} to - # delete `client_mutation_id` from the kwargs. - def resolve_with_support(**inputs) - input = inputs[:input].to_kwargs - - if input - # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are. - input_kwargs = input.to_h - client_mutation_id = input_kwargs.delete(:client_mutation_id) - inputs[:input] = input_kwargs - end - - return_value = super(**inputs) - - context.query.after_lazy(return_value) do |return_hash| - # It might be an error - if return_hash.is_a?(Hash) - return_hash[:client_mutation_id] = client_mutation_id - end - return_hash - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/resolver.rb b/vendor/gems/graphql/lib/graphql/schema/resolver.rb deleted file mode 100644 index e902aceb84e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/resolver.rb +++ /dev/null @@ -1,413 +0,0 @@ -# frozen_string_literal: true -require "graphql/schema/resolver/has_payload_type" - -module GraphQL - class Schema - # A class-based container for field configuration and resolution logic. It supports: - # - # - Arguments, via `.argument(...)` helper, which will be applied to the field. - # - Return type, via `.type(..., null: ...)`, which will be applied to the field. - # - Description, via `.description(...)`, which will be applied to the field - # - Comment, via `.comment(...)`, which will be applied to the field - # - Resolution, via `#resolve(**args)` method, which will be called to resolve the field. - # - `#object` and `#context` accessors for use during `#resolve`. - # - # Resolvers can be attached with the `resolver:` option in a `field(...)` call. - # - # A resolver's configuration may be overridden with other keywords in the `field(...)` call. - # - # @see {GraphQL::Schema::Mutation} for a concrete subclass of `Resolver`. - # @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function` - class Resolver - include Schema::Member::GraphQLTypeNames - # Really we only need description & comment from here, but: - extend Schema::Member::BaseDSLMethods - extend Member::BaseDSLMethods::ConfigurationExtension - extend GraphQL::Schema::Member::HasArguments - extend GraphQL::Schema::Member::HasValidators - include Schema::Member::HasPath - extend Schema::Member::HasPath - extend Schema::Member::HasDirectives - include Schema::Member::HasDataloader - - # @param object [Object] The application object that this field is being resolved on - # @param context [GraphQL::Query::Context] - # @param field [GraphQL::Schema::Field] - def initialize(object:, context:, field:) - @object = object - @context = context - @field = field - # Since this hash is constantly rebuilt, cache it for this call - @arguments_by_keyword = {} - context.types.arguments(self.class).each do |arg| - @arguments_by_keyword[arg.keyword] = arg - end - @prepared_arguments = nil - end - - # @return [Object] The application object this field is being resolved on - attr_reader :object - - # @return [GraphQL::Query::Context] - attr_reader :context - - # @return [GraphQL::Schema::Field] - attr_reader :field - - def arguments - @prepared_arguments || raise("Arguments have not been prepared yet, still waiting for #load_arguments to resolve. (Call `.arguments` later in the code.)") - end - - # This method is _actually_ called by the runtime, - # it does some preparation and then eventually calls - # the user-defined `#resolve` method. - # @api private - def resolve_with_support(**args) - # First call the ready? hook which may raise - raw_ready_val = if !args.empty? - ready?(**args) - else - ready? - end - context.query.after_lazy(raw_ready_val) do |ready_val| - if ready_val.is_a?(Array) - is_ready, ready_early_return = ready_val - if is_ready != false - raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{ready_early_return.inspect}]" - else - ready_early_return - end - elsif ready_val - # Then call each prepare hook, which may return a different value - # for that argument, or may return a lazy object - load_arguments_val = load_arguments(args) - context.query.after_lazy(load_arguments_val) do |loaded_args| - @prepared_arguments = loaded_args - Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field) - # Then call `authorized?`, which may raise or may return a lazy object - raw_authorized_val = if !loaded_args.empty? - authorized?(**loaded_args) - else - authorized? - end - context.query.after_lazy(raw_authorized_val) do |authorized_val| - # If the `authorized?` returned two values, `false, early_return`, - # then use the early return value instead of continuing - if authorized_val.is_a?(Array) - authorized_result, early_return = authorized_val - if authorized_result == false - early_return - else - raise "Unexpected result from #authorized? (expected `true`, `false` or `[false, {...}]`): [#{authorized_result.inspect}, #{early_return.inspect}]" - end - elsif authorized_val - # Finally, all the hooks have passed, so resolve it - call_resolve(loaded_args) - else - raise GraphQL::UnauthorizedFieldError.new(context: context, object: object, type: field.owner, field: field) - end - end - end - end - end - end - - # @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache - def call_resolve(args_hash) - if !args_hash.empty? - public_send(self.class.resolve_method, **args_hash) - else - public_send(self.class.resolve_method) - end - end - - # Do the work. Everything happens here. - # @return [Object] An object corresponding to the return type - def resolve(**args) - raise GraphQL::RequiredImplementationMissingError, "#{self.class.name}#resolve should execute the field's logic" - end - - # Called before arguments are prepared. - # Implement this hook to make checks before doing any work. - # - # If it returns a lazy object (like a promise), it will be synced by GraphQL - # (but the resulting value won't be used). - # - # @param args [Hash] The input arguments, if there are any - # @raise [GraphQL::ExecutionError] To add an error to the response - # @raise [GraphQL::UnauthorizedError] To signal an authorization failure - # @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.) - def ready?(**args) - true - end - - # Called after arguments are loaded, but before resolving. - # - # Override it to check everything before calling the mutation. - # @param inputs [Hash] The input arguments - # @raise [GraphQL::ExecutionError] To add an error to the response - # @raise [GraphQL::UnauthorizedError] To signal an authorization failure - # @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.) - def authorized?(**inputs) - arg_owner = @field # || self.class - args = context.types.arguments(arg_owner) - authorize_arguments(args, inputs) - end - - # Called when an object loaded by `loads:` fails the `.authorized?` check for its resolved GraphQL object type. - # - # By default, the error is re-raised and passed along to {{Schema.unauthorized_object}}. - # - # Any value returned here will be used _instead of_ of the loaded object. - # @param err [GraphQL::UnauthorizedError] - def unauthorized_object(err) - raise err - end - - private - - def authorize_arguments(args, inputs) - args.each do |argument| - arg_keyword = argument.keyword - if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value) - auth_result = argument.authorized?(self, arg_value, context) - if auth_result.is_a?(Array) - # only return this second value if the application returned a second value - arg_auth, err = auth_result - if !arg_auth - return arg_auth, err - end - elsif auth_result == false - return auth_result - end - end - end - true - end - - def load_arguments(args) - prepared_args = {} - prepare_lazies = [] - - args.each do |key, value| - arg_defn = @arguments_by_keyword[key] - if arg_defn - prepped_value = prepared_args[key] = arg_defn.load_and_authorize_value(self, value, context) - if context.schema.lazy?(prepped_value) - prepare_lazies << context.query.after_lazy(prepped_value) do |finished_prepped_value| - prepared_args[key] = finished_prepped_value - end - end - else - # these are `extras:` - prepared_args[key] = value - end - end - - # Avoid returning a lazy if none are needed - if !prepare_lazies.empty? - GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args } - else - prepared_args - end - end - - def get_argument(name, context = GraphQL::Query::NullContext.instance) - self.class.get_argument(name, context) - end - - class << self - def field_arguments(context = GraphQL::Query::NullContext.instance) - arguments(context) - end - - def any_field_arguments? - any_arguments? - end - - def get_field_argument(name, context = GraphQL::Query::NullContext.instance) - get_argument(name, context) - end - - def all_field_argument_definitions - all_argument_definitions - end - - # Default `:resolve` set below. - # @return [Symbol] The method to call on instances of this object to resolve the field - def resolve_method(new_method = nil) - if new_method - @resolve_method = new_method - end - @resolve_method || (superclass.respond_to?(:resolve_method) ? superclass.resolve_method : :resolve) - end - - # Additional info injected into {#resolve} - # @see {GraphQL::Schema::Field#extras} - def extras(new_extras = nil) - if new_extras - @own_extras = new_extras - end - own_extras = @own_extras || [] - own_extras + (superclass.respond_to?(:extras) ? superclass.extras : []) - end - - # If `true` (default), then the return type for this resolver will be nullable. - # If `false`, then the return type is non-null. - # - # @see #type which sets the return type of this field and accepts a `null:` option - # @param allow_null [Boolean] Whether or not the response can be null - def null(allow_null = nil) - if !allow_null.nil? - @null = allow_null - end - - @null.nil? ? (superclass.respond_to?(:null) ? superclass.null : true) : @null - end - - def resolver_method(new_method_name = nil) - if new_method_name - @resolver_method = new_method_name - else - @resolver_method || :resolve_with_support - end - end - - # Call this method to get the return type of the field, - # or use it as a configuration method to assign a return type - # instead of generating one. - # TODO unify with {#null} - # @param new_type [Class, Array, nil] If a type definition class is provided, it will be used as the return type of the field - # @param null [true, false] Whether or not the field may return `nil` - # @return [Class] The type which this field returns. - def type(new_type = nil, null: nil) - if new_type - if null.nil? - raise ArgumentError, "required argument `null:` is missing" - end - @type_expr = new_type - @null = null - else - if type_expr - GraphQL::Schema::Member::BuildType.parse_type(type_expr, null: self.null) - elsif superclass.respond_to?(:type) - superclass.type - else - nil - end - end - end - - # Specifies the complexity of the field. Defaults to `1` - # @return [Integer, Proc] - def complexity(new_complexity = nil) - if new_complexity - @complexity = new_complexity - end - @complexity || (superclass.respond_to?(:complexity) ? superclass.complexity : 1) - end - - def broadcastable(new_broadcastable) - @broadcastable = new_broadcastable - end - - # @return [Boolean, nil] - def broadcastable? - if defined?(@broadcastable) - @broadcastable - else - (superclass.respond_to?(:broadcastable?) ? superclass.broadcastable? : nil) - end - end - - # Get or set the `max_page_size:` which will be configured for fields using this resolver - # (`nil` means "unlimited max page size".) - # @param max_page_size [Integer, nil] Set a new value - # @return [Integer, nil] The `max_page_size` assigned to fields that use this resolver - def max_page_size(new_max_page_size = NOT_CONFIGURED) - if new_max_page_size != NOT_CONFIGURED - @max_page_size = new_max_page_size - elsif defined?(@max_page_size) - @max_page_size - elsif superclass.respond_to?(:max_page_size) - superclass.max_page_size - else - nil - end - end - - # @return [Boolean] `true` if this resolver or a superclass has an assigned `max_page_size` - def has_max_page_size? - (!!defined?(@max_page_size)) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?) - end - - # Get or set the `default_page_size:` which will be configured for fields using this resolver - # (`nil` means "unlimited default page size".) - # @param default_page_size [Integer, nil] Set a new value - # @return [Integer, nil] The `default_page_size` assigned to fields that use this resolver - def default_page_size(new_default_page_size = NOT_CONFIGURED) - if new_default_page_size != NOT_CONFIGURED - @default_page_size = new_default_page_size - elsif defined?(@default_page_size) - @default_page_size - elsif superclass.respond_to?(:default_page_size) - superclass.default_page_size - else - nil - end - end - - # @return [Boolean] `true` if this resolver or a superclass has an assigned `default_page_size` - def has_default_page_size? - (!!defined?(@default_page_size)) || (superclass.respond_to?(:has_default_page_size?) && superclass.has_default_page_size?) - end - - # A non-normalized type configuration, without `null` applied - def type_expr - @type_expr || (superclass.respond_to?(:type_expr) ? superclass.type_expr : nil) - end - - # Add an argument to this field's signature, but - # also add some preparation hook methods which will be used for this argument - # @see {GraphQL::Schema::Argument#initialize} for the signature - def argument(*args, **kwargs, &block) - # Use `from_resolver: true` to short-circuit the InputObject's own `loads:` implementation - # so that we can support `#load_{x}` methods below. - super(*args, from_resolver: true, **kwargs) - end - - # Registers new extension - # @param extension [Class] Extension class - # @param options [Hash] Optional extension options - def extension(extension, **options) - @own_extensions ||= [] - @own_extensions << {extension => options} - end - - # @api private - def extensions - own_exts = @own_extensions - # Jump through some hoops to avoid creating arrays when we don't actually need them - if superclass.respond_to?(:extensions) - s_exts = superclass.extensions - if own_exts - if !s_exts.empty? - own_exts + s_exts - else - own_exts - end - else - s_exts - end - else - own_exts || EMPTY_ARRAY - end - end - - private - - attr_reader :own_extensions - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/resolver/has_payload_type.rb b/vendor/gems/graphql/lib/graphql/schema/resolver/has_payload_type.rb deleted file mode 100644 index 89a974164de..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/resolver/has_payload_type.rb +++ /dev/null @@ -1,106 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Resolver - # Adds `field(...)` helper to resolvers so that they can - # generate payload types. - # - # Or, an already-defined one can be attached with `payload_type(...)`. - module HasPayloadType - # Call this method to get the derived return type of the mutation, - # or use it as a configuration method to assign a return type - # instead of generating one. - # @param new_payload_type [Class, nil] If a type definition class is provided, it will be used as the return type of the mutation field - # @return [Class] The object type which this mutation returns. - def payload_type(new_payload_type = nil) - if new_payload_type - @payload_type = new_payload_type - end - @payload_type ||= generate_payload_type - end - - def type(new_type = nil, null: nil) - if new_type - payload_type(new_type) - if !null.nil? - self.null(null) - end - else - super() - end - end - - alias :type_expr :payload_type - - def field_class(new_class = nil) - if new_class - @field_class = new_class - elsif defined?(@field_class) && @field_class - @field_class - else - find_inherited_value(:field_class, GraphQL::Schema::Field) - end - end - - # An object class to use for deriving return types - # @param new_class [Class, nil] Defaults to {GraphQL::Schema::Object} - # @return [Class] - def object_class(new_class = nil) - if new_class - if defined?(@payload_type) - raise "Can't configure `object_class(...)` after the payload type has already been initialized. Move this configuration higher up the class definition." - end - @object_class = new_class - else - @object_class || find_inherited_value(:object_class, GraphQL::Schema::Object) - end - end - - NO_INTERFACES = [].freeze - - def field(*args, **kwargs, &block) - pt = payload_type # make sure it's initialized with any inherited fields - field_defn = super - - # Remove any inherited fields to avoid false conflicts at runtime - prev_fields = pt.own_fields[field_defn.graphql_name] - case prev_fields - when GraphQL::Schema::Field - if prev_fields.owner != self - pt.own_fields.delete(field_defn.graphql_name) - end - when Array - prev_fields.reject! { |f| f.owner != self } - if prev_fields.empty? - pt.own_fields.delete(field_defn.graphql_name) - end - end - - pt.add_field(field_defn, method_conflict_warning: false) - field_defn - end - - private - - # Build a subclass of {.object_class} based on `self`. - # This value will be cached as `{.payload_type}`. - # Override this hook to customize return type generation. - def generate_payload_type - resolver_name = graphql_name - resolver_fields = all_field_definitions - pt = Class.new(object_class) - pt.graphql_name("#{resolver_name}Payload") - pt.description("Autogenerated return type of #{resolver_name}.") - resolver_fields.each do |f| - # Reattach the already-defined field here - # (The field's `.owner` will still point to the mutation, not the object type, I think) - # Don't re-warn about a method conflict. Since this type is generated, it should be fixed in the resolver instead. - pt.add_field(f, method_conflict_warning: false) - end - pt - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/scalar.rb b/vendor/gems/graphql/lib/graphql/schema/scalar.rb deleted file mode 100644 index 6e3a94e03ee..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/scalar.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Scalar < GraphQL::Schema::Member - extend GraphQL::Schema::Member::ValidatesInput - - class << self - def coerce_input(val, ctx) - val - end - - def coerce_result(val, ctx) - val - end - - def kind - GraphQL::TypeKinds::SCALAR - end - - def specified_by_url(new_url = nil) - if new_url - directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url) - elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" }) - directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop - elsif superclass.respond_to?(:specified_by_url) - superclass.specified_by_url - else - nil - end - end - - def default_scalar(is_default = nil) - if !is_default.nil? - @default_scalar = is_default - end - @default_scalar - end - - def default_scalar? - @default_scalar ||= false - end - - def validate_non_null_input(value, ctx, max_errors: nil) - coerced_result = begin - coerce_input(value, ctx) - rescue GraphQL::CoercionError => err - err - rescue StandardError => err - ctx.query.handle_or_reraise(err) - end - - if coerced_result.nil? - str_value = if value == Float::INFINITY - "" - else - " #{GraphQL::Language.serialize(value)}" - end - Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}") - elsif coerced_result.is_a?(GraphQL::CoercionError) - Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions) - else - nil - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/subscription.rb b/vendor/gems/graphql/lib/graphql/schema/subscription.rb deleted file mode 100644 index 8f1c07971cb..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/subscription.rb +++ /dev/null @@ -1,201 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # This class can be extended to create fields on your subscription root. - # - # It provides hooks for the different parts of the subscription lifecycle: - # - # - `#authorized?`: called before initial subscription and subsequent updates - # - `#subscribe`: called for the initial subscription - # - `#update`: called for subsequent update - # - # Also, `#unsubscribe` terminates the subscription. - class Subscription < GraphQL::Schema::Resolver - extend GraphQL::Schema::Resolver::HasPayloadType - extend GraphQL::Schema::Member::HasFields - NO_UPDATE = :no_update - # The generated payload type is required; If there's no payload, - # propagate null. - null false - - # @api private - def initialize(object:, context:, field:) - super - # Figure out whether this is an update or an initial subscription - @mode = context.query.subscription_update? ? :update : :subscribe - @subscription_written = false - @original_arguments = nil - if (subs_ns = context.namespace(:subscriptions)) && - (sub_insts = subs_ns[:subscriptions]) - sub_insts[context.current_path] = self - end - end - - # @api private - def resolve_with_support(**args) - @original_arguments = args # before `loads:` have been run - result = nil - unsubscribed = true - unsubscribed_result = catch :graphql_subscription_unsubscribed do - result = super - unsubscribed = false - end - - - if unsubscribed - if unsubscribed_result - context.namespace(:subscriptions)[:final_update] = true - unsubscribed_result - else - context.skip - end - else - result - end - end - - # Implement the {Resolve} API. - # You can implement this if you want code to run for _both_ the initial subscription - # and for later updates. Or, implement {#subscribe} and {#update} - def resolve(**args) - # Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever - # have an unexpected `@mode` - public_send("resolve_#{@mode}", **args) - end - - # Wrap the user-defined `#subscribe` hook - # @api private - def resolve_subscribe(**args) - ret_val = !args.empty? ? subscribe(**args) : subscribe - if ret_val == :no_response - context.skip - else - ret_val - end - end - - # The default implementation returns nothing on subscribe. - # Override it to return an object or - # `:no_response` to (explicitly) return nothing. - def subscribe(args = {}) - :no_response - end - - # Wrap the user-provided `#update` hook - # @api private - def resolve_update(**args) - ret_val = !args.empty? ? update(**args) : update - if ret_val == NO_UPDATE - context.namespace(:subscriptions)[:no_update] = true - context.skip - else - ret_val - end - end - - # The default implementation returns the root object. - # Override it to return {NO_UPDATE} if you want to - # skip updates sometimes. Or override it to return a different object. - def update(args = {}) - object - end - - # If an argument is flagged with `loads:` and no object is found for it, - # remove this subscription (assuming that the object was deleted in the meantime, - # or that it became inaccessible). - def load_application_object_failed(err) - if @mode == :update - unsubscribe - end - super - end - - # Call this to halt execution and remove this subscription from the system - # @param update_value [Object] if present, deliver this update before unsubscribing - # @return [void] - def unsubscribe(update_value = nil) - context.namespace(:subscriptions)[:unsubscribed] = true - throw :graphql_subscription_unsubscribed, update_value - end - - # Call this method to provide a new subscription_scope; OR - # call it without an argument to get the subscription_scope - # @param new_scope [Symbol] - # @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription. - # @return [Symbol] - def self.subscription_scope(new_scope = NOT_CONFIGURED, optional: false) - if new_scope != NOT_CONFIGURED - @subscription_scope = new_scope - @subscription_scope_optional = optional - elsif defined?(@subscription_scope) - @subscription_scope - else - find_inherited_value(:subscription_scope) - end - end - - def self.subscription_scope_optional? - if defined?(@subscription_scope_optional) - @subscription_scope_optional - else - find_inherited_value(:subscription_scope_optional, false) - end - end - - # This is called during initial subscription to get a "name" for this subscription. - # Later, when `.trigger` is called, this will be called again to build another "name". - # Any subscribers with matching topic will begin the update flow. - # - # The default implementation creates a string using the field name, subscription scope, and argument keys and values. - # In that implementation, only `.trigger` calls with _exact matches_ result in updates to subscribers. - # - # To implement a filtered stream-type subscription flow, override this method to return a string with field name and subscription scope. - # Then, implement {#update} to compare its arguments to the current `object` and return {NO_UPDATE} when an - # update should be filtered out. - # - # @see {#update} for how to skip updates when an event comes with a matching topic. - # @param arguments [Hash Object>] The arguments for this topic, in GraphQL-style (camelized strings) - # @param field [GraphQL::Schema::Field] - # @param scope [Object, nil] A value corresponding to `.trigger(... scope:)` (for updates) or the `subscription_scope` found in `context` (for initial subscriptions). - # @return [String] An identifier corresponding to a stream of updates - def self.topic_for(arguments:, field:, scope:) - Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments]) - end - - # Calls through to `schema.subscriptions` to register this subscription with the backend. - # This is automatically called by GraphQL-Ruby after a query finishes successfully, - # but if you need to commit the subscription during `#subscribe`, you can call it there. - # (This method also sets a flag showing that this subscription was already written.) - # - # If you call this method yourself, you may also need to {#unsubscribe} - # or call `subscriptions.delete_subscription` to clean up the database if the query crashes with an error - # later in execution. - # @return [void] - def write_subscription - if subscription_written? - raise GraphQL::Error, "`write_subscription` was called but `#{self.class}#subscription_written?` is already true. Remove a call to `write subscription`." - else - @subscription_written = true - context.schema.subscriptions.write_subscription(context.query, [event]) - end - nil - end - - # @return [Boolean] `true` if {#write_subscription} was called already - def subscription_written? - @subscription_written - end - - # @return [Subscriptions::Event] This object is used as a representation of this subscription for the backend - def event - @event ||= Subscriptions::Event.new( - name: field.name, - arguments: @original_arguments, - context: context, - field: field, - ) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/timeout.rb b/vendor/gems/graphql/lib/graphql/schema/timeout.rb deleted file mode 100644 index 0a860fa0c15..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/timeout.rb +++ /dev/null @@ -1,119 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # This plugin will stop resolving new fields after `max_seconds` have elapsed. - # After the time has passed, any remaining fields will be `nil`, with errors added - # to the `errors` key. Any already-resolved fields will be in the `data` key, so - # you'll get a partial response. - # - # You can subclass `GraphQL::Schema::Timeout` and override `max_seconds` and/or `handle_timeout` - # to provide custom logic when a timeout error occurs. - # - # Note that this will stop a query _in between_ field resolutions, but - # it doesn't interrupt long-running `resolve` functions. Be sure to use - # timeout options for external connections. For more info, see - # www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/ - # - # @example Stop resolving fields after 2 seconds - # class MySchema < GraphQL::Schema - # use GraphQL::Schema::Timeout, max_seconds: 2 - # end - # - # @example Notifying Bugsnag and logging a timeout - # class MyTimeout < GraphQL::Schema::Timeout - # def handle_timeout(error, query) - # Rails.logger.warn("GraphQL Timeout: #{error.message}: #{query.query_string}") - # Bugsnag.notify(error, {query_string: query.query_string}) - # end - # end - # - # class MySchema < GraphQL::Schema - # use MyTimeout, max_seconds: 2 - # end - # - class Timeout - def self.use(schema, max_seconds: nil) - timeout = self.new(max_seconds: max_seconds) - schema.trace_with(self::Trace, timeout: timeout) - end - - def initialize(max_seconds:) - @max_seconds = max_seconds - end - - module Trace - # @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields - def initialize(timeout:, **rest) - @timeout = timeout - super - end - - def execute_multiplex(multiplex:) - multiplex.queries.each do |query| - timeout_duration_s = @timeout.max_seconds(query) - timeout_state = if timeout_duration_s == false - # if the method returns `false`, don't apply a timeout - false - else - now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - timeout_at = now + (timeout_duration_s * 1000) - { - timeout_at: timeout_at, - timed_out: false - } - end - query.context.namespace(@timeout)[:state] = timeout_state - end - super - end - - def execute_field(query:, field:, **_rest) - timeout_state = query.context.namespace(@timeout).fetch(:state) - # If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query. - if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at) - error = GraphQL::Schema::Timeout::TimeoutError.new(field) - # Only invoke the timeout callback for the first timeout - if !timeout_state[:timed_out] - timeout_state[:timed_out] = true - @timeout.handle_timeout(error, query) - end - - error - else - super - end - end - end - - # Called at the start of each query. - # The default implementation returns the `max_seconds:` value from installing this plugin. - # - # @param query [GraphQL::Query] The query that's about to run - # @return [Numeric, false] The number of seconds after which to interrupt query execution and call {#handle_error}, or `false` to bypass the timeout. - def max_seconds(query) - @max_seconds - end - - # Invoked when a query times out. - # @param error [GraphQL::Schema::Timeout::TimeoutError] - # @param query [GraphQL::Error] - def handle_timeout(error, query) - # override to do something interesting - end - - # This error is raised when a query exceeds `max_seconds`. - # Since it's a child of {GraphQL::ExecutionError}, - # its message will be added to the response's `errors` key. - # - # To raise an error that will stop query resolution, use a custom block - # to take this error and raise a new one which _doesn't_ descend from {GraphQL::ExecutionError}, - # such as `RuntimeError`. - class TimeoutError < GraphQL::ExecutionError - def initialize(field) - super("Timeout on #{field.path}") - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/type_expression.rb b/vendor/gems/graphql/lib/graphql/schema/type_expression.rb deleted file mode 100644 index 5ade5c70145..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/type_expression.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - # @api private - module TypeExpression - # Fetch a type from a type map by its AST specification. - # Return `nil` if not found. - # @param type_owner [#type] A thing for looking up types by name - # @param ast_node [GraphQL::Language::Nodes::AbstractNode] - # @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List] - def self.build_type(type_owner, ast_node) - case ast_node - when GraphQL::Language::Nodes::TypeName - type_owner.type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware - when GraphQL::Language::Nodes::NonNullType - ast_inner_type = ast_node.of_type - inner_type = build_type(type_owner, ast_inner_type) - wrap_type(inner_type, :to_non_null_type) - when GraphQL::Language::Nodes::ListType - ast_inner_type = ast_node.of_type - inner_type = build_type(type_owner, ast_inner_type) - wrap_type(inner_type, :to_list_type) - else - raise "Invariant: unexpected type from ast: #{ast_node.inspect}" - end - end - - class << self - private - - def wrap_type(type, wrapper_method) - if type.nil? - nil - elsif wrapper_method == :to_list_type || wrapper_method == :to_non_null_type - type.public_send(wrapper_method) - else - raise ArgumentError, "Unexpected wrapper method: #{wrapper_method.inspect}" - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/type_membership.rb b/vendor/gems/graphql/lib/graphql/schema/type_membership.rb deleted file mode 100644 index 6f25da49db0..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/type_membership.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - # This class joins an object type to an abstract type (interface or union) of which - # it is a member. - class TypeMembership - # @return [Class] - attr_accessor :object_type - - # @return [Class, Module] - attr_reader :abstract_type - - # @return [Hash] - attr_reader :options - - # Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types} - # or {Schema::Object.implements} (for interfaces). - # - # @param abstract_type [Class, Module] - # @param object_type [Class] - # @param options [Hash] Any options passed to `.possible_types` or `.implements` - def initialize(abstract_type, object_type, **options) - @abstract_type = abstract_type - @object_type = object_type - @options = options - end - - # @return [Boolean] if false, {#object_type} will be treated as _not_ a member of {#abstract_type} - def visible?(ctx) - warden = Warden.from_context(ctx) - (@object_type.respond_to?(:visible?) ? warden.visible_type?(@object_type, ctx) : true) && - (@abstract_type.respond_to?(:visible?) ? warden.visible_type?(@abstract_type, ctx) : true) - end - - def graphql_name - "#{@object_type.graphql_name}.#{@abstract_type.kind.interface? ? "implements" : "belongsTo" }.#{@abstract_type.graphql_name}" - end - - def path - graphql_name - end - - def inspect - "#<#{self.class} #{@object_type.inspect} => #{@abstract_type.inspect}>" - end - - alias :type_class :itself - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/union.rb b/vendor/gems/graphql/lib/graphql/schema/union.rb deleted file mode 100644 index f0ad4feec7f..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/union.rb +++ /dev/null @@ -1,90 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Union < GraphQL::Schema::Member - extend GraphQL::Schema::Member::HasUnresolvedTypeError - - class << self - def inherited(child_class) - add_unresolved_type_error(child_class) - super - end - - def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options) - if !types.empty? - types.each do |t| - assert_valid_union_member(t) - type_memberships << type_membership_class.new(self, t, **options) - end - else - visible_types = [] - warden = Warden.from_context(context) - type_memberships.each do |type_membership| - if warden.visible_type_membership?(type_membership, context) - visible_types << type_membership.object_type - end - end - visible_types - end - end - - def all_possible_types - type_memberships.map(&:object_type) - end - - def type_membership_class(membership_class = nil) - if membership_class - @type_membership_class = membership_class - else - @type_membership_class || find_inherited_value(:type_membership_class, GraphQL::Schema::TypeMembership) - end - end - - def kind - GraphQL::TypeKinds::UNION - end - - def type_memberships - @type_memberships ||= [] - end - - # Update a type membership whose `.object_type` is a string or late-bound type - # so that the type membership's `.object_type` is the given `object_type`. - # (This is used for updating the union after the schema as lazily loaded the union member.) - # @api private - def assign_type_membership_object_type(object_type) - assert_valid_union_member(object_type) - type_memberships.each { |tm| - possible_type = tm.object_type - if possible_type.is_a?(String) && (possible_type == object_type.name) - # This is a match of Ruby class names, not graphql names, - # since strings are used to refer to constants. - tm.object_type = object_type - elsif possible_type.is_a?(LateBoundType) && possible_type.graphql_name == object_type.graphql_name - tm.object_type = object_type - end - } - nil - end - - private - - def assert_valid_union_member(type_defn) - case type_defn - when Class - if !type_defn.kind.object? - raise ArgumentError, "Union possible_types can only be object types (not #{type_defn.kind.name}, #{type_defn.inspect})" - end - when Module - # it's an interface type, defined as a module - raise ArgumentError, "Union possible_types can only be object types (not interface types), remove #{type_defn.graphql_name} (#{type_defn.inspect})" - when String, GraphQL::Schema::LateBoundType - # Ok - assume it will get checked later - else - raise ArgumentError, "Union possible_types can only be class-based GraphQL types (not #{type_defn.inspect} (#{type_defn.class.name}))." - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/unique_within_type.rb b/vendor/gems/graphql/lib/graphql/schema/unique_within_type.rb deleted file mode 100644 index 55c5f715296..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/unique_within_type.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true -require "base64" - -module GraphQL - class Schema - module UniqueWithinType - class << self - attr_accessor :default_id_separator - end - self.default_id_separator = "-" - - module_function - - # @param type_name [String] - # @param object_value [Any] - # @return [String] a unique, opaque ID generated as a function of the two inputs - def encode(type_name, object_value, separator: self.default_id_separator) - object_value_str = object_value.to_s - - if type_name.include?(separator) - raise "encode(#{type_name}, #{object_value_str}) contains reserved characters `#{separator}` in the type name" - end - - Base64.strict_encode64([type_name, object_value_str].join(separator)) - end - - # @param node_id [String] A unique ID generated by {.encode} - # @return [Array<(String, String)>] The type name & value passed to {.encode} - def decode(node_id, separator: self.default_id_separator) - GraphQL::Schema::Base64Encoder.decode(node_id).split(separator, 2) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator.rb deleted file mode 100644 index fac5c34511b..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator.rb +++ /dev/null @@ -1,173 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # The thing being validated - # @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class] - attr_reader :validated - - # @param validated [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class] The argument or argument owner this validator is attached to - # @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation - # @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation - def initialize(validated:, allow_blank: false, allow_null: false) - @validated = validated - @allow_blank = allow_blank - @allow_null = allow_null - end - - # @param object [Object] The application object that this argument's field is being resolved for - # @param context [GraphQL::Query::Context] - # @param value [Object] The client-provided value for this argument (after parsing and coercing by the input type) - # @return [nil, Array, String] Error message or messages to add - def validate(object, context, value) - raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate" - end - - # This is like `String#%`, but it supports the case that only some of `string`'s - # values are present in `substitutions` - def partial_format(string, substitutions) - substitutions.each do |key, value| - sub_v = value.is_a?(String) ? value : value.to_s - string = string.gsub("%{#{key}}", sub_v) - end - string - end - - # @return [Boolean] `true` if `value` is `nil` and this validator has `allow_null: true` or if value is `.blank?` and this validator has `allow_blank: true` - def permitted_empty_value?(value) - (value.nil? && @allow_null) || - (@allow_blank && value.respond_to?(:blank?) && value.blank?) - end - - # @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class] - # @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:` - # @return [Array] - def self.from_config(schema_member, validates_hash) - if validates_hash.nil? || validates_hash.empty? - EMPTY_ARRAY - else - validates_hash = validates_hash.dup - - default_options = {} - if validates_hash[:allow_null] - default_options[:allow_null] = validates_hash.delete(:allow_null) - end - if validates_hash[:allow_blank] - default_options[:allow_blank] = validates_hash.delete(:allow_blank) - end - - # allow_nil or allow_blank are the _only_ validations: - if validates_hash.empty? - validates_hash = default_options - end - - validates_hash.map do |validator_name, options| - validator_class = case validator_name - when Class - validator_name - else - all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}") - end - if options.is_a?(Hash) - validator_class.new(validated: schema_member, **(default_options.merge(options))) - else - validator_class.new(options, validated: schema_member, **default_options) - end - end - end - end - - # Add `validator_class` to be initialized when `validates:` is given `name`. - # (It's initialized with whatever options are given by the key `name`). - # @param name [Symbol] - # @param validator_class [Class] - # @return [void] - def self.install(name, validator_class) - all_validators[name] = validator_class - nil - end - - # Remove whatever validator class is {.install}ed at `name`, if there is one - # @param name [Symbol] - # @return [void] - def self.uninstall(name) - all_validators.delete(name) - nil - end - - class << self - attr_accessor :all_validators - end - - self.all_validators = {} - - include GraphQL::EmptyObjects - - class ValidationFailedError < GraphQL::ExecutionError - attr_reader :errors - - def initialize(errors:) - @errors = errors - super(errors.join(", ")) - end - end - - # @param validators [Array] - # @param object [Object] - # @param context [Query::Context] - # @param value [Object] - # @return [void] - # @raises [ValidationFailedError] - def self.validate!(validators, object, context, value, as: nil) - # Assuming the default case is no errors, reduce allocations in that case. - # This will be replaced with a mutable array if we actually get any errors. - all_errors = EMPTY_ARRAY - - validators.each do |validator| - validated = as || validator.validated - errors = validator.validate(object, context, value) - if errors && - (errors.is_a?(Array) && errors != EMPTY_ARRAY) || - (errors.is_a?(String)) - if all_errors.frozen? # It's empty - all_errors = [] - end - interpolation_vars = { validated: validated.graphql_name, value: value.inspect } - if errors.is_a?(String) - all_errors << (errors % interpolation_vars) - else - errors = errors.map { |e| e % interpolation_vars } - all_errors.concat(errors) - end - end - end - - if !all_errors.empty? - raise ValidationFailedError.new(errors: all_errors) - end - nil - end - end - end -end - - -require "graphql/schema/validator/length_validator" -GraphQL::Schema::Validator.install(:length, GraphQL::Schema::Validator::LengthValidator) -require "graphql/schema/validator/numericality_validator" -GraphQL::Schema::Validator.install(:numericality, GraphQL::Schema::Validator::NumericalityValidator) -require "graphql/schema/validator/format_validator" -GraphQL::Schema::Validator.install(:format, GraphQL::Schema::Validator::FormatValidator) -require "graphql/schema/validator/inclusion_validator" -GraphQL::Schema::Validator.install(:inclusion, GraphQL::Schema::Validator::InclusionValidator) -require "graphql/schema/validator/exclusion_validator" -GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator) -require "graphql/schema/validator/required_validator" -GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator) -require "graphql/schema/validator/allow_null_validator" -GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator) -require "graphql/schema/validator/allow_blank_validator" -GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator) -require "graphql/schema/validator/all_validator" -GraphQL::Schema::Validator.install(:all, GraphQL::Schema::Validator::AllValidator) diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/all_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/all_validator.rb deleted file mode 100644 index 5850144a02f..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/all_validator.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this to validate each member of an array value. - # - # @example validate format of all strings in an array - # - # argument :handles, [String], - # validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ } } } - # - # @example multiple validators can be combined - # - # argument :handles, [String], - # validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ }, length: { maximum: 32 } } } - # - # @example any type can be used - # - # argument :choices, [Integer], - # validates: { all: { inclusion: { in: 1..12 } } } - # - class AllValidator < Validator - def initialize(validated:, allow_blank: false, allow_null: false, **validators) - super(validated: validated, allow_blank: allow_blank, allow_null: allow_null) - - @validators = Validator.from_config(validated, validators) - end - - def validate(object, context, value) - return EMPTY_ARRAY if permitted_empty_value?(value) - - all_errors = EMPTY_ARRAY - - value.each do |subvalue| - @validators.each do |validator| - errors = validator.validate(object, context, subvalue) - if errors && - (errors.is_a?(Array) && errors != EMPTY_ARRAY) || - (errors.is_a?(String)) - if all_errors.frozen? # It's empty - all_errors = [] - end - if errors.is_a?(String) - all_errors << errors - else - all_errors.concat(errors) - end - end - end - end - - unless all_errors.frozen? - all_errors.uniq! - end - - all_errors - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/allow_blank_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/allow_blank_validator.rb deleted file mode 100644 index 99cbc934bf0..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/allow_blank_validator.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this to specifically reject values that respond to `.blank?` and respond truthy for that method. - # - # @example Require a non-empty string for an argument - # argument :name, String, required: true, validate: { allow_blank: false } - class AllowBlankValidator < Validator - def initialize(allow_blank_positional, allow_blank: nil, message: "%{validated} can't be blank", **default_options) - @message = message - super(**default_options) - @allow_blank = allow_blank.nil? ? allow_blank_positional : allow_blank - end - - def validate(_object, _context, value) - if value.respond_to?(:blank?) && value.blank? - if (value.nil? && @allow_null) || @allow_blank - # pass - else - @message - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/allow_null_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/allow_null_validator.rb deleted file mode 100644 index 33da57d0768..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/allow_null_validator.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this to specifically reject or permit `nil` values (given as `null` from GraphQL). - # - # @example require a non-null value for an argument if it is provided - # argument :name, String, required: false, validates: { allow_null: false } - class AllowNullValidator < Validator - MESSAGE = "%{validated} can't be null" - def initialize(allow_null_positional, allow_null: nil, message: MESSAGE, **default_options) - @message = message - super(**default_options) - @allow_null = allow_null.nil? ? allow_null_positional : allow_null - end - - def validate(_object, _context, value) - if value.nil? && !@allow_null - @message - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/exclusion_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/exclusion_validator.rb deleted file mode 100644 index 0a7a9b1cd99..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/exclusion_validator.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this to specifically reject values from an argument. - # - # @example disallow certain values - # - # argument :favorite_non_prime, Integer, required: true, - # validates: { exclusion: { in: [2, 3, 5, 7, ... ]} } - # - class ExclusionValidator < Validator - # @param message [String] - # @param in [Array] The values to reject - def initialize(message: "%{validated} is reserved", in:, **default_options) - # `in` is a reserved word, so work around that - @in_list = binding.local_variable_get(:in) - @message = message - super(**default_options) - end - - def validate(_object, _context, value) - if permitted_empty_value?(value) - # pass - elsif @in_list.include?(value) - @message - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/format_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/format_validator.rb deleted file mode 100644 index bdbd3d6a02d..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/format_validator.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this to assert that string values match (or don't match) the given RegExp. - # - # @example requiring input to match a pattern - # - # argument :handle, String, required: true, - # validates: { format: { with: /\A[a-z0-9_]+\Z/ } } - # - # @example reject inputs that match a pattern - # - # argument :word_that_doesnt_begin_with_a_vowel, String, required: true, - # validates: { format: { without: /\A[aeiou]/ } } - # - # # It's pretty hard to come up with a legitimate use case for `without:` - # - class FormatValidator < Validator - # @param with [RegExp, nil] - # @param without [Regexp, nil] - # @param message [String] - def initialize( - with: nil, - without: nil, - message: "%{validated} is invalid", - **default_options - ) - @with_pattern = with - @without_pattern = without - @message = message - super(**default_options) - end - - def validate(_object, _context, value) - if permitted_empty_value?(value) - # Do nothing - elsif value.nil? || - (@with_pattern && !value.match?(@with_pattern)) || - (@without_pattern && value.match?(@without_pattern)) - @message - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/inclusion_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/inclusion_validator.rb deleted file mode 100644 index 3735ab1ba6a..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/inclusion_validator.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # You can use this to allow certain values for an argument. - # - # Usually, a {GraphQL::Schema::Enum} is better for this, because it's self-documenting. - # - # @example only allow certain values for an argument - # - # argument :favorite_prime, Integer, required: true, - # validates: { inclusion: { in: [2, 3, 5, 7, 11, ... ] } } - # - class InclusionValidator < Validator - # @param message [String] - # @param in [Array] The values to allow - def initialize(in:, message: "%{validated} is not included in the list", **default_options) - # `in` is a reserved word, so work around that - @in_list = binding.local_variable_get(:in) - @message = message - super(**default_options) - end - - def validate(_object, _context, value) - if permitted_empty_value?(value) - # pass - elsif !@in_list.include?(value) - @message - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/length_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/length_validator.rb deleted file mode 100644 index cddba1582d3..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/length_validator.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this to enforce a `.length` restriction on incoming values. It works for both Strings and Lists. - # - # @example Allow no more than 10 IDs - # - # argument :ids, [ID], required: true, validates: { length: { maximum: 10 } } - # - # @example Require three selections - # - # argument :ice_cream_preferences, [ICE_CREAM_FLAVOR], required: true, validates: { length: { is: 3 } } - # - class LengthValidator < Validator - # @param maximum [Integer] - # @param too_long [String] Used when `maximum` is exceeded or value is greater than `within` - # @param minimum [Integer] - # @param too_short [String] Used with value is less than `minimum` or less than `within` - # @param is [Integer] Exact length requirement - # @param wrong_length [String] Used when value doesn't match `is` - # @param within [Range] An allowed range (becomes `minimum:` and `maximum:` under the hood) - # @param message [String] - def initialize( - maximum: nil, too_long: "%{validated} is too long (maximum is %{count})", - minimum: nil, too_short: "%{validated} is too short (minimum is %{count})", - is: nil, within: nil, wrong_length: "%{validated} is the wrong length (should be %{count})", - message: nil, - **default_options - ) - if within && (minimum || maximum) - raise ArgumentError, "`length: { ... }` may include `within:` _or_ `minimum:`/`maximum:`, but not both" - end - # Under the hood, `within` is decomposed into `minimum` and `maximum` - @maximum = maximum || (within && within.max) - @too_long = message || too_long - @minimum = minimum || (within && within.min) - @too_short = message || too_short - @is = is - @wrong_length = message || wrong_length - super(**default_options) - end - - def validate(_object, _context, value) - return if permitted_empty_value?(value) # pass in this case - length = value.nil? ? 0 : value.length - if @maximum && length > @maximum - partial_format(@too_long, { count: @maximum }) - elsif @minimum && length < @minimum - partial_format(@too_short, { count: @minimum }) - elsif @is && length != @is - partial_format(@wrong_length, { count: @is }) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/numericality_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/numericality_validator.rb deleted file mode 100644 index 96e8c92e7c7..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/numericality_validator.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Validator - # Use this to assert numerical comparisons hold true for inputs. - # - # @example Require a number between 0 and 1 - # - # argument :batting_average, Float, required: true, validates: { numericality: { within: 0..1 } } - # - # @example Require the number 42 - # - # argument :the_answer, Integer, required: true, validates: { numericality: { equal_to: 42 } } - # - # @example Require a real number - # - # argument :items_count, Integer, required: true, validates: { numericality: { greater_than_or_equal_to: 0 } } - # - class NumericalityValidator < Validator - # @param greater_than [Integer] - # @param greater_than_or_equal_to [Integer] - # @param less_than [Integer] - # @param less_than_or_equal_to [Integer] - # @param equal_to [Integer] - # @param other_than [Integer] - # @param odd [Boolean] - # @param even [Boolean] - # @param within [Range] - # @param message [String] used for all validation failures - def initialize( - greater_than: nil, greater_than_or_equal_to: nil, - less_than: nil, less_than_or_equal_to: nil, - equal_to: nil, other_than: nil, - odd: nil, even: nil, within: nil, - message: "%{validated} must be %{comparison} %{target}", - null_message: Validator::AllowNullValidator::MESSAGE, - **default_options - ) - - @greater_than = greater_than - @greater_than_or_equal_to = greater_than_or_equal_to - @less_than = less_than - @less_than_or_equal_to = less_than_or_equal_to - @equal_to = equal_to - @other_than = other_than - @odd = odd - @even = even - @within = within - @message = message - @null_message = null_message - super(**default_options) - end - - def validate(object, context, value) - if permitted_empty_value?(value) - # pass in this case - elsif value.nil? # @allow_null is handled in the parent class - @null_message - elsif @greater_than && value <= @greater_than - partial_format(@message, { comparison: "greater than", target: @greater_than }) - elsif @greater_than_or_equal_to && value < @greater_than_or_equal_to - partial_format(@message, { comparison: "greater than or equal to", target: @greater_than_or_equal_to }) - elsif @less_than && value >= @less_than - partial_format(@message, { comparison: "less than", target: @less_than }) - elsif @less_than_or_equal_to && value > @less_than_or_equal_to - partial_format(@message, { comparison: "less than or equal to", target: @less_than_or_equal_to }) - elsif @equal_to && value != @equal_to - partial_format(@message, { comparison: "equal to", target: @equal_to }) - elsif @other_than && value == @other_than - partial_format(@message, { comparison: "something other than", target: @other_than }) - elsif @even && !value.even? - (partial_format(@message, { comparison: "even", target: "" })).strip - elsif @odd && !value.odd? - (partial_format(@message, { comparison: "odd", target: "" })).strip - elsif @within && !@within.include?(value) - partial_format(@message, { comparison: "within", target: @within }) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/validator/required_validator.rb b/vendor/gems/graphql/lib/graphql/schema/validator/required_validator.rb deleted file mode 100644 index 147b2cb34b9..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/validator/required_validator.rb +++ /dev/null @@ -1,123 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Validator - # Use this validator to require _one_ of the named arguments to be present. - # Or, use Arrays of symbols to name a valid _set_ of arguments. - # - # (This is for specifying mutually exclusive sets of arguments.) - # - # @example Require exactly one of these arguments - # - # field :update_amount, IngredientAmount, null: false do - # argument :ingredient_id, ID, required: true - # argument :cups, Integer, required: false - # argument :tablespoons, Integer, required: false - # argument :teaspoons, Integer, required: false - # validates required: { one_of: [:cups, :tablespoons, :teaspoons] } - # end - # - # @example Require one of these _sets_ of arguments - # - # field :find_object, Node, null: true do - # argument :node_id, ID, required: false - # argument :object_type, String, required: false - # argument :object_id, Integer, required: false - # # either a global `node_id` or an `object_type`/`object_id` pair is required: - # validates required: { one_of: [:node_id, [:object_type, :object_id]] } - # end - # - # @example require _some_ value for an argument, even if it's null - # field :update_settings, AccountSettings do - # # `required: :nullable` means this argument must be given, but may be `null` - # argument :age, Integer, required: :nullable - # end - # - class RequiredValidator < Validator - # @param one_of [Array] A list of arguments, exactly one of which is required for this field - # @param argument [Symbol] An argument that is required for this field - # @param message [String] - def initialize(one_of: nil, argument: nil, message: nil, **default_options) - @one_of = if one_of - one_of - elsif argument - [argument] - else - raise ArgumentError, "`one_of:` or `argument:` must be given in `validates required: {...}`" - end - @message = message - super(**default_options) - end - - def validate(_object, context, value) - fully_matched_conditions = 0 - partially_matched_conditions = 0 - - if !value.nil? - @one_of.each do |one_of_condition| - case one_of_condition - when Symbol - if value.key?(one_of_condition) - fully_matched_conditions += 1 - end - when Array - any_match = false - full_match = true - - one_of_condition.each do |k| - if value.key?(k) - any_match = true - else - full_match = false - end - end - - partial_match = !full_match && any_match - - if full_match - fully_matched_conditions += 1 - end - - if partial_match - partially_matched_conditions += 1 - end - else - raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}" - end - end - end - - if fully_matched_conditions == 1 && partially_matched_conditions == 0 - nil # OK - else - @message || build_message(context) - end - end - - def build_message(context) - argument_definitions = @validated.arguments(context).values - required_names = @one_of.map do |arg_keyword| - if arg_keyword.is_a?(Array) - names = arg_keyword.map { |arg| arg_keyword_to_grapqhl_name(argument_definitions, arg) } - "(" + names.join(" and ") + ")" - else - arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword) - end - end - - if required_names.size == 1 - "%{validated} must include the following argument: #{required_names.first}." - else - "%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}." - end - end - - def arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword) - argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword } - argument_definition.graphql_name - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/visibility.rb b/vendor/gems/graphql/lib/graphql/schema/visibility.rb deleted file mode 100644 index 4ffe1be408f..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/visibility.rb +++ /dev/null @@ -1,299 +0,0 @@ -# frozen_string_literal: true -require "graphql/schema/visibility/profile" -require "graphql/schema/visibility/migration" -require "graphql/schema/visibility/visit" - -module GraphQL - class Schema - # Use this plugin to make some parts of your schema hidden from some viewers. - # - class Visibility - # @param schema [Class] - # @param profiles [Hash Hash>] A hash of `name => context` pairs for preloading visibility profiles - # @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`) - # @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results - def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false) - profiles&.each { |name, ctx| - ctx[:visibility_profile] = name - ctx.freeze - } - schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors) - if preload - schema.visibility.preload - end - end - - def initialize(schema, dynamic:, preload:, profiles:, migration_errors:) - @schema = schema - schema.use_visibility_profile = true - schema.visibility_profile_class = if migration_errors - Visibility::Migration - else - Visibility::Profile - end - @preload = preload - @profiles = profiles - @cached_profiles = {} - @dynamic = dynamic - @migration_errors = migration_errors - # Top-level type caches: - @visit = nil - @interface_type_memberships = nil - @directives = nil - @types = nil - @all_references = nil - @loaded_all = false - end - - def all_directives - load_all - @directives - end - - def all_interface_type_memberships - load_all - @interface_type_memberships - end - - def all_references - load_all - @all_references - end - - def get_type(type_name) - load_all - @types[type_name] - end - - def preload? - @preload - end - - def preload - # Traverse the schema now (and in the *_configured hooks below) - # To make sure things are loaded during boot - @preloaded_types = Set.new - types_to_visit = [ - @schema.query, - @schema.mutation, - @schema.subscription, - *@schema.introspection_system.types.values, - *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap }, - *@schema.orphan_types, - ] - # Root types may have been nil: - types_to_visit.compact! - ensure_all_loaded(types_to_visit) - @profiles.each do |profile_name, example_ctx| - prof = profile_for(example_ctx) - prof.all_types # force loading - end - end - - # @api private - def query_configured(query_type) - if @preload - ensure_all_loaded([query_type]) - end - end - - # @api private - def mutation_configured(mutation_type) - if @preload - ensure_all_loaded([mutation_type]) - end - end - - # @api private - def subscription_configured(subscription_type) - if @preload - ensure_all_loaded([subscription_type]) - end - end - - # @api private - def orphan_types_configured(orphan_types) - if @preload - ensure_all_loaded(orphan_types) - end - end - - # @api private - def introspection_system_configured(introspection_system) - if @preload - introspection_types = [ - *@schema.introspection_system.types.values, - *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap }, - ] - ensure_all_loaded(introspection_types) - end - end - - # Make another Visibility for `schema` based on this one - # @return [Visibility] - # @api private - def dup_for(other_schema) - self.class.new( - other_schema, - dynamic: @dynamic, - preload: @preload, - profiles: @profiles, - migration_errors: @migration_errors - ) - end - - def migration_errors? - @migration_errors - end - - attr_reader :cached_profiles - - def profile_for(context, visibility_profile = context[:visibility_profile]) - if !@profiles.empty? - if visibility_profile.nil? - if @dynamic - if context.is_a?(Query::NullContext) - top_level_profile - else - @schema.visibility_profile_class.new(context: context, schema: @schema) - end - elsif !@profiles.empty? - raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration." - end - elsif !@profiles.include?(visibility_profile) - raise ArgumentError, "`#{visibility_profile.inspect}` isn't allowed for `visibility_profile:` (must be one of #{@profiles.keys.map(&:inspect).join(", ")}). Or, add `#{visibility_profile.inspect}` to the list of profiles in the schema definition." - else - profile_ctx = @profiles[visibility_profile] - @cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema) - end - elsif context.is_a?(Query::NullContext) - top_level_profile - else - @schema.visibility_profile_class.new(context: context, schema: @schema) - end - end - - attr_reader :top_level - - # @api private - attr_reader :unfiltered_interface_type_memberships - - def top_level_profile(refresh: false) - if refresh - @top_level_profile = nil - end - @top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema) - end - - private - - def ensure_all_loaded(types_to_visit) - while (type = types_to_visit.shift) - if type.kind.fields? && @preloaded_types.add?(type) - type.all_field_definitions.each do |field_defn| - field_defn.ensure_loaded - types_to_visit << field_defn.type.unwrap - end - end - end - top_level_profile(refresh: true) - nil - end - - def load_all(types: nil) - if @visit.nil? - # Set up the visit system - @interface_type_memberships = Hash.new { |h, interface_type| h[interface_type] = [] }.compare_by_identity - @directives = [] - @types = {} # String => Module - @all_references = Hash.new { |h, member| h[member] = Set.new.compare_by_identity }.compare_by_identity - @unions_for_references = Set.new - @visit = Visibility::Visit.new(@schema) do |member| - if member.is_a?(Module) - type_name = member.graphql_name - if (prev_t = @types[type_name]) - if prev_t.is_a?(Array) - prev_t << member - else - @types[type_name] = [member, prev_t] - end - else - @types[member.graphql_name] = member - end - member.directives.each { |dir| @all_references[dir.class] << member } - if member < GraphQL::Schema::Directive - @directives << member - elsif member.respond_to?(:interface_type_memberships) - member.interface_type_memberships.each do |itm| - @all_references[itm.abstract_type] << member - # `itm.object_type` may not actually be `member` if this implementation - # is inherited from a superclass - @interface_type_memberships[itm.abstract_type] << [itm, member] - end - elsif member < GraphQL::Schema::Union - @unions_for_references << member - end - elsif member.is_a?(GraphQL::Schema::Argument) - member.validate_default_value - @all_references[member.type.unwrap] << member - if !(dirs = member.directives).empty? - dir_owner = member.owner - if dir_owner.respond_to?(:owner) - dir_owner = dir_owner.owner - end - dirs.each { |dir| @all_references[dir.class] << dir_owner } - end - elsif member.is_a?(GraphQL::Schema::Field) - @all_references[member.type.unwrap] << member - if !(dirs = member.directives).empty? - dir_owner = member.owner - dirs.each { |dir| @all_references[dir.class] << dir_owner } - end - elsif member.is_a?(GraphQL::Schema::EnumValue) - if !(dirs = member.directives).empty? - dir_owner = member.owner - dirs.each { |dir| @all_references[dir.class] << dir_owner } - end - end - true - end - - @schema.root_types.each { |t| @all_references[t] << true } - @schema.introspection_system.types.each_value { |t| @all_references[t] << true } - @schema.directives.each_value { |dir_class| @all_references[dir_class] << true } - - @visit.visit_each(types: []) # visit default directives - end - - if types - @visit.visit_each(types: types, directives: []) - elsif @loaded_all == false - @loaded_all = true - @visit.visit_each - else - # already loaded all - return - end - - # TODO: somehow don't iterate over all these, - # only the ones that may have been modified - @interface_type_memberships.each do |int_type, type_membership_pairs| - referers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) } - if !referers.empty? - type_membership_pairs.each do |(type_membership, impl_type)| - # Add new items only: - @all_references[impl_type] |= referers - end - end - end - - @unions_for_references.each do |union_type| - refs = @all_references[union_type] - union_type.all_possible_types.each do |object_type| - @all_references[object_type] |= refs # Add new items - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/visibility/migration.rb b/vendor/gems/graphql/lib/graphql/schema/visibility/migration.rb deleted file mode 100644 index 8639fa141a8..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/visibility/migration.rb +++ /dev/null @@ -1,188 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Visibility - # You can use this to see how {GraphQL::Schema::Warden} and {GraphQL::Schema::Visibility::Profile} - # handle `.visible?` differently in your schema. - # - # It runs the same method on both implementations and raises an error when the results diverge. - # - # To fix the error, modify your schema so that both implementations return the same thing. - # Or, open an issue on GitHub to discuss the difference. - # - # This plugin adds overhead to runtime and may cause unexpected crashes -- **don't** use it in production! - # - # This plugin adds two keys to `context` when running: - # - # - `visibility_migration_running: true` - # - For the {Schema::Warden} which it instantiates, it adds `visibility_migration_warden_running: true`. - # - # Use those keys to modify your `visible?` behavior as needed. - # - # Also, in a pinch, you can set `skip_visibility_migration_error: true` in context to turn off this behavior per-query. - # (In that case, it uses {Profile} directly.) - # - # @example Adding this plugin - # - # use GraphQL::Schema::Visibility, migration_errors: true - # - class Migration < GraphQL::Schema::Visibility::Profile - class RuntimeTypesMismatchError < GraphQL::Error - def initialize(method_called, warden_result, profile_result, method_args) - super(<<~ERR) - Mismatch in types for `##{method_called}(#{method_args.map(&:inspect).join(", ")})`: - - #{compare_results(warden_result, profile_result)} - - Update your `.visible?` implementation to make these implementations return the same value. - - See: https://graphql-ruby.org/authorization/visibility_migration.html - ERR - end - - private - def compare_results(warden_result, profile_result) - if warden_result.is_a?(Array) && profile_result.is_a?(Array) - all_results = warden_result | profile_result - all_results.sort_by!(&:graphql_name) - - entries_text = all_results.map { |entry| "#{entry.graphql_name} (#{entry})"} - width = entries_text.map(&:size).max - yes = " ✔ " - no = " " - res = "".dup - res << "#{"Result".center(width)} Warden Profile \n" - all_results.each_with_index do |entry, idx| - res << "#{entries_text[idx].ljust(width)}#{warden_result.include?(entry) ? yes : no}#{profile_result.include?(entry) ? yes : no}\n" - end - res << "\n" - else - "- Warden returned: #{humanize(warden_result)}\n\n- Visibility::Profile returned: #{humanize(profile_result)}" - end - end - def humanize(val) - case val - when Array - "#{val.size}: #{val.map { |v| humanize(v) }.sort.inspect}" - when Module - if val.respond_to?(:graphql_name) - "#{val.graphql_name} (#{val.inspect})" - else - val.inspect - end - else - val.inspect - end - end - end - - def initialize(context:, schema:, name: nil) - @name = name - @skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash) - @profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema) - if !@skip_error - context[:visibility_migration_running] = true - warden_ctx_vals = context.to_h.dup - warden_ctx_vals[:visibility_migration_warden_running] = true - if schema.const_defined?(:WardenCompatSchema, false) # don't use a defn from a superclass - warden_schema = schema.const_get(:WardenCompatSchema, false) - else - warden_schema = Class.new(schema) - warden_schema.use_visibility_profile = false - # TODO public API - warden_schema.send(:add_type_and_traverse, [warden_schema.query, warden_schema.mutation, warden_schema.subscription].compact, root: true) - warden_schema.send(:add_type_and_traverse, warden_schema.directives.values + warden_schema.orphan_types, root: false) - schema.const_set(:WardenCompatSchema, warden_schema) - end - warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals) - warden_ctx.warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx) - warden_ctx.warden.skip_warning = true - warden_ctx.types = @warden_types = warden_ctx.warden.visibility_profile - end - end - - def loaded_types - @profile_types.loaded_types - end - - PUBLIC_PROFILE_METHODS = [ - :enum_values, - :interfaces, - :all_types, - :all_types_h, - :fields, - :loadable?, - :loadable_possible_types, - :type, - :arguments, - :argument, - :directive_exists?, - :directives, - :field, - :query_root, - :mutation_root, - :possible_types, - :subscription_root, - :reachable_type?, - :visible_enum_value?, - ] - - PUBLIC_PROFILE_METHODS.each do |profile_method| - define_method(profile_method) do |*args| - call_method_and_compare(profile_method, args) - end - end - - def call_method_and_compare(method, args) - res_1 = @profile_types.public_send(method, *args) - if @skip_error - return res_1 - end - - res_2 = @warden_types.public_send(method, *args) - normalized_res_1 = res_1.is_a?(Array) ? Set.new(res_1) : res_1 - normalized_res_2 = res_2.is_a?(Array) ? Set.new(res_2) : res_2 - if !equivalent_schema_members?(normalized_res_1, normalized_res_2) - # Raise the errors with the orignally returned values: - err = RuntimeTypesMismatchError.new(method, res_2, res_1, args) - raise err - else - res_1 - end - end - - def equivalent_schema_members?(member1, member2) - if member1.class != member2.class - return false - end - - case member1 - when Set - member1_array = member1.to_a.sort_by(&:graphql_name) - member2_array = member2.to_a.sort_by(&:graphql_name) - member1_array.each_with_index do |inner_member1, idx| - inner_member2 = member2_array[idx] - equivalent_schema_members?(inner_member1, inner_member2) - end - when GraphQL::Schema::Field - member1.ensure_loaded - member2.ensure_loaded - if member1.introspection? && member2.introspection? - member1.inspect == member2.inspect - else - member1 == member2 - end - when Module - if member1.introspection? && member2.introspection? - member1.graphql_name == member2.graphql_name - else - member1 == member2 - end - else - member1 == member2 - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/visibility/profile.rb b/vendor/gems/graphql/lib/graphql/schema/visibility/profile.rb deleted file mode 100644 index b88027e5c5e..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/visibility/profile.rb +++ /dev/null @@ -1,359 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Visibility - # This class filters the types, fields, arguments, enum values, and directives in a schema - # based on the given `context`. - # - # It's like {Warden}, but has some differences: - # - # - It doesn't use {Schema}'s top-level caches (eg {Schema.references_to}, {Schema.possible_types}, {Schema.types}) - # - It doesn't hide Interface or Union types when all their possible types are hidden. (Instead, those types should implement `.visible?` to hide in that case.) - # - It checks `.visible?` on root introspection types - # - It can be used to cache profiles by name for re-use across queries - class Profile - # @return [Schema::Visibility::Profile] - def self.from_context(ctx, schema) - if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self) - types - else - schema.visibility.profile_for(ctx) - end - end - - def self.null_profile(context:, schema:) - profile = self.new(name: "NullProfile", context: context, schema: schema) - profile.instance_variable_set(:@cached_visible, Hash.new { |k, v| k[v] = true }.compare_by_identity) - profile - end - - # @return [Symbol, nil] - attr_reader :name - - def initialize(name: nil, context:, schema:) - @name = name - @context = context - @schema = schema - @all_types = {} - @all_types_loaded = false - @unvisited_types = [] - @all_directives = nil - @cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity - - @cached_visible_fields = Hash.new { |h, owner| - h[owner] = Hash.new do |h2, field| - h2[field] = visible_field_for(owner, field) - end.compare_by_identity - }.compare_by_identity - - @cached_visible_arguments = Hash.new do |h, arg| - h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type] - true - else - false - end - end.compare_by_identity - - @cached_parent_fields = Hash.new do |h, type| - h[type] = Hash.new do |h2, field_name| - h2[field_name] = type.get_field(field_name, @context) - end - end.compare_by_identity - - @cached_parent_arguments = Hash.new do |h, arg_owner| - h[arg_owner] = Hash.new do |h2, arg_name| - h2[arg_name] = arg_owner.get_argument(arg_name, @context) - end - end.compare_by_identity - - @cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity - - @cached_enum_values = Hash.new do |h, enum_t| - values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible) - if values.size == 0 - raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t) - end - h[enum_t] = values - end.compare_by_identity - - @cached_fields = Hash.new do |h, owner| - h[owner] = non_duplicate_items(owner.all_field_definitions.each(&:ensure_loaded), @cached_visible_fields[owner]) - end.compare_by_identity - - @cached_arguments = Hash.new do |h, owner| - h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments) - end.compare_by_identity - - @loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity - end - - def field_on_visible_interface?(field, owner) - ints = owner.interface_type_memberships.map(&:abstract_type) - field_name = field.graphql_name - filtered_ints = interfaces(owner) - any_interface_has_field = false - any_interface_has_visible_field = false - ints.each do |int_t| - if (_int_f_defn = @cached_parent_fields[int_t][field_name]) - any_interface_has_field = true - - if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field] - any_interface_has_visible_field = true - break - end - end - end - - if any_interface_has_field - any_interface_has_visible_field - else - true - end - end - - def type(type_name) - t = @schema.visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop - if t - if t.is_a?(Array) - vis_t = nil - t.each do |t_defn| - if @cached_visible[t_defn] && referenced?(t_defn) - if vis_t.nil? - vis_t = t_defn - else - raise_duplicate_definition(vis_t, t_defn) - end - end - end - vis_t - else - if t && @cached_visible[t] && referenced?(t) - t - else - nil - end - end - end - end - - def field(owner, field_name) - f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name]) - field - elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name)) - entry_point_field - elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name)) - dynamic_field - else - nil - end - if f.is_a?(Array) - visible_f = nil - f.each do |f_defn| - if @cached_visible_fields[owner][f_defn] - - if visible_f.nil? - visible_f = f_defn - else - raise_duplicate_definition(visible_f, f_defn) - end - end - end - visible_f&.ensure_loaded - elsif f && @cached_visible_fields[owner][f.ensure_loaded] - f - else - nil - end - end - - def fields(owner) - @cached_fields[owner] - end - - def arguments(owner) - @cached_arguments[owner] - end - - def argument(owner, arg_name) - arg = @cached_parent_arguments[owner][arg_name] - if arg.is_a?(Array) - visible_arg = nil - arg.each do |arg_defn| - if @cached_visible_arguments[arg_defn] - if visible_arg.nil? - visible_arg = arg_defn - else - raise_duplicate_definition(visible_arg, arg_defn) - end - end - end - visible_arg - else - if arg && @cached_visible_arguments[arg] - arg - else - nil - end - end - end - - def possible_types(type) - @cached_possible_types[type] - end - - def interfaces(obj_or_int_type) - ints = obj_or_int_type.interface_type_memberships - .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] } - .map!(&:abstract_type) - ints.uniq! # Remove any duplicate interfaces implemented via other interfaces - ints - end - - def query_root - ((t = @schema.query) && @cached_visible[t]) ? t : nil - end - - def mutation_root - ((t = @schema.mutation) && @cached_visible[t]) ? t : nil - end - - def subscription_root - ((t = @schema.subscription) && @cached_visible[t]) ? t : nil - end - - def all_types - load_all_types - @all_types.values - end - - def all_types_h - load_all_types - @all_types - end - - def enum_values(owner) - @cached_enum_values[owner] - end - - def directive_exists?(dir_name) - directives.any? { |d| d.graphql_name == dir_name } - end - - def directives - @all_directives ||= @schema.visibility.all_directives.select { |dir| - @cached_visible[dir] && @schema.visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) } - } - end - - def loadable?(t, _ctx) - load_all_types - !@all_types[t.graphql_name] && @cached_visible[t] - end - - def loadable_possible_types(t, _ctx) - @loadable_possible_types[t] - end - - def loaded_types - @all_types.values - end - - def reachable_type?(type_name) - load_all_types - !!@all_types[type_name] - end - - def visible_enum_value?(enum_value, _ctx = nil) - @cached_visible[enum_value] - end - - private - - def non_duplicate_items(definitions, visibility_cache) - non_dups = [] - definitions.each do |defn| - if visibility_cache[defn] - if (dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name }) - raise_duplicate_definition(dup_defn, defn) - end - non_dups << defn - end - end - non_dups - end - - def raise_duplicate_definition(first_defn, second_defn) - raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect) - end - - def load_all_types - return if @all_types_loaded - @all_types_loaded = true - visit = Visibility::Visit.new(@schema) do |member| - if member.is_a?(Module) && member.respond_to?(:kind) - if @cached_visible[member] - type_name = member.graphql_name - if (prev_t = @all_types[type_name]) && !prev_t.equal?(member) - raise_duplicate_definition(member, prev_t) - end - @all_types[type_name] = member - true - else - false - end - else - @cached_visible[member] - end - end - visit.visit_each - @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) } - nil - end - - def referenced?(type_defn) - @schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] } - end - - def possible_types_for(type) - case type.kind.name - when "INTERFACE" - pts = [] - @schema.visibility.all_interface_type_memberships[type].each do |(itm, impl_type)| - if @cached_visible[itm] && @cached_visible[impl_type] && referenced?(impl_type) - pts << impl_type - end - end - pts - when "UNION" - pts = [] - type.type_memberships.each { |tm| - if @cached_visible[tm] && - (ot = tm.object_type) && - @cached_visible[ot] && - referenced?(ot) - pts << ot - end - } - pts - when "OBJECT" - if @cached_visible[type] - [type] - else - EmptyObjects::EMPTY_ARRAY - end - else - GraphQL::EmptyObjects::EMPTY_ARRAY - end - end - - def visible_field_for(owner, field) - @cached_visible[field] && - (ret_type = field.type.unwrap) && - @cached_visible[ret_type] && - (owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner)) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/visibility/visit.rb b/vendor/gems/graphql/lib/graphql/schema/visibility/visit.rb deleted file mode 100644 index 51c3e1e8c6d..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/visibility/visit.rb +++ /dev/null @@ -1,190 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Schema - class Visibility - class Visit - def initialize(schema, &visit_block) - @schema = schema - @late_bound_types = nil - @unvisited_types = nil - # These accumulate between calls to prevent re-visiting the same types - @visited_types = Set.new.compare_by_identity - @visited_directives = Set.new.compare_by_identity - @visit_block = visit_block - end - - def entry_point_types - ept = [ - @schema.query, - @schema.mutation, - @schema.subscription, - *@schema.introspection_system.types.values, - *@schema.orphan_types, - ] - ept.compact! - ept - end - - def entry_point_directives - @schema.directives.values - end - - def visit_each(types: entry_point_types, directives: entry_point_directives) - @unvisited_types && raise("Can't re-enter `visit_each` on this Visit (another visit is already in progress)") - @unvisited_types = types - @late_bound_types = [] - directives_to_visit = directives - - while !@unvisited_types.empty? || !@late_bound_types.empty? - while (type = @unvisited_types.pop) - if @visited_types.add?(type) && @visit_block.call(type) - directives_to_visit.concat(type.directives) - case type.kind.name - when "OBJECT", "INTERFACE" - type.interface_type_memberships.each do |itm| - append_unvisited_type(type, itm.abstract_type) - end - if type.kind.interface? - type.orphan_types.each do |orphan_type| - append_unvisited_type(type, orphan_type) - end - end - - type.all_field_definitions.each do |field| - field.ensure_loaded - if @visit_block.call(field) - directives_to_visit.concat(field.directives) - append_unvisited_type(type, field.type.unwrap) - field.all_argument_definitions.each do |argument| - if @visit_block.call(argument) - directives_to_visit.concat(argument.directives) - append_unvisited_type(field, argument.type.unwrap) - end - end - end - end - when "INPUT_OBJECT" - type.all_argument_definitions.each do |argument| - if @visit_block.call(argument) - directives_to_visit.concat(argument.directives) - append_unvisited_type(type, argument.type.unwrap) - end - end - when "UNION" - type.type_memberships.each do |tm| - append_unvisited_type(type, tm.object_type) - end - when "ENUM" - type.all_enum_value_definitions.each do |val| - if @visit_block.call(val) - directives_to_visit.concat(val.directives) - end - end - when "SCALAR" - # pass -- nothing else to visit - else - raise "Invariant: unhandled type kind: #{type.kind.inspect}" - end - end - end - - directives_to_visit.each do |dir| - dir_class = dir.is_a?(Class) ? dir : dir.class - if @visited_directives.add?(dir_class) && @visit_block.call(dir_class) - dir_class.all_argument_definitions.each do |arg_defn| - if @visit_block.call(arg_defn) - directives_to_visit.concat(arg_defn.directives) - append_unvisited_type(dir_class, arg_defn.type.unwrap) - end - end - end - end - - missed_late_types_streak = 0 - while (owner, late_type = @late_bound_types.shift) - if (late_type.is_a?(String) && (type = Member::BuildType.constantize(type))) || - (late_type.is_a?(LateBoundType) && (type = @visited_types.find { |t| t.graphql_name == late_type.graphql_name })) - missed_late_types_streak = 0 # might succeed next round - update_type_owner(owner, type) - append_unvisited_type(owner, type) - else - # Didn't find it -- keep trying - missed_late_types_streak += 1 - @late_bound_types << [owner, late_type] - if missed_late_types_streak == @late_bound_types.size - raise UnresolvedLateBoundTypeError.new(type: late_type) - end - end - end - end - - @unvisited_types = nil - nil - end - - private - - def append_unvisited_type(owner, type) - if type.is_a?(LateBoundType) || type.is_a?(String) - @late_bound_types << [owner, type] - else - @unvisited_types << type - end - end - - def update_type_owner(owner, type) - case owner - when Module - if owner.kind.union? - owner.assign_type_membership_object_type(type) - elsif type.kind.interface? - new_interfaces = [] - owner.interfaces.each do |int_t| - if int_t.is_a?(String) && int_t == type.graphql_name - new_interfaces << type - elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name - new_interfaces << type - else - # Don't re-add proper interface definitions, - # they were probably already added, maybe with options. - end - end - owner.implements(*new_interfaces) - new_interfaces.each do |int| - pt = @possible_types[int] ||= [] - if !pt.include?(owner) && owner.is_a?(Class) - pt << owner - end - int.interfaces.each do |indirect_int| - if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name)) # rubocop:disable Development/ContextIsPassedCop - update_type_owner(owner, indirect_int_type) - end - end - end - end - when GraphQL::Schema::Argument, GraphQL::Schema::Field - orig_type = owner.type - # Apply list/non-null wrapper as needed - if orig_type.respond_to?(:of_type) - transforms = [] - while (orig_type.respond_to?(:of_type)) - if orig_type.kind.non_null? - transforms << :to_non_null_type - elsif orig_type.kind.list? - transforms << :to_list_type - else - raise "Invariant: :of_type isn't non-null or list" - end - orig_type = orig_type.of_type - end - transforms.reverse_each { |t| type = type.public_send(t) } - end - owner.type = type - else - raise "Unexpected update: #{owner.inspect} #{type.inspect}" - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/warden.rb b/vendor/gems/graphql/lib/graphql/schema/warden.rb deleted file mode 100644 index 16be5149780..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/warden.rb +++ /dev/null @@ -1,634 +0,0 @@ -# frozen_string_literal: true - -require 'set' - -module GraphQL - class Schema - # Restrict access to a {GraphQL::Schema} with a user-defined `visible?` implementations. - # - # When validating and executing a query, all access to schema members - # should go through a warden. If you access the schema directly, - # you may show a client something that it shouldn't be allowed to see. - # - # @api private - class Warden - def self.from_context(context) - context.warden || PassThruWarden - rescue NoMethodError - # this might be a hash which won't respond to #warden - PassThruWarden - end - - def self.types_from_context(context) - context.types || PassThruWarden - rescue NoMethodError - # this might be a hash which won't respond to #warden - PassThruWarden - end - - def self.use(schema) - # no-op - end - - # @param visibility_method [Symbol] a Warden method to call for this entry - # @param entry [Object, Array] One or more definitions for a given name in a GraphQL Schema - # @param context [GraphQL::Query::Context] - # @param warden [Warden] - # @return [Object] `entry` or one of `entry`'s items if exactly one of them is visible for this context - # @return [nil] If neither `entry` nor any of `entry`'s items are visible for this context - def self.visible_entry?(visibility_method, entry, context, warden = Warden.from_context(context)) - if entry.is_a?(Array) - visible_item = nil - entry.each do |item| - if warden.public_send(visibility_method, item, context) - if visible_item.nil? - visible_item = item - else - raise DuplicateNamesError.new( - duplicated_name: item.path, duplicated_definition_1: visible_item.inspect, duplicated_definition_2: item.inspect - ) - end - end - end - visible_item - elsif warden.public_send(visibility_method, entry, context) - entry - else - nil - end - end - - # This is used when a caller provides a Hash for context. - # We want to call the schema's hooks, but we don't have a full-blown warden. - # The `context` arguments to these methods exist purely to simplify the code that - # calls methods on this object, so it will have everything it needs. - class PassThruWarden - class << self - def visible_field?(field, ctx); field.visible?(ctx); end - def visible_argument?(arg, ctx); arg.visible?(ctx); end - def visible_type?(type, ctx); type.visible?(ctx); end - def visible_enum_value?(ev, ctx); ev.visible?(ctx); end - def visible_type_membership?(tm, ctx); tm.visible?(ctx); end - def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end - def arguments(owner, ctx); owner.arguments(ctx); end - def loadable?(type, ctx); type.visible?(ctx); end - def loadable_possible_types(type, ctx); type.possible_types(ctx); end - def visibility_profile - @visibility_profile ||= Warden::VisibilityProfile.new(self) - end - end - end - - class NullWarden - def initialize(_filter = nil, context:, schema:) - @schema = schema - @visibility_profile = Warden::VisibilityProfile.new(self) - end - - # No-op, but for compatibility: - attr_writer :skip_warning - - attr_reader :visibility_profile - - def visible_field?(field_defn, _ctx = nil, owner = nil); true; end - def visible_argument?(arg_defn, _ctx = nil); true; end - def visible_type?(type_defn, _ctx = nil); true; end - def visible_enum_value?(enum_value, _ctx = nil); enum_value.visible?(Query::NullContext.instance); end - def visible_type_membership?(type_membership, _ctx = nil); true; end - def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end - def get_type(type_name); @schema.get_type(type_name, Query::NullContext.instance, false); end # rubocop:disable Development/ContextIsPassedCop - def arguments(argument_owner, ctx = nil); argument_owner.all_argument_definitions; end - def enum_values(enum_defn); enum_defn.enum_values(Query::NullContext.instance); end # rubocop:disable Development/ContextIsPassedCop - def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop - def types; @schema.types; end # rubocop:disable Development/ContextIsPassedCop - def root_type_for_operation(op_name); @schema.root_type_for_operation(op_name); end - def directives; @schema.directives.values; end - def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop - def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end - def reachable_type?(type_name); true; end - def loadable?(type, _ctx); true; end - def loadable_possible_types(union_type, _ctx); union_type.possible_types; end - def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop - def possible_types(type_defn); @schema.possible_types(type_defn, Query::NullContext.instance, false); end - def interfaces(obj_type); obj_type.interfaces; end - end - - def visibility_profile - @visibility_profile ||= VisibilityProfile.new(self) - end - - class VisibilityProfile - def initialize(warden) - @warden = warden - end - - def directives - @warden.directives - end - - def directive_exists?(dir_name) - @warden.directives.any? { |d| d.graphql_name == dir_name } - end - - def type(name) - @warden.get_type(name) - end - - def field(owner, field_name) - @warden.get_field(owner, field_name) - end - - def argument(owner, arg_name) - @warden.get_argument(owner, arg_name) - end - - def query_root - @warden.root_type_for_operation("query") - end - - def mutation_root - @warden.root_type_for_operation("mutation") - end - - def subscription_root - @warden.root_type_for_operation("subscription") - end - - def arguments(owner) - @warden.arguments(owner) - end - - def fields(owner) - @warden.fields(owner) - end - - def possible_types(type) - @warden.possible_types(type) - end - - def enum_values(enum_type) - @warden.enum_values(enum_type) - end - - def all_types - @warden.reachable_types - end - - def interfaces(obj_type) - @warden.interfaces(obj_type) - end - - def loadable?(t, ctx) # TODO remove ctx here? - @warden.loadable?(t, ctx) - end - - def loadable_possible_types(t, ctx) - @warden.loadable_possible_types(t, ctx) - end - - def reachable_type?(type_name) - !!@warden.reachable_type?(type_name) - end - - def visible_enum_value?(enum_value, ctx = nil) - @warden.visible_enum_value?(enum_value, ctx) - end - end - - # @param context [GraphQL::Query::Context] - # @param schema [GraphQL::Schema] - def initialize(context:, schema:) - @schema = schema - # Cache these to avoid repeated hits to the inheritance chain when one isn't present - @query = @schema.query - @mutation = @schema.mutation - @subscription = @schema.subscription - @context = context - @visibility_cache = read_through { |m| check_visible(schema, m) } - # Initialize all ivars to improve object shape consistency: - @types = @visible_types = @reachable_types = @visible_parent_fields = - @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays = - @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships = - @visible_and_reachable_type = @unions = @unfiltered_interfaces = - @reachable_type_set = @visibility_profile = @loadable_possible_types = - nil - @skip_warning = schema.plugins.any? { |(plugin, _opts)| plugin == GraphQL::Schema::Warden } - end - - attr_writer :skip_warning - - # @return [Hash] Visible types in the schema - def types - @types ||= begin - vis_types = {} - @schema.types(@context).each do |n, t| - if visible_and_reachable_type?(t) - vis_types[n] = t - end - end - vis_types - end - end - - # @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden. - def loadable?(type, _ctx) - !reachable_type_set.include?(type) && visible_type?(type) - end - - def loadable_possible_types(union_type, _ctx) - @loadable_possible_types ||= read_through do |t| - t.possible_types # unfiltered - end - @loadable_possible_types[union_type] - end - - # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`) - def get_type(type_name) - @visible_types ||= read_through do |name| - type_defn = @schema.get_type(name, @context, false) - if type_defn && visible_and_reachable_type?(type_defn) - type_defn - else - nil - end - end - - @visible_types[type_name] - end - - # @return [Array] Visible and reachable types in the schema - def reachable_types - @reachable_types ||= reachable_type_set.to_a - end - - # @return Boolean True if the type is visible and reachable in the schema - def reachable_type?(type_name) - type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware - type && reachable_type_set.include?(type) - end - - # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`, if it exists - def get_field(parent_type, field_name) - @visible_parent_fields ||= read_through do |type| - read_through do |f_name| - field_defn = @schema.get_field(type, f_name, @context) - if field_defn && visible_field?(field_defn, nil, type) - field_defn - else - nil - end - end - end - - @visible_parent_fields[parent_type][field_name] - end - - # @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible - def get_argument(parent_type, argument_name) - argument = parent_type.get_argument(argument_name, @context) - return argument if argument && visible_argument?(argument, @context) - end - - # @return [Array] The types which may be member of `type_defn` - def possible_types(type_defn) - @visible_possible_types ||= read_through { |type_defn| - pt = @schema.possible_types(type_defn, @context, false) - pt.select { |t| visible_and_reachable_type?(t) } - } - @visible_possible_types[type_defn] - end - - # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType] - # @return [Array] Fields on `type_defn` - def fields(type_defn) - @visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values } - @visible_fields[type_defn] - end - - # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType] - # @return [Array] Visible arguments on `argument_owner` - def arguments(argument_owner, ctx = nil) - @visible_arguments ||= read_through { |o| - args = o.arguments(@context) - if !args.empty? - args = args.values - args.select! { |a| visible_argument?(a, @context) } - args - else - EmptyObjects::EMPTY_ARRAY - end - } - @visible_arguments[argument_owner] - end - - # @return [Array] Visible members of `enum_defn` - def enum_values(enum_defn) - @visible_enum_arrays ||= read_through { |e| - values = e.enum_values(@context) - if values.size == 0 - raise GraphQL::Schema::Enum::MissingValuesError.new(e) - end - values - } - @visible_enum_arrays[enum_defn] - end - - def visible_enum_value?(enum_value, _ctx = nil) - @visible_enum_values ||= read_through { |ev| visible?(ev) } - @visible_enum_values[enum_value] - end - - # @return [Array] Visible interfaces implemented by `obj_type` - def interfaces(obj_type) - @visible_interfaces ||= read_through { |t| - ints = t.interfaces(@context) - if !ints.empty? - ints.select! { |i| visible_type?(i) } - end - ints - } - @visible_interfaces[obj_type] - end - - def directives - @schema.directives.each_value.select { |d| visible?(d) } - end - - def root_type_for_operation(op_name) - root_type = @schema.root_type_for_operation(op_name) - if root_type && visible?(root_type) - root_type - else - nil - end - end - - # @param owner [Class, Module] If provided, confirm that field has the given owner. - def visible_field?(field_defn, _ctx = nil, owner = field_defn.owner) - # This field is visible in its own right - visible?(field_defn) && - # This field's return type is visible - visible_and_reachable_type?(field_defn.type.unwrap) && - # This field is either defined on this object type, - # or the interface it's inherited from is also visible - ((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner)) - end - - def visible_argument?(arg_defn, _ctx = nil) - visible?(arg_defn) && visible_and_reachable_type?(arg_defn.type.unwrap) - end - - def visible_type?(type_defn, _ctx = nil) - @type_visibility ||= read_through { |type_defn| visible?(type_defn) } - @type_visibility[type_defn] - end - - def visible_type_membership?(type_membership, _ctx = nil) - visible?(type_membership) - end - - def interface_type_memberships(obj_type, _ctx = nil) - @type_memberships ||= read_through do |obj_t| - obj_t.interface_type_memberships - end - @type_memberships[obj_type] - end - - private - - def visible_and_reachable_type?(type_defn) - @visible_and_reachable_type ||= read_through do |type_defn| - next false unless visible_type?(type_defn) - next true if root_type?(type_defn) || type_defn.introspection? - - if type_defn.kind.union? - !possible_types(type_defn).empty? && (referenced?(type_defn) || orphan_type?(type_defn)) - elsif type_defn.kind.interface? - if !possible_types(type_defn).empty? - true - else - if @context.respond_to?(:logger) && (logger = @context.logger) - logger.debug { "Interface `#{type_defn.graphql_name}` hidden because it has no visible implementers" } - end - false - end - else - if referenced?(type_defn) - true - elsif type_defn.kind.object? - # Show this object if it belongs to ... - interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema - union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types - else - false - end - end - end - - @visible_and_reachable_type[type_defn] - end - - def union_memberships(obj_type) - @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } } - @unions[obj_type] - end - - # We need this to tell whether a field was inherited by an interface - # even when that interface is hidden from `#interfaces` - def unfiltered_interfaces(type_defn) - @unfiltered_interfaces ||= read_through(&:interfaces) - @unfiltered_interfaces[type_defn] - end - - # If this field was inherited from an interface, and the field on that interface is _hidden_, - # then treat this inherited field as hidden. - # (If it _wasn't_ inherited, then don't hide it for this reason.) - def field_on_visible_interface?(field_defn, type_defn) - if type_defn.kind.object? - any_interface_has_field = false - any_interface_has_visible_field = false - ints = unfiltered_interfaces(type_defn) - ints.each do |interface_type| - if (iface_field_defn = interface_type.get_field(field_defn.graphql_name, @context)) - any_interface_has_field = true - - if interfaces(type_defn).include?(interface_type) && visible_field?(iface_field_defn, nil, interface_type) - any_interface_has_visible_field = true - end - end - end - - if any_interface_has_field - any_interface_has_visible_field - else - # it's the object's own field - true - end - else - true - end - end - - def root_type?(type_defn) - @query == type_defn || - @mutation == type_defn || - @subscription == type_defn - end - - def referenced?(type_defn) - members = @schema.references_to(type_defn) - members.any? { |m| visible?(m) } - end - - def orphan_type?(type_defn) - @schema.orphan_types.include?(type_defn) - end - - def visible?(member) - @visibility_cache[member] - end - - def read_through - Hash.new { |h, k| h[k] = yield(k) }.compare_by_identity - end - - def check_visible(schema, member) - if schema.visible?(member, @context) - true - elsif @skip_warning - false - else - member_s = member.respond_to?(:path) ? member.path : member.inspect - member_type = case member - when Module - if member.respond_to?(:kind) - member.kind.name.downcase - else - "" - end - when GraphQL::Schema::Field - "field" - when GraphQL::Schema::EnumValue - "enum value" - when GraphQL::Schema::Argument - "argument" - else - "" - end - - schema_s = schema.name ? "#{schema.name}'s" : "" - schema_name = schema.name ? "#{schema.name}" : "your schema" - warn(ADD_WARDEN_WARNING % { schema_s: schema_s, schema_name: schema_name, member: member_s, member_type: member_type }) - @skip_warning = true # only warn once per query - # If there's no schema name, add the backtrace for additional context: - if schema_s == "" - puts caller.map { |l| " #{l}"} - end - false - end - end - - ADD_WARDEN_WARNING = <<~WARNING -DEPRECATION: %{schema_s} "%{member}" %{member_type} returned `false` for `.visible?` but `GraphQL::Schema::Visibility` isn't configured yet. - - Address this warning by adding: - - use GraphQL::Schema::Visibility - - to the definition for %{schema_name}. (Future GraphQL-Ruby versions won't check `.visible?` methods by default.) - - Alternatively, for legacy behavior, add: - - use GraphQL::Schema::Warden # legacy visibility behavior - - For more information see: https://graphql-ruby.org/authorization/visibility.html - WARNING - - def reachable_type_set - return @reachable_type_set if @reachable_type_set - - @reachable_type_set = Set.new - rt_hash = {} - - unvisited_types = [] - ['query', 'mutation', 'subscription'].each do |op_name| - root_type = root_type_for_operation(op_name) - unvisited_types << root_type if root_type - end - unvisited_types.concat(@schema.introspection_system.types.values) - - directives.each do |dir_class| - arguments(dir_class).each do |arg_defn| - arg_t = arg_defn.type.unwrap - if get_type(arg_t.graphql_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware - unvisited_types << arg_t - end - end - end - - @schema.orphan_types.each do |orphan_type| - if get_type(orphan_type.graphql_name) == orphan_type # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware - unvisited_types << orphan_type - end - end - - included_interface_possible_types_set = Set.new - - until unvisited_types.empty? - type = unvisited_types.pop - visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false) - end - - @reachable_type_set - end - - def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:) - if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type)) - type_by_name = type_by_name_hash[type.graphql_name] ||= type - if type_by_name != type - name_1, name_2 = [type.inspect, type_by_name.inspect].sort - raise DuplicateNamesError.new( - duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2 - ) - end - if type.kind.input_object? - # recurse into visible arguments - arguments(type).each do |argument| - argument_type = argument.type.unwrap - unvisited_types << argument_type - end - elsif type.kind.union? - # recurse into visible possible types - possible_types(type).each do |possible_type| - unvisited_types << possible_type - end - elsif type.kind.fields? - if type.kind.object? - # recurse into visible implemented interfaces - interfaces(type).each do |interface| - unvisited_types << interface - end - elsif include_interface_possible_types - possible_types(type).each do |pt| - unvisited_types << pt - end - end - # Don't visit interface possible types -- it's not enough to justify visibility - - # recurse into visible fields - fields(type).each do |field| - field_type = field.type.unwrap - # In this case, if it's an interface, we want to include - visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true) - # recurse into visible arguments - arguments(field).each do |argument| - argument_type = argument.type.unwrap - unvisited_types << argument_type - end - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/schema/wrapper.rb b/vendor/gems/graphql/lib/graphql/schema/wrapper.rb deleted file mode 100644 index 28f6d4fa42d..00000000000 --- a/vendor/gems/graphql/lib/graphql/schema/wrapper.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Schema - class Wrapper - include GraphQL::Schema::Member::TypeSystemHelpers - - # @return [Class, Module] The inner type of this wrapping type, the type of which one or more objects may be present. - attr_reader :of_type - - def initialize(of_type) - @of_type = of_type - end - - def unwrap - @of_type.unwrap - end - - def ==(other) - self.class == other.class && of_type == other.of_type - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation.rb b/vendor/gems/graphql/lib/graphql/static_validation.rb deleted file mode 100644 index 43d923e937d..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -require "graphql/static_validation/error" -require "graphql/static_validation/definition_dependencies" -require "graphql/static_validation/validator" -require "graphql/static_validation/validation_context" -require "graphql/static_validation/validation_timeout_error" -require "graphql/static_validation/literal_validator" -require "graphql/static_validation/base_visitor" - -rules_glob = File.expand_path("../static_validation/rules/*.rb", __FILE__) -Dir.glob(rules_glob).each do |file| - require(file) -end - -require "graphql/static_validation/all_rules" -require "graphql/static_validation/interpreter_visitor" diff --git a/vendor/gems/graphql/lib/graphql/static_validation/all_rules.rb b/vendor/gems/graphql/lib/graphql/static_validation/all_rules.rb deleted file mode 100644 index 6c6c8b8df8b..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/all_rules.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # Default rules for {GraphQL::StaticValidation::Validator} - # - # Order is important here. Some validators skip later hooks. - # which stops the visit on that node. That way it doesn't try to find fields on types that - # don't exist, etc. - ALL_RULES = [ - GraphQL::StaticValidation::NoDefinitionsArePresent, - GraphQL::StaticValidation::DirectivesAreDefined, - GraphQL::StaticValidation::DirectivesAreInValidLocations, - GraphQL::StaticValidation::UniqueDirectivesPerLocation, - GraphQL::StaticValidation::OperationNamesAreValid, - GraphQL::StaticValidation::FragmentNamesAreUnique, - GraphQL::StaticValidation::FragmentsAreFinite, - GraphQL::StaticValidation::FragmentsAreNamed, - GraphQL::StaticValidation::FragmentsAreUsed, - GraphQL::StaticValidation::FragmentTypesExist, - GraphQL::StaticValidation::FragmentsAreOnCompositeTypes, - GraphQL::StaticValidation::FragmentSpreadsArePossible, - GraphQL::StaticValidation::FieldsAreDefinedOnType, - GraphQL::StaticValidation::FieldsWillMerge, - GraphQL::StaticValidation::FieldsHaveAppropriateSelections, - GraphQL::StaticValidation::ArgumentsAreDefined, - GraphQL::StaticValidation::ArgumentLiteralsAreCompatible, - GraphQL::StaticValidation::RequiredArgumentsArePresent, - GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent, - GraphQL::StaticValidation::ArgumentNamesAreUnique, - GraphQL::StaticValidation::VariableNamesAreUnique, - GraphQL::StaticValidation::VariablesAreInputTypes, - GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped, - GraphQL::StaticValidation::VariablesAreUsedAndDefined, - GraphQL::StaticValidation::VariableUsagesAreAllowed, - GraphQL::StaticValidation::MutationRootExists, - GraphQL::StaticValidation::QueryRootExists, - GraphQL::StaticValidation::SubscriptionRootExists, - GraphQL::StaticValidation::InputObjectNamesAreUnique, - GraphQL::StaticValidation::OneOfInputObjectsAreValid, - ] - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/base_visitor.rb b/vendor/gems/graphql/lib/graphql/static_validation/base_visitor.rb deleted file mode 100644 index a73ee6566b6..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/base_visitor.rb +++ /dev/null @@ -1,201 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class BaseVisitor < GraphQL::Language::StaticVisitor - def initialize(document, context) - @path = [] - @object_types = [] - @directives = [] - @field_definitions = [] - @argument_definitions = [] - @directive_definitions = [] - @context = context - @types = context.query.types - @schema = context.schema - super(document) - end - - attr_reader :context - - # @return [Array] Types whose scope we've entered - attr_reader :object_types - - # @return [Array] The nesting of the current position in the AST - def path - @path.dup - end - - # Build a class to visit the AST and perform validation, - # or use a pre-built class if rules is `ALL_RULES` or empty. - # @param rules [Array] - # @return [Class] A class for validating `rules` during visitation - def self.including_rules(rules) - if rules.empty? - # It's not doing _anything?!?_ - BaseVisitor - elsif rules == ALL_RULES - InterpreterVisitor - else - visitor_class = Class.new(self) do - include(GraphQL::StaticValidation::DefinitionDependencies) - end - - rules.reverse_each do |r| - # If it's a class, it gets attached later. - if !r.is_a?(Class) - visitor_class.include(r) - end - end - - visitor_class.include(ContextMethods) - visitor_class - end - end - - module ContextMethods - def on_operation_definition(node, parent) - object_type = @schema.root_type_for_operation(node.operation_type) - push_type(object_type) - @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}") - super - @object_types.pop - @path.pop - end - - def on_fragment_definition(node, parent) - on_fragment_with_type(node) do - @path.push("fragment #{node.name}") - super - end - end - - def on_inline_fragment(node, parent) - on_fragment_with_type(node) do - @path.push("...#{node.type ? " on #{node.type.to_query_string}" : ""}") - super - end - end - - def on_field(node, parent) - parent_type = @object_types.last - field_definition = @types.field(parent_type, node.name) - @field_definitions.push(field_definition) - if !field_definition.nil? - next_object_type = field_definition.type.unwrap - push_type(next_object_type) - else - push_type(nil) - end - @path.push(node.alias || node.name) - super - @field_definitions.pop - @object_types.pop - @path.pop - end - - def on_directive(node, parent) - directive_defn = @context.schema_directives[node.name] - @directive_definitions.push(directive_defn) - super - @directive_definitions.pop - end - - def on_argument(node, parent) - argument_defn = if (arg = @argument_definitions.last) - arg_type = arg.type.unwrap - if arg_type.kind.input_object? - @types.argument(arg_type, node.name) - else - nil - end - elsif (directive_defn = @directive_definitions.last) - @types.argument(directive_defn, node.name) - elsif (field_defn = @field_definitions.last) - @types.argument(field_defn, node.name) - else - nil - end - - @argument_definitions.push(argument_defn) - @path.push(node.name) - super - @argument_definitions.pop - @path.pop - end - - def on_fragment_spread(node, parent) - @path.push("... #{node.name}") - super - @path.pop - end - - def on_input_object(node, parent) - arg_defn = @argument_definitions.last - if arg_defn && arg_defn.type.list? - @path.push(parent.children.index(node)) - super - @path.pop - else - super - end - end - - # @return [GraphQL::BaseType] The current object type - def type_definition - @object_types.last - end - - # @return [GraphQL::BaseType] The type which the current type came from - def parent_type_definition - @object_types[-2] - end - - # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one - def field_definition - @field_definitions.last - end - - # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one - def directive_definition - @directive_definitions.last - end - - # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one - def argument_definition - # Don't get the _last_ one because that's the current one. - # Get the second-to-last one, which is the parent of the current one. - @argument_definitions[-2] - end - - private - - def on_fragment_with_type(node) - object_type = if node.type - @types.type(node.type.name) - else - @object_types.last - end - push_type(object_type) - yield(node) - @object_types.pop - @path.pop - end - - def push_type(t) - @object_types.push(t) - end - end - - private - - def add_error(error, path: nil) - if @context.too_many_errors? - throw :too_many_validation_errors - end - error.path ||= (path || @path.dup) - context.errors << error - end - - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/definition_dependencies.rb b/vendor/gems/graphql/lib/graphql/static_validation/definition_dependencies.rb deleted file mode 100644 index 2ef2e402b24..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/definition_dependencies.rb +++ /dev/null @@ -1,204 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # Track fragment dependencies for operations - # and expose the fragment definitions which - # are used by a given operation - module DefinitionDependencies - attr_reader :dependencies - - def initialize(*) - super - @defdep_node_paths = {} - - # { name => [node, ...] } pairs for fragments (although duplicate-named fragments are _invalid_, they are _possible_) - @defdep_fragment_definitions = Hash.new{ |h, k| h[k] = [] } - - # This tracks dependencies from fragment to Node where it was used - # { fragment_definition_name => [dependent_node, dependent_node]} - @defdep_dependent_definitions = Hash.new { |h, k| h[k] = Set.new } - - # First-level usages of spreads within definitions - # (When a key has an empty list as its value, - # we can resolve that key's dependents) - # { definition_node => [node, node ...] } - @defdep_immediate_dependencies = Hash.new { |h, k| h[k] = Set.new } - - # When we encounter a spread, - # this node is the one who depends on it - @defdep_current_parent = nil - end - - def on_document(node, parent) - node.definitions.each do |definition| - if definition.is_a? GraphQL::Language::Nodes::FragmentDefinition - @defdep_fragment_definitions[definition.name] << definition - end - end - super - @dependencies = dependency_map { |defn, spreads, frag| - context.on_dependency_resolve_handlers.each { |h| h.call(defn, spreads, frag) } - } - end - - def on_operation_definition(node, prev_node) - @defdep_node_paths[node.name] = NodeWithPath.new(node, context.path) - @defdep_current_parent = node - super - @defdep_current_parent = nil - end - - def on_fragment_definition(node, parent) - @defdep_node_paths[node] = NodeWithPath.new(node, context.path) - @defdep_current_parent = node - super - @defdep_current_parent = nil - end - - def on_fragment_spread(node, parent) - @defdep_node_paths[node] = NodeWithPath.new(node, context.path) - - # Track both sides of the dependency - @defdep_dependent_definitions[node.name] << @defdep_current_parent - @defdep_immediate_dependencies[@defdep_current_parent] << node - super - end - - # A map of operation definitions to an array of that operation's dependencies - # @return [DependencyMap] - def dependency_map(&block) - @dependency_map ||= resolve_dependencies(&block) - end - - # Map definition AST nodes to the definition AST nodes they depend on. - # Expose circular dependencies. - class DependencyMap - # @return [Array] - attr_reader :cyclical_definitions - - # @return [Hash>] - attr_reader :unmet_dependencies - - # @return [Array] - attr_reader :unused_dependencies - - def initialize - @dependencies = Hash.new { |h, k| h[k] = [] } - @cyclical_definitions = [] - @unmet_dependencies = Hash.new { |h, k| h[k] = [] } - @unused_dependencies = [] - end - - # @return [Array] dependencies for `definition_node` - def [](definition_node) - @dependencies[definition_node] - end - end - - class NodeWithPath - extend Forwardable - attr_reader :node, :path - def initialize(node, path) - @node = node - @path = path - end - - def_delegators :@node, :name, :eql?, :hash - end - - private - - # Return a hash of { node => [node, node ... ]} pairs - # Keys are top-level definitions - # Values are arrays of flattened dependencies - def resolve_dependencies - dependency_map = DependencyMap.new - # Don't allow the loop to run more times - # than the number of fragments in the document - max_loops = 0 - @defdep_fragment_definitions.each_value do |v| - max_loops += v.size - end - - loops = 0 - - # Instead of tracking independent fragments _as you visit_, - # determine them at the end. This way, we can treat fragments with the - # same name as if they were the same name. If _any_ of the fragments - # with that name has a dependency, we record it. - independent_fragment_nodes = @defdep_fragment_definitions.values.flatten - @defdep_immediate_dependencies.keys - visited_fragment_names = Set.new - while fragment_node = independent_fragment_nodes.pop - if visited_fragment_names.add?(fragment_node.name) - # this is a new fragment name - else - # this is a duplicate fragment name - next - end - loops += 1 - if loops > max_loops - raise("Resolution loops exceeded the number of definitions; infinite loop detected. (Max: #{max_loops}, Current: #{loops})") - end - # Since it's independent, let's remove it from here. - # That way, we can use the remainder to identify cycles - @defdep_immediate_dependencies.delete(fragment_node) - fragment_usages = @defdep_dependent_definitions[fragment_node.name] - if fragment_usages.empty? - # If we didn't record any usages during the visit, - # then this fragment is unused. - dependency_map.unused_dependencies << @defdep_node_paths[fragment_node] - else - fragment_usages.each do |definition_node| - # Register the dependency AND second-order dependencies - dependency_map[definition_node] << fragment_node - dependency_map[definition_node].concat(dependency_map[fragment_node]) - # Since we've registered it, remove it from our to-do list - deps = @defdep_immediate_dependencies[definition_node] - # Can't find a way to _just_ delete from `deps` and return the deleted entries - removed, remaining = deps.partition { |spread| spread.name == fragment_node.name } - @defdep_immediate_dependencies[definition_node] = remaining - if block_given? - yield(definition_node, removed, fragment_node) - end - if remaining.empty? && - definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition) && - definition_node.name != fragment_node.name - # If all of this definition's dependencies have - # been resolved, we can now resolve its - # own dependents. - # - # But, it's possible to have a duplicate-named fragment here. - # Skip it in that case - independent_fragment_nodes << definition_node - end - end - end - end - - # If any dependencies were _unmet_ - # (eg, spreads with no corresponding definition) - # then they're still in there - @defdep_immediate_dependencies.each do |defn_node, deps| - deps.each do |spread| - if !@defdep_fragment_definitions.key?(spread.name) - dependency_map.unmet_dependencies[@defdep_node_paths[defn_node]] << @defdep_node_paths[spread] - deps.delete(spread) - end - end - if deps.empty? - @defdep_immediate_dependencies.delete(defn_node) - end - end - - # Anything left in @immediate_dependencies is cyclical - cyclical_nodes = @defdep_immediate_dependencies.keys.map { |n| @defdep_node_paths[n] } - # @immediate_dependencies also includes operation names, but we don't care about - # those. They became nil when we looked them up on `@fragment_definitions`, so remove them. - cyclical_nodes.compact! - dependency_map.cyclical_definitions.concat(cyclical_nodes) - - dependency_map - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/error.rb b/vendor/gems/graphql/lib/graphql/static_validation/error.rb deleted file mode 100644 index d8a4e940362..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/error.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # Generates GraphQL-compliant validation message. - class Error - # Convenience for validators - module ErrorHelper - # Error `error_message` is located at `node` - def error(error_message, nodes, context: nil, path: nil, extensions: {}) - path ||= context.path - nodes = Array(nodes) - GraphQL::StaticValidation::Error.new(error_message, nodes: nodes, path: path) - end - end - - attr_reader :message - attr_accessor :path - - def initialize(message, path: nil, nodes: []) - @message = message - @nodes = Array(nodes) - @path = path - end - - # A hash representation of this Message - def to_h - { - "message" => message, - "locations" => locations - }.tap { |h| h["path"] = path unless path.nil? } - end - - attr_reader :nodes - - private - - def locations - nodes.map do |node| - h = {"line" => node.line, "column" => node.col} - h["filename"] = node.filename if node.filename - h - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/interpreter_visitor.rb b/vendor/gems/graphql/lib/graphql/static_validation/interpreter_visitor.rb deleted file mode 100644 index d652222b9da..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/interpreter_visitor.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class InterpreterVisitor < BaseVisitor - include(GraphQL::StaticValidation::DefinitionDependencies) - - StaticValidation::ALL_RULES.reverse_each do |r| - include(r) - end - - include(ContextMethods) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/literal_validator.rb b/vendor/gems/graphql/lib/graphql/static_validation/literal_validator.rb deleted file mode 100644 index 5b2ec5f090c..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/literal_validator.rb +++ /dev/null @@ -1,156 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # Test whether `ast_value` is a valid input for `type` - class LiteralValidator - def initialize(context:) - @context = context - @types = context.types - @invalid_response = GraphQL::Query::InputValidationResult.new(valid: false, problems: []) - @valid_response = GraphQL::Query::InputValidationResult.new(valid: true, problems: []) - end - - def validate(ast_value, type) - catch(:invalid) do - recursively_validate(ast_value, type) - end - end - - private - - def replace_nulls_in(ast_value) - case ast_value - when Array - ast_value.map { |v| replace_nulls_in(v) } - when GraphQL::Language::Nodes::InputObject - ast_value.to_h - when GraphQL::Language::Nodes::NullValue - nil - else - ast_value - end - end - - def recursively_validate(ast_value, type) - if type.nil? - # this means we're an undefined argument, see #present_input_field_values_are_valid - maybe_raise_if_invalid(ast_value) do - @invalid_response - end - elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue) - maybe_raise_if_invalid(ast_value) do - type.kind.non_null? ? @invalid_response : @valid_response - end - elsif type.kind.non_null? - maybe_raise_if_invalid(ast_value) do - ast_value.nil? ? - @invalid_response : - recursively_validate(ast_value, type.of_type) - end - elsif type.kind.list? - item_type = type.of_type - results = ensure_array(ast_value).map { |val| recursively_validate(val, item_type) } - merge_results(results) - elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) - @valid_response - elsif type.kind.scalar? && constant_scalar?(ast_value) - maybe_raise_if_invalid(ast_value) do - ruby_value = replace_nulls_in(ast_value) - type.validate_input(ruby_value, @context) - end - elsif type.kind.enum? - maybe_raise_if_invalid(ast_value) do - if ast_value.is_a?(GraphQL::Language::Nodes::Enum) - type.validate_input(ast_value.name, @context) - else - # if our ast_value isn't an Enum it's going to be invalid so return false - @invalid_response - end - end - elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject) - maybe_raise_if_invalid(ast_value) do - merge_results([ - required_input_fields_are_present(type, ast_value), - present_input_field_values_are_valid(type, ast_value) - ]) - end - else - maybe_raise_if_invalid(ast_value) do - @invalid_response - end - end - end - - # When `error_bubbling` is false, we want to bail on the first failure that we find. - # Use `throw` to escape the current call stack, returning the invalid response. - def maybe_raise_if_invalid(ast_value) - ret = yield - if !@context.schema.error_bubbling && !ret.valid? - throw(:invalid, ret) - else - ret - end - end - - # The GraphQL grammar supports variables embedded within scalars but graphql.js - # doesn't support it so we won't either for simplicity - def constant_scalar?(ast_value) - if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) - false - elsif ast_value.is_a?(Array) - ast_value.all? { |element| constant_scalar?(element) } - elsif ast_value.is_a?(GraphQL::Language::Nodes::InputObject) - ast_value.arguments.all? { |arg| constant_scalar?(arg.value) } - else - true - end - end - - def required_input_fields_are_present(type, ast_node) - # TODO - would be nice to use these to create an error message so the caller knows - # that required fields are missing - required_field_names = @types.arguments(type) - .select { |argument| argument.type.kind.non_null? && !argument.default_value? } - .map!(&:name) - - present_field_names = ast_node.arguments.map(&:name) - missing_required_field_names = required_field_names - present_field_names - if @context.schema.error_bubbling - missing_required_field_names.empty? ? @valid_response : @invalid_response - else - results = missing_required_field_names.map do |name| - arg_type = @types.argument(type, name).type - recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type) - end - if type.one_of? && ast_node.arguments.size != 1 - results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})") - end - merge_results(results) - end - end - - def present_input_field_values_are_valid(type, ast_node) - results = ast_node.arguments.map do |value| - field = @types.argument(type, value.name) - # we want to call validate on an argument even if it's an invalid one - # so that our raise exception is on it instead of the entire InputObject - field_type = field && field.type - recursively_validate(value.value, field_type) - end - merge_results(results) - end - - def ensure_array(value) - value.is_a?(Array) ? value : [value] - end - - def merge_results(results_list) - merged_result = Query::InputValidationResult.new - results_list.each do |inner_result| - merged_result.merge_result!([], inner_result) - end - merged_result - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb deleted file mode 100644 index f167885fd08..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module ArgumentLiteralsAreCompatible - def on_argument(node, parent) - # Check the child arguments first; - # don't add a new error if one of them reports an error - super - - # Don't validate variables here - if node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) - return - end - - if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path } - parent_defn = parent_definition(parent) - - if parent_defn && (arg_defn = @types.argument(parent_defn, node.name)) - validation_result = context.validate_literal(node.value, arg_defn.type) - if !validation_result.valid? - kind_of_node = node_type(parent) - error_arg_name = parent_name(parent, parent_defn) - string_value = if node.value == Float::INFINITY - "" - else - " (#{GraphQL::Language::Printer.new.print(node.value)})" - end - - problems = validation_result.problems - first_problem = problems && problems.first - if first_problem - message = first_problem["message"] - # This is some legacy stuff from when `CoercionError` was raised thru the stack - if message - coerce_extensions = first_problem["extensions"] || { - "code" => "argumentLiteralsIncompatible" - } - end - end - - error_options = { - nodes: parent, - type: kind_of_node, - argument_name: node.name, - argument: arg_defn, - value: node.value - } - if coerce_extensions - error_options[:coerce_extensions] = coerce_extensions - end - - message ||= "Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value#{string_value}. Expected type '#{arg_defn.type.to_type_signature}'." - - error = GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new( - message, - **error_options - ) - - add_error(error) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb deleted file mode 100644 index ab0c4364ddc..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class ArgumentLiteralsAreCompatibleError < StaticValidation::Error - attr_reader :type_name - attr_reader :argument_name - attr_reader :argument - attr_reader :value - - def initialize(message, path: nil, nodes: [], type:, argument_name: nil, extensions: nil, coerce_extensions: nil, argument: nil, value: nil) - super(message, path: path, nodes: nodes) - @type_name = type - @argument_name = argument_name - @extensions = extensions - @coerce_extensions = coerce_extensions - @argument = argument - @value = value - end - - # A hash representation of this Message - def to_h - if @coerce_extensions - extensions = @coerce_extensions - # This is for legacy compat -- but this key is supposed to be a GraphQL type name :confounded: - extensions["typeName"] = "CoercionError" - else - extensions = { - "code" => code, - "typeName" => type_name - } - - if argument_name - extensions["argumentName"] = argument_name - end - end - - extensions.merge!(@extensions) unless @extensions.nil? - super.merge({ - "extensions" => extensions - }) - end - - def code - "argumentLiteralsIncompatible" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique.rb deleted file mode 100644 index be26bb8707e..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module ArgumentNamesAreUnique - include GraphQL::StaticValidation::Error::ErrorHelper - - def on_field(node, parent) - validate_arguments(node) - super - end - - def on_directive(node, parent) - validate_arguments(node) - super - end - - def validate_arguments(node) - argument_defns = node.arguments - if !argument_defns.empty? - args_by_name = Hash.new { |h, k| h[k] = [] } - argument_defns.each { |a| args_by_name[a.name] << a } - args_by_name.each do |name, defns| - if defns.size > 1 - add_error(GraphQL::StaticValidation::ArgumentNamesAreUniqueError.new("There can be only one argument named \"#{name}\"", nodes: defns, name: name)) - end - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb deleted file mode 100644 index 13b53d94e9a..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class ArgumentNamesAreUniqueError < StaticValidation::Error - attr_reader :name - - def initialize(message, path: nil, nodes: [], name:) - super(message, path: path, nodes: nodes) - @name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "name" => name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "argumentNotUnique" - end - end - end -end - diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined.rb deleted file mode 100644 index 0875d32c85f..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module ArgumentsAreDefined - def on_argument(node, parent) - parent_defn = parent_definition(parent) - - if parent_defn && @types.argument(parent_defn, node.name) - super - elsif parent_defn - kind_of_node = node_type(parent) - error_arg_name = parent_name(parent, parent_defn) - arg_names = context.types.arguments(parent_defn).map(&:graphql_name) - add_error(GraphQL::StaticValidation::ArgumentsAreDefinedError.new( - "#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'#{context.did_you_mean_suggestion(node.name, arg_names)}", - nodes: node, - name: error_arg_name, - type: kind_of_node, - argument_name: node.name, - parent: parent_defn - )) - else - # Some other weird error - super - end - end - - private - - # TODO smell: these methods are added to all visitors, since they're included in a module. - def parent_name(parent, type_defn) - case parent - when GraphQL::Language::Nodes::Field - parent.alias || parent.name - when GraphQL::Language::Nodes::InputObject - type_defn.graphql_name - when GraphQL::Language::Nodes::Argument, GraphQL::Language::Nodes::Directive - parent.name - else - raise "Invariant: Unexpected parent #{parent.inspect} (#{parent.class})" - end - end - - def node_type(parent) - parent.class.name.split("::").last - end - - def parent_definition(parent) - case parent - when GraphQL::Language::Nodes::InputObject - arg_defn = context.argument_definition - if arg_defn.nil? - nil - else - arg_ret_type = arg_defn.type.unwrap - if arg_ret_type.kind.input_object? - arg_ret_type - else - nil - end - end - when GraphQL::Language::Nodes::Directive - context.schema_directives[parent.name] - when GraphQL::Language::Nodes::Field - context.field_definition - else - raise "Unexpected argument parent: #{parent.class} (##{parent})" - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined_error.rb deleted file mode 100644 index 3f5b9eb27ce..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class ArgumentsAreDefinedError < StaticValidation::Error - attr_reader :name - attr_reader :type_name - attr_reader :argument_name - attr_reader :parent - - def initialize(message, path: nil, nodes: [], name:, type:, argument_name:, parent:) - super(message, path: path, nodes: nodes) - @name = name - @type_name = type - @argument_name = argument_name - @parent = parent - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "name" => name, - "typeName" => type_name, - "argumentName" => argument_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "argumentNotAccepted" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined.rb deleted file mode 100644 index d8fac625d98..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module DirectivesAreDefined - def initialize(*) - super - end - - def on_directive(node, parent) - if !@types.directive_exists?(node.name) - @directives_are_defined_errors_by_name ||= {} - error = @directives_are_defined_errors_by_name[node.name] ||= begin - @directive_names ||= @types.directives.map(&:graphql_name) - err = GraphQL::StaticValidation::DirectivesAreDefinedError.new( - "Directive @#{node.name} is not defined#{context.did_you_mean_suggestion(node.name, @directive_names)}", - nodes: [], - directive: node.name - ) - add_error(err) - err - end - error.nodes << node - else - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined_error.rb deleted file mode 100644 index 2f09522f963..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_defined_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class DirectivesAreDefinedError < StaticValidation::Error - attr_reader :directive_name - - def initialize(message, path: nil, nodes: [], directive:) - super(message, path: path, nodes: nodes) - @directive_name = directive - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "directiveName" => directive_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "undefinedDirective" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb deleted file mode 100644 index 65f7f2a06ca..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module DirectivesAreInValidLocations - include GraphQL::Language - - def on_directive(node, parent) - validate_location(node, parent, context.schema_directives) - super - end - - private - - LOCATION_MESSAGE_NAMES = { - GraphQL::Schema::Directive::QUERY => "queries", - GraphQL::Schema::Directive::MUTATION => "mutations", - GraphQL::Schema::Directive::SUBSCRIPTION => "subscriptions", - GraphQL::Schema::Directive::FIELD => "fields", - GraphQL::Schema::Directive::FRAGMENT_DEFINITION => "fragment definitions", - GraphQL::Schema::Directive::FRAGMENT_SPREAD => "fragment spreads", - GraphQL::Schema::Directive::INLINE_FRAGMENT => "inline fragments", - GraphQL::Schema::Directive::VARIABLE_DEFINITION => "variable definitions", - } - - SIMPLE_LOCATIONS = { - Nodes::Field => GraphQL::Schema::Directive::FIELD, - Nodes::InlineFragment => GraphQL::Schema::Directive::INLINE_FRAGMENT, - Nodes::FragmentSpread => GraphQL::Schema::Directive::FRAGMENT_SPREAD, - Nodes::FragmentDefinition => GraphQL::Schema::Directive::FRAGMENT_DEFINITION, - Nodes::VariableDefinition => GraphQL::Schema::Directive::VARIABLE_DEFINITION, - } - - SIMPLE_LOCATION_NODES = SIMPLE_LOCATIONS.keys - - def validate_location(ast_directive, ast_parent, directives) - directive_defn = directives[ast_directive.name] - case ast_parent - when Nodes::OperationDefinition - required_location = GraphQL::Schema::Directive.const_get(ast_parent.operation_type.upcase) - assert_includes_location(directive_defn, ast_directive, required_location) - when *SIMPLE_LOCATION_NODES - required_location = SIMPLE_LOCATIONS[ast_parent.class] - assert_includes_location(directive_defn, ast_directive, required_location) - else - add_error(GraphQL::StaticValidation::DirectivesAreInValidLocationsError.new( - "Directives can't be applied to #{ast_parent.class.name}s", - nodes: ast_directive, - target: ast_parent.class.name - )) - end - end - - def assert_includes_location(directive_defn, directive_ast, required_location) - if !directive_defn.locations.include?(required_location) - location_name = LOCATION_MESSAGE_NAMES[required_location] - allowed_location_names = directive_defn.locations.map { |loc| LOCATION_MESSAGE_NAMES[loc] } - add_error(GraphQL::StaticValidation::DirectivesAreInValidLocationsError.new( - "'@#{directive_defn.graphql_name}' can't be applied to #{location_name} (allowed: #{allowed_location_names.join(", ")})", - nodes: directive_ast, - target: location_name, - name: directive_defn.graphql_name - )) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb deleted file mode 100644 index eb6e3230d6d..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class DirectivesAreInValidLocationsError < StaticValidation::Error - attr_reader :target_name - attr_reader :name - - def initialize(message, path: nil, nodes: [], target:, name: nil) - super(message, path: path, nodes: nodes) - @target_name = target - @name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "targetName" => target_name - }.tap { |h| h["name"] = name unless name.nil? } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "directiveCannotBeApplied" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb deleted file mode 100644 index 24da08fbb96..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FieldsAreDefinedOnType - def on_field(node, parent) - parent_type = @object_types[-2] - field = context.query.types.field(parent_type, node.name) - - if field.nil? - if parent_type.kind.union? - add_error(GraphQL::StaticValidation::FieldsHaveAppropriateSelectionsError.new( - "Selections can't be made directly on unions (see selections on #{parent_type.graphql_name})", - nodes: parent, - node_name: parent_type.graphql_name - )) - else - possible_fields = possible_fields(context, parent_type) - suggestion = context.did_you_mean_suggestion(node.name, possible_fields) - message = "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'#{suggestion}" - add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new( - message, - nodes: node, - field: node.name, - type: parent_type.graphql_name - )) - end - else - super - end - end - - private - - def possible_fields(context, parent_type) - return EmptyObjects::EMPTY_ARRAY if parent_type.kind.leaf? - context.types.fields(parent_type).map(&:graphql_name) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb deleted file mode 100644 index c405eac7e65..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FieldsAreDefinedOnTypeError < StaticValidation::Error - attr_reader :type_name - attr_reader :field_name - - def initialize(message, path: nil, nodes: [], type:, field:) - super(message, path: path, nodes: nodes) - @type_name = type - @field_name = field - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "typeName" => type_name, - "fieldName" => field_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "undefinedField" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb deleted file mode 100644 index c17530bc2be..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # Scalars _can't_ have selections - # Objects _must_ have selections - module FieldsHaveAppropriateSelections - include GraphQL::StaticValidation::Error::ErrorHelper - - def on_field(node, parent) - field_defn = field_definition - if validate_field_selections(node, field_defn.type.unwrap) - super - end - end - - def on_operation_definition(node, _parent) - if validate_field_selections(node, type_definition) - super - end - end - - private - - - def validate_field_selections(ast_node, resolved_type) - msg = if resolved_type.nil? - nil - elsif !ast_node.selections.empty? && resolved_type.kind.leaf? - selection_strs = ast_node.selections.map do |n| - case n - when GraphQL::Language::Nodes::InlineFragment - "\"... on #{n.type.name} { ... }\"" - when GraphQL::Language::Nodes::Field - "\"#{n.name}\"" - when GraphQL::Language::Nodes::FragmentSpread - "\"#{n.name}\"" - else - raise "Invariant: unexpected selection node: #{n}" - end - end - "Selections can't be made on #{resolved_type.kind.name.sub("_", " ").downcase}s (%{node_name} returns #{resolved_type.graphql_name} but has selections [#{selection_strs.join(", ")}])" - elsif resolved_type.kind.fields? && ast_node.selections.empty? - "Field must have selections (%{node_name} returns #{resolved_type.graphql_name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)" - else - nil - end - - if msg - node_name = case ast_node - when GraphQL::Language::Nodes::Field - "field '#{ast_node.name}'" - when GraphQL::Language::Nodes::OperationDefinition - if ast_node.name.nil? - "anonymous query" - else - "#{ast_node.operation_type} '#{ast_node.name}'" - end - else - raise("Unexpected node #{ast_node}") - end - extensions = { - "rule": "StaticValidation::FieldsHaveAppropriateSelections", - "name": node_name.to_s - } - unless resolved_type.nil? - extensions["type"] = resolved_type.to_type_signature - end - add_error(GraphQL::StaticValidation::FieldsHaveAppropriateSelectionsError.new( - msg % { node_name: node_name }, - nodes: ast_node, - node_name: node_name.to_s, - type: resolved_type.nil? ? nil : resolved_type.graphql_name, - )) - false - else - true - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb deleted file mode 100644 index 34b06a97168..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FieldsHaveAppropriateSelectionsError < StaticValidation::Error - attr_reader :type_name - attr_reader :node_name - - def initialize(message, path: nil, nodes: [], node_name:, type: nil) - super(message, path: path, nodes: nodes) - @node_name = node_name - @type_name = type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "nodeName" => node_name - }.tap { |h| h["typeName"] = type_name unless type_name.nil? } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "selectionMismatch" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge.rb deleted file mode 100644 index 05b62f54c20..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge.rb +++ /dev/null @@ -1,426 +0,0 @@ - # frozen_string_literal: true - -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FieldsWillMerge - # Validates that a selection set is valid if all fields (including spreading any - # fragments) either correspond to distinct response names or can be merged - # without ambiguity. - # - # Original Algorithm: https://github.com/graphql/graphql-js/blob/master/src/validation/rules/OverlappingFieldsCanBeMerged.js - NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH - - Field = Struct.new(:node, :definition, :owner_type, :parents) - FragmentSpread = Struct.new(:name, :parents) - - def initialize(*) - super - @visited_fragments = {} - @compared_fragments = {} - @conflict_count = 0 - end - - def on_operation_definition(node, _parent) - setting_errors { conflicts_within_selection_set(node, type_definition) } - super - end - - def on_field(node, _parent) - setting_errors { conflicts_within_selection_set(node, type_definition) } - super - end - - private - - def field_conflicts - @field_conflicts ||= Hash.new do |errors, field| - errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :field, field_name: field) - end - end - - def arg_conflicts - @arg_conflicts ||= Hash.new do |errors, field| - errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :argument, field_name: field) - end - end - - def setting_errors - @field_conflicts = nil - @arg_conflicts = nil - - yield - # don't initialize these if they weren't initialized in the block: - @field_conflicts && @field_conflicts.each_value { |error| add_error(error) } - @arg_conflicts && @arg_conflicts.each_value { |error| add_error(error) } - end - - def conflicts_within_selection_set(node, parent_type) - return if parent_type.nil? - - fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: nil) - - # (A) Find find all conflicts "within" the fields of this selection set. - find_conflicts_within(fields) - - fragment_spreads.each_with_index do |fragment_spread, i| - are_mutually_exclusive = mutually_exclusive?( - fragment_spread.parents, - [parent_type] - ) - - # (B) Then find conflicts between these fields and those represented by - # each spread fragment name found. - find_conflicts_between_fields_and_fragment( - fragment_spread, - fields, - mutually_exclusive: are_mutually_exclusive, - ) - - # (C) Then compare this fragment with all other fragments found in this - # selection set to collect conflicts between fragments spread together. - # This compares each item in the list of fragment names to every other - # item in that same list (except for itself). - fragment_spreads[i + 1..-1].each do |fragment_spread2| - are_mutually_exclusive = mutually_exclusive?( - fragment_spread.parents, - fragment_spread2.parents - ) - - find_conflicts_between_fragments( - fragment_spread, - fragment_spread2, - mutually_exclusive: are_mutually_exclusive, - ) - end - end - end - - def find_conflicts_between_fragments(fragment_spread1, fragment_spread2, mutually_exclusive:) - fragment_name1 = fragment_spread1.name - fragment_name2 = fragment_spread2.name - return if fragment_name1 == fragment_name2 - - cache_key = compared_fragments_key( - fragment_name1, - fragment_name2, - mutually_exclusive, - ) - if @compared_fragments.key?(cache_key) - return - else - @compared_fragments[cache_key] = true - end - - fragment1 = context.fragments[fragment_name1] - fragment2 = context.fragments[fragment_name2] - - return if fragment1.nil? || fragment2.nil? - - fragment_type1 = context.query.types.type(fragment1.type.name) - fragment_type2 = context.query.types.type(fragment2.type.name) - - return if fragment_type1.nil? || fragment_type2.nil? - - fragment_fields1, fragment_spreads1 = fields_and_fragments_from_selection( - fragment1, - owner_type: fragment_type1, - parents: [*fragment_spread1.parents, fragment_type1] - ) - fragment_fields2, fragment_spreads2 = fields_and_fragments_from_selection( - fragment2, - owner_type: fragment_type1, - parents: [*fragment_spread2.parents, fragment_type2] - ) - - # (F) First, find all conflicts between these two collections of fields - # (not including any nested fragments). - find_conflicts_between( - fragment_fields1, - fragment_fields2, - mutually_exclusive: mutually_exclusive, - ) - - # (G) Then collect conflicts between the first fragment and any nested - # fragments spread in the second fragment. - fragment_spreads2.each do |fragment_spread| - find_conflicts_between_fragments( - fragment_spread1, - fragment_spread, - mutually_exclusive: mutually_exclusive, - ) - end - - # (G) Then collect conflicts between the first fragment and any nested - # fragments spread in the second fragment. - fragment_spreads1.each do |fragment_spread| - find_conflicts_between_fragments( - fragment_spread2, - fragment_spread, - mutually_exclusive: mutually_exclusive, - ) - end - end - - def find_conflicts_between_fields_and_fragment(fragment_spread, fields, mutually_exclusive:) - fragment_name = fragment_spread.name - return if @visited_fragments.key?(fragment_name) - @visited_fragments[fragment_name] = true - - fragment = context.fragments[fragment_name] - return if fragment.nil? - - fragment_type = @types.type(fragment.type.name) - return if fragment_type.nil? - - fragment_fields, fragment_spreads = fields_and_fragments_from_selection(fragment, owner_type: fragment_type, parents: [*fragment_spread.parents, fragment_type]) - - # (D) First find any conflicts between the provided collection of fields - # and the collection of fields represented by the given fragment. - find_conflicts_between( - fields, - fragment_fields, - mutually_exclusive: mutually_exclusive, - ) - - # (E) Then collect any conflicts between the provided collection of fields - # and any fragment names found in the given fragment. - fragment_spreads.each do |fragment_spread| - find_conflicts_between_fields_and_fragment( - fragment_spread, - fields, - mutually_exclusive: mutually_exclusive, - ) - end - end - - def find_conflicts_within(response_keys) - response_keys.each do |key, fields| - next if fields.size < 2 - # find conflicts within nodes - i = 0 - while i < fields.size - j = i + 1 - while j < fields.size - find_conflict(key, fields[i], fields[j]) - j += 1 - end - i += 1 - end - end - end - - def find_conflict(response_key, field1, field2, mutually_exclusive: false) - return if @conflict_count >= context.max_errors - return if field1.definition.nil? || field2.definition.nil? - - node1 = field1.node - node2 = field2.node - - are_mutually_exclusive = mutually_exclusive || - mutually_exclusive?(field1.parents, field2.parents) - - if !are_mutually_exclusive - if node1.name != node2.name - conflict = field_conflicts[response_key] - - conflict.add_conflict(node1, node1.name) - conflict.add_conflict(node2, node2.name) - - @conflict_count += 1 - end - - if !same_arguments?(node1, node2) - conflict = arg_conflicts[response_key] - - conflict.add_conflict(node1, GraphQL::Language.serialize(serialize_field_args(node1))) - conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2))) - - @conflict_count += 1 - end - end - - find_conflicts_between_sub_selection_sets( - field1, - field2, - mutually_exclusive: are_mutually_exclusive, - ) - end - - def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:) - return if field1.definition.nil? || - field2.definition.nil? || - (field1.node.selections.empty? && field2.node.selections.empty?) - - return_type1 = field1.definition.type.unwrap - return_type2 = field2.definition.type.unwrap - parents1 = [return_type1] - parents2 = [return_type2] - - fields, fragment_spreads = fields_and_fragments_from_selection( - field1.node, - owner_type: return_type1, - parents: parents1 - ) - - fields2, fragment_spreads2 = fields_and_fragments_from_selection( - field2.node, - owner_type: return_type2, - parents: parents2 - ) - - # (H) First, collect all conflicts between these two collections of field. - find_conflicts_between(fields, fields2, mutually_exclusive: mutually_exclusive) - - # (I) Then collect conflicts between the first collection of fields and - # those referenced by each fragment name associated with the second. - fragment_spreads2.each do |fragment_spread| - find_conflicts_between_fields_and_fragment( - fragment_spread, - fields, - mutually_exclusive: mutually_exclusive, - ) - end - - # (I) Then collect conflicts between the second collection of fields and - # those referenced by each fragment name associated with the first. - fragment_spreads.each do |fragment_spread| - find_conflicts_between_fields_and_fragment( - fragment_spread, - fields2, - mutually_exclusive: mutually_exclusive, - ) - end - - # (J) Also collect conflicts between any fragment names by the first and - # fragment names by the second. This compares each item in the first set of - # names to each item in the second set of names. - fragment_spreads.each do |frag1| - fragment_spreads2.each do |frag2| - find_conflicts_between_fragments( - frag1, - frag2, - mutually_exclusive: mutually_exclusive - ) - end - end - end - - def find_conflicts_between(response_keys, response_keys2, mutually_exclusive:) - response_keys.each do |key, fields| - fields2 = response_keys2[key] - if fields2 - fields.each do |field| - fields2.each do |field2| - find_conflict( - key, - field, - field2, - mutually_exclusive: mutually_exclusive, - ) - end - end - end - end - end - - NO_SELECTIONS = [GraphQL::EmptyObjects::EMPTY_HASH, GraphQL::EmptyObjects::EMPTY_ARRAY].freeze - - def fields_and_fragments_from_selection(node, owner_type:, parents:) - if node.selections.empty? - NO_SELECTIONS - else - parents ||= [] - fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: []) - response_keys = fields.group_by { |f| f.node.alias || f.node.name } - [response_keys, fragment_spreads] - end - end - - def find_fields_and_fragments(selections, owner_type:, parents:, fields:, fragment_spreads:) - selections.each do |node| - case node - when GraphQL::Language::Nodes::Field - definition = @types.field(owner_type, node.name) - fields << Field.new(node, definition, owner_type, parents) - when GraphQL::Language::Nodes::InlineFragment - fragment_type = node.type ? @types.type(node.type.name) : owner_type - find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: owner_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type - when GraphQL::Language::Nodes::FragmentSpread - fragment_spreads << FragmentSpread.new(node.name, parents) - end - end - - [fields, fragment_spreads] - end - - def same_arguments?(field1, field2) - # Check for incompatible / non-identical arguments on this node: - arguments1 = field1.arguments - arguments2 = field2.arguments - - return false if arguments1.length != arguments2.length - - arguments1.all? do |argument1| - argument2 = arguments2.find { |argument| argument.name == argument1.name } - return false if argument2.nil? - - serialize_arg(argument1.value) == serialize_arg(argument2.value) - end - end - - def serialize_arg(arg_value) - case arg_value - when GraphQL::Language::Nodes::AbstractNode - arg_value.to_query_string - when Array - "[#{arg_value.map { |a| serialize_arg(a) }.join(", ")}]" - else - GraphQL::Language.serialize(arg_value) - end - end - - def serialize_field_args(field) - serialized_args = {} - field.arguments.each do |argument| - serialized_args[argument.name] = serialize_arg(argument.value) - end - serialized_args - end - - def compared_fragments_key(frag1, frag2, exclusive) - # Cache key to not compare two fragments more than once. - # The key includes both fragment names sorted (this way we - # avoid computing "A vs B" and "B vs A"). It also includes - # "exclusive" since the result may change depending on the parent_type - "#{[frag1, frag2].sort.join('-')}-#{exclusive}" - end - - # Given two list of parents, find out if they are mutually exclusive - # In this context, `parents` represents the "self scope" of the field, - # what types may be found at this point in the query. - def mutually_exclusive?(parents1, parents2) - if parents1.empty? || parents2.empty? - false - elsif parents1.length == parents2.length - parents1.length.times.any? do |i| - type1 = parents1[i - 1] - type2 = parents2[i - 1] - if type1 == type2 - # If the types we're comparing are the same type, - # then they aren't mutually exclusive - false - else - # Check if these two scopes have _any_ types in common. - possible_right_types = context.types.possible_types(type1) - possible_left_types = context.types.possible_types(type2) - (possible_right_types & possible_left_types).empty? - end - end - else - true - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge_error.rb deleted file mode 100644 index 742f4e52943..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fields_will_merge_error.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FieldsWillMergeError < StaticValidation::Error - attr_reader :field_name - attr_reader :kind - - def initialize(kind:, field_name:) - super(nil) - - @field_name = field_name - @kind = kind - @conflicts = [] - end - - def message - "Field '#{field_name}' has #{kind == :argument ? 'an' : 'a'} #{kind} conflict: #{conflicts}?" - end - - def path - [] - end - - def conflicts - @conflicts.join(' or ') - end - - def add_conflict(node, conflict_str) - return if nodes.include?(node) - - @nodes << node - @conflicts << conflict_str - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "fieldName" => field_name, - "conflicts" => conflicts - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "fieldConflict" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique.rb deleted file mode 100644 index 7f31d3cbdd8..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentNamesAreUnique - - def initialize(*) - super - @fragments_by_name = Hash.new { |h, k| h[k] = [] } - end - - def on_fragment_definition(node, parent) - @fragments_by_name[node.name] << node - super - end - - def on_document(_n, _p) - super - @fragments_by_name.each do |name, fragments| - if fragments.length > 1 - add_error(GraphQL::StaticValidation::FragmentNamesAreUniqueError.new( - %|Fragment name "#{name}" must be unique|, - nodes: fragments, - name: name - )) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb deleted file mode 100644 index 692035c948b..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentNamesAreUniqueError < StaticValidation::Error - attr_reader :fragment_name - - def initialize(message, path: nil, nodes: [], name:) - super(message, path: path, nodes: nodes) - @fragment_name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "fragmentName" => fragment_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "fragmentNotUnique" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb deleted file mode 100644 index f7e045f59e1..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentSpreadsArePossible - def initialize(*) - super - @spreads_to_validate = [] - end - - def on_inline_fragment(node, parent) - fragment_parent = context.object_types[-2] - fragment_child = context.object_types.last - if fragment_child - validate_fragment_in_scope(fragment_parent, fragment_child, node, context, context.path) - end - super - end - - def on_fragment_spread(node, parent) - fragment_parent = context.object_types.last - @spreads_to_validate << FragmentSpread.new(node: node, parent_type: fragment_parent, path: context.path) - super - end - - def on_document(node, parent) - super - @spreads_to_validate.each do |frag_spread| - frag_node = context.fragments[frag_spread.node.name] - if frag_node - fragment_child_name = frag_node.type.name - fragment_child = @types.type(fragment_child_name) - # Might be non-existent type name - if fragment_child - validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path) - end - end - end - end - - private - - def validate_fragment_in_scope(parent_type, child_type, node, context, path) - if !child_type.kind.fields? - # It's not a valid fragment type, this error was handled someplace else - return - end - parent_types = @types.possible_types(parent_type.unwrap) - child_types = @types.possible_types(child_type.unwrap) - - if child_types.none? { |c| parent_types.include?(c) } - name = node.respond_to?(:name) ? " #{node.name}" : "" - add_error(GraphQL::StaticValidation::FragmentSpreadsArePossibleError.new( - "Fragment#{name} on #{child_type.graphql_name} can't be spread inside #{parent_type.graphql_name}", - nodes: node, - path: path, - fragment_name: name.empty? ? "unknown" : name, - type: child_type.graphql_name, - parent: parent_type.graphql_name - )) - end - end - - class FragmentSpread - attr_reader :node, :parent_type, :path - def initialize(node:, parent_type:, path:) - @node = node - @parent_type = parent_type - @path = path - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb deleted file mode 100644 index cf543d918f9..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentSpreadsArePossibleError < StaticValidation::Error - attr_reader :type_name - attr_reader :fragment_name - attr_reader :parent_name - - def initialize(message, path: nil, nodes: [], type:, fragment_name:, parent:) - super(message, path: path, nodes: nodes) - @type_name = type - @fragment_name = fragment_name - @parent_name = parent - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "typeName" => type_name, - "fragmentName" => fragment_name, - "parentName" => parent_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "cannotSpreadFragment" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist.rb deleted file mode 100644 index e5e4430f031..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentTypesExist - def on_fragment_definition(node, _parent) - if validate_type_exists(node) - super - end - end - - def on_inline_fragment(node, _parent) - if validate_type_exists(node) - super - end - end - - private - - def validate_type_exists(fragment_node) - if !fragment_node.type - true - else - type_name = fragment_node.type.name - type = @types.type(type_name) - if type.nil? - @all_possible_fragment_type_names ||= begin - names = [] - context.types.all_types.each do |type| - if type.kind.fields? - names << type.graphql_name - end - end - names - end - - add_error(GraphQL::StaticValidation::FragmentTypesExistError.new( - "No such type #{type_name}, so it can't be a fragment condition#{context.did_you_mean_suggestion(type_name, @all_possible_fragment_type_names)}", - nodes: fragment_node, - type: type_name - )) - false - else - true - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist_error.rb deleted file mode 100644 index 3d8a9b05323..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentTypesExistError < StaticValidation::Error - attr_reader :type_name - - def initialize(message, path: nil, nodes: [], type:) - super(message, path: path, nodes: nodes) - @type_name = type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "typeName" => type_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "undefinedType" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite.rb deleted file mode 100644 index 45b88247b68..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentsAreFinite - def on_document(_n, _p) - super - dependency_map = context.dependencies - dependency_map.cyclical_definitions.each do |defn| - if defn.node.is_a?(GraphQL::Language::Nodes::FragmentDefinition) - add_error(GraphQL::StaticValidation::FragmentsAreFiniteError.new( - "Fragment #{defn.name} contains an infinite loop", - nodes: defn.node, - path: defn.path, - name: defn.name - )) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite_error.rb deleted file mode 100644 index 1c5e9881a5b..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentsAreFiniteError < StaticValidation::Error - attr_reader :fragment_name - - def initialize(message, path: nil, nodes: [], name:) - super(message, path: path, nodes: nodes) - @fragment_name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "fragmentName" => fragment_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "infiniteLoop" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named.rb deleted file mode 100644 index 42f58bec8c4..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentsAreNamed - def on_fragment_definition(node, _parent) - if node.name.nil? - add_error(GraphQL::StaticValidation::FragmentsAreNamedError.new( - "Fragment definition has no name", - nodes: node - )) - end - super - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named_error.rb deleted file mode 100644 index dd5826c1880..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_named_error.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentsAreNamedError < StaticValidation::Error - - def initialize(message, path: nil, nodes: []) - super(message, path: path, nodes: nodes) - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "anonymousFragment" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb deleted file mode 100644 index 63f4aa49e8b..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentsAreOnCompositeTypes - def on_fragment_definition(node, parent) - validate_type_is_composite(node) && super - end - - def on_inline_fragment(node, parent) - validate_type_is_composite(node) && super - end - - private - - def validate_type_is_composite(node) - node_type = node.type - if node_type.nil? - # Inline fragment on the same type - true - else - type_name = node_type.to_query_string - type_def = @types.type(type_name) - if type_def.nil? || !type_def.kind.composite? - add_error(GraphQL::StaticValidation::FragmentsAreOnCompositeTypesError.new( - "Invalid fragment on type #{type_name} (must be Union, Interface or Object)", - nodes: node, - type: type_name - )) - false - else - true - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb deleted file mode 100644 index 2b546c295cd..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentsAreOnCompositeTypesError < StaticValidation::Error - attr_reader :type_name - attr_reader :argument_name - - def initialize(message, path: nil, nodes: [], type:) - super(message, path: path, nodes: nodes) - @type_name = type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "typeName" => type_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "fragmentOnNonCompositeType" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used.rb deleted file mode 100644 index 3be22b03593..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module FragmentsAreUsed - def on_document(node, parent) - super - dependency_map = context.dependencies - dependency_map.unmet_dependencies.each do |op_defn, spreads| - spreads.each do |fragment_spread| - add_error(GraphQL::StaticValidation::FragmentsAreUsedError.new( - "Fragment #{fragment_spread.name} was used, but not defined", - nodes: fragment_spread.node, - path: fragment_spread.path, - fragment: fragment_spread.name - )) - end - end - - dependency_map.unused_dependencies.each do |fragment| - if fragment && !fragment.name.nil? - add_error(GraphQL::StaticValidation::FragmentsAreUsedError.new( - "Fragment #{fragment.name} was defined, but not used", - nodes: fragment.node, - path: fragment.path, - fragment: fragment.name - )) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used_error.rb deleted file mode 100644 index 1e16794b19a..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/fragments_are_used_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class FragmentsAreUsedError < StaticValidation::Error - attr_reader :fragment_name - - def initialize(message, path: nil, nodes: [], fragment:) - super(message, path: path, nodes: nodes) - @fragment_name = fragment - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "fragmentName" => fragment_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "useAndDefineFragment" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique.rb deleted file mode 100644 index ef49b050d65..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module InputObjectNamesAreUnique - def on_input_object(node, parent) - validate_input_fields(node) - super - end - - private - - def validate_input_fields(node) - input_field_defns = node.arguments - input_fields_by_name = Hash.new { |h, k| h[k] = [] } - input_field_defns.each { |a| input_fields_by_name[a.name] << a } - - input_fields_by_name.each do |name, defns| - if defns.size > 1 - error = GraphQL::StaticValidation::InputObjectNamesAreUniqueError.new( - "There can be only one input field named \"#{name}\"", - nodes: defns, - name: name - ) - add_error(error) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb deleted file mode 100644 index 2e31bc107b9..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class InputObjectNamesAreUniqueError < StaticValidation::Error - attr_reader :name - - def initialize(message, path: nil, nodes: [], name:) - super(message, path: path, nodes: nodes) - @name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "name" => name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "inputFieldNotUnique" - end - end - end -end - diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists.rb deleted file mode 100644 index 903221c851e..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module MutationRootExists - def on_operation_definition(node, _parent) - if node.operation_type == 'mutation' && context.query.types.mutation_root.nil? - add_error(GraphQL::StaticValidation::MutationRootExistsError.new( - 'Schema is not configured for mutations', - nodes: node - )) - else - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists_error.rb deleted file mode 100644 index 9fa0fc94afc..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class MutationRootExistsError < StaticValidation::Error - - def initialize(message, path: nil, nodes: []) - super(message, path: path, nodes: nodes) - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "missingMutationConfiguration" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present.rb deleted file mode 100644 index 00b68246c2d..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module NoDefinitionsArePresent - include GraphQL::StaticValidation::Error::ErrorHelper - - def initialize(*) - super - @schema_definition_nodes = [] - end - - def on_invalid_node(node, parent) - @schema_definition_nodes << node - nil - end - - alias :on_directive_definition :on_invalid_node - alias :on_schema_definition :on_invalid_node - alias :on_scalar_type_definition :on_invalid_node - alias :on_object_type_definition :on_invalid_node - alias :on_input_object_type_definition :on_invalid_node - alias :on_interface_type_definition :on_invalid_node - alias :on_union_type_definition :on_invalid_node - alias :on_enum_type_definition :on_invalid_node - alias :on_schema_extension :on_invalid_node - alias :on_scalar_type_extension :on_invalid_node - alias :on_object_type_extension :on_invalid_node - alias :on_input_object_type_extension :on_invalid_node - alias :on_interface_type_extension :on_invalid_node - alias :on_union_type_extension :on_invalid_node - alias :on_enum_type_extension :on_invalid_node - - def on_document(node, parent) - super - if !@schema_definition_nodes.empty? - add_error(GraphQL::StaticValidation::NoDefinitionsArePresentError.new(%|Query cannot contain schema definitions|, nodes: @schema_definition_nodes)) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb deleted file mode 100644 index 362e9bbaf84..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class NoDefinitionsArePresentError < StaticValidation::Error - def initialize(message, path: nil, nodes: []) - super(message, path: path, nodes: nodes) - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "queryContainsSchemaDefinitions" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb deleted file mode 100644 index a811788e35f..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module OneOfInputObjectsAreValid - def on_input_object(node, parent) - return super unless parent.is_a?(GraphQL::Language::Nodes::Argument) - - parent_type = get_parent_type(context, parent) - return super unless parent_type && parent_type.kind.input_object? && parent_type.one_of? - - validate_one_of_input_object(node, context, parent_type) - super - end - - private - - def validate_one_of_input_object(ast_node, context, parent_type) - present_fields = ast_node.arguments.map(&:name) - input_object_type = parent_type.to_type_signature - - if present_fields.count != 1 - add_error( - OneOfInputObjectsAreValidError.new( - "OneOf Input Object '#{input_object_type}' must specify exactly one key.", - path: context.path, - nodes: ast_node, - input_object_type: input_object_type - ) - ) - return - end - - field = present_fields.first - value = ast_node.arguments.first.value - - if value.is_a?(GraphQL::Language::Nodes::NullValue) - add_error( - OneOfInputObjectsAreValidError.new( - "Argument '#{input_object_type}.#{field}' must be non-null.", - path: [*context.path, field], - nodes: ast_node.arguments.first, - input_object_type: input_object_type - ) - ) - return - end - - if value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) - variable_name = value.name - variable_type = @declared_variables[variable_name].type - - unless variable_type.is_a?(GraphQL::Language::Nodes::NonNullType) - add_error( - OneOfInputObjectsAreValidError.new( - "Variable '#{variable_name}' must be non-nullable to be used for OneOf Input Object '#{input_object_type}'.", - path: [*context.path, field], - nodes: ast_node, - input_object_type: input_object_type - ) - ) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb deleted file mode 100644 index fc925486b2e..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class OneOfInputObjectsAreValidError < StaticValidation::Error - attr_reader :input_object_type - - def initialize(message, path:, nodes:, input_object_type:) - super(message, path: path, nodes: nodes) - @input_object_type = input_object_type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "inputObjectType" => input_object_type - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "invalidOneOfInputObject" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid.rb deleted file mode 100644 index d93f0de4b40..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module OperationNamesAreValid - def initialize(*) - super - @operation_names = Hash.new { |h, k| h[k] = [] } - end - - def on_operation_definition(node, parent) - @operation_names[node.name] << node - super - end - - def on_document(node, parent) - super - op_count = @operation_names.values.inject(0) { |m, v| m + v.size } - - @operation_names.each do |name, nodes| - if name.nil? && op_count > 1 - add_error(GraphQL::StaticValidation::OperationNamesAreValidError.new( - %|Operation name is required when multiple operations are present|, - nodes: nodes - )) - elsif nodes.length > 1 - add_error(GraphQL::StaticValidation::OperationNamesAreValidError.new( - %|Operation name "#{name}" must be unique|, - nodes: nodes, - name: name - )) - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb deleted file mode 100644 index 01b131b16de..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class OperationNamesAreValidError < StaticValidation::Error - attr_reader :operation_name - - def initialize(message, path: nil, nodes: [], name: nil) - super(message, path: path, nodes: nodes) - @operation_name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code - }.tap { |h| h["operationName"] = operation_name unless operation_name.nil? } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "uniquelyNamedOperations" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists.rb deleted file mode 100644 index a27207dbef3..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module QueryRootExists - def on_operation_definition(node, _parent) - if (node.operation_type == 'query' || node.operation_type.nil?) && context.query.types.query_root.nil? - add_error(GraphQL::StaticValidation::QueryRootExistsError.new( - 'Schema is not configured for queries', - nodes: node - )) - else - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists_error.rb deleted file mode 100644 index 650285053ad..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/query_root_exists_error.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class QueryRootExistsError < StaticValidation::Error - - def initialize(message, path: nil, nodes: []) - super(message, path: path, nodes: nodes) - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "missingQueryConfiguration" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present.rb deleted file mode 100644 index 81e15f2d350..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module RequiredArgumentsArePresent - def on_field(node, _parent) - assert_required_args(node, field_definition) - super - end - - def on_directive(node, _parent) - directive_defn = context.schema_directives[node.name] - assert_required_args(node, directive_defn) - super - end - - private - - def assert_required_args(ast_node, defn) - args = @context.query.types.arguments(defn) - return if args.empty? - present_argument_names = ast_node.arguments.map(&:name) - required_argument_names = context.query.types.arguments(defn) - .select { |a| a.type.kind.non_null? && !a.default_value? && context.query.types.argument(defn, a.name) } - .map!(&:name) - - missing_names = required_argument_names - present_argument_names - if !missing_names.empty? - add_error(GraphQL::StaticValidation::RequiredArgumentsArePresentError.new( - "#{ast_node.class.name.split("::").last} '#{ast_node.name}' is missing required arguments: #{missing_names.join(", ")}", - nodes: ast_node, - class_name: ast_node.class.name.split("::").last, - name: ast_node.name, - arguments: "#{missing_names.join(", ")}" - )) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb deleted file mode 100644 index 828bd3482c1..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class RequiredArgumentsArePresentError < StaticValidation::Error - attr_reader :class_name - attr_reader :name - attr_reader :arguments - - def initialize(message, path: nil, nodes: [], class_name:, name:, arguments:) - super(message, path: path, nodes: nodes) - @class_name = class_name - @name = name - @arguments = arguments - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "className" => class_name, - "name" => name, - "arguments" => arguments - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "missingRequiredArguments" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb deleted file mode 100644 index 08461f49309..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module RequiredInputObjectAttributesArePresent - def on_input_object(node, parent) - if parent.is_a? GraphQL::Language::Nodes::Argument - validate_input_object(node, context, parent) - end - super - end - - private - - def get_parent_type(context, parent) - # If argument_definition is defined we're at nested object - # and need to refer to the containing input object type rather - # than the field_definition. - # h/t @rmosolgo - arg_defn = context.argument_definition - - # Double checking that arg_defn is an input object as nested - # scalars, namely JSON, can make it to this branch - defn = if arg_defn && arg_defn.type.unwrap.kind.input_object? - arg_defn.type.unwrap - else - context.directive_definition || context.field_definition - end - - parent_type = context.types.argument(defn, parent_name(parent, defn)) - parent_type ? parent_type.type.unwrap : nil - end - - def validate_input_object(ast_node, context, parent) - parent_type = get_parent_type(context, parent) - return unless parent_type && parent_type.kind.input_object? - - required_fields = context.types.arguments(parent_type) - .select{ |arg| arg.type.kind.non_null? && !arg.default_value? } - .map!(&:graphql_name) - - present_fields = ast_node.arguments.map(&:name) - missing_fields = required_fields - present_fields - - missing_fields.each do |missing_field| - path = [*context.path, missing_field] - missing_field_type = context.types.argument(parent_type, missing_field).type - add_error(RequiredInputObjectAttributesArePresentError.new( - "Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}", - argument_name: missing_field, - argument_type: missing_field_type.to_type_signature, - input_object_type: parent_type.to_type_signature, - path: path, - nodes: ast_node, - )) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb deleted file mode 100644 index 581d0a20e11..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class RequiredInputObjectAttributesArePresentError < StaticValidation::Error - attr_reader :argument_type - attr_reader :argument_name - attr_reader :input_object_type - - def initialize(message, path:, nodes:, argument_type:, argument_name:, input_object_type:) - super(message, path: path, nodes: nodes) - @argument_type = argument_type - @argument_name = argument_name - @input_object_type = input_object_type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "argumentName" => argument_name, - "argumentType" => argument_type, - "inputObjectType" => input_object_type, - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "missingRequiredInputObjectAttribute" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists.rb deleted file mode 100644 index b3c41748f11..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module SubscriptionRootExists - def on_operation_definition(node, _parent) - if node.operation_type == "subscription" && context.types.subscription_root.nil? - add_error(GraphQL::StaticValidation::SubscriptionRootExistsError.new( - 'Schema is not configured for subscriptions', - nodes: node - )) - else - super - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists_error.rb deleted file mode 100644 index ca2f46c0d9b..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class SubscriptionRootExistsError < StaticValidation::Error - - def initialize(message, path: nil, nodes: []) - super(message, path: path, nodes: nodes) - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "missingSubscriptionConfiguration" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location.rb deleted file mode 100644 index 2d043a406b6..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module UniqueDirectivesPerLocation - DIRECTIVE_NODE_HOOKS = [ - :on_fragment_definition, - :on_fragment_spread, - :on_inline_fragment, - :on_operation_definition, - :on_scalar_type_definition, - :on_object_type_definition, - :on_input_value_definition, - :on_field_definition, - :on_interface_type_definition, - :on_union_type_definition, - :on_enum_type_definition, - :on_enum_value_definition, - :on_input_object_type_definition, - :on_field, - ] - - DIRECTIVE_NODE_HOOKS.each do |method_name| - define_method(method_name) do |node, parent| - if !node.directives.empty? - validate_directive_location(node) - end - super(node, parent) - end - end - - private - - def validate_directive_location(node) - used_directives = {} - node.directives.each do |ast_directive| - directive_name = ast_directive.name - if (first_node = used_directives[directive_name]) - @directives_are_unique_errors_by_first_node ||= {} - err = @directives_are_unique_errors_by_first_node[first_node] ||= begin - error = GraphQL::StaticValidation::UniqueDirectivesPerLocationError.new( - "The directive \"#{directive_name}\" can only be used once at this location.", - nodes: [used_directives[directive_name]], - directive: directive_name, - ) - add_error(error) - error - end - err.nodes << ast_directive - elsif !((dir_defn = context.schema_directives[directive_name]) && dir_defn.repeatable?) - used_directives[directive_name] = ast_directive - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb deleted file mode 100644 index 68d9944aab8..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class UniqueDirectivesPerLocationError < StaticValidation::Error - attr_reader :directive_name - - def initialize(message, path: nil, nodes: [], directive:) - super(message, path: path, nodes: nodes) - @directive_name = directive - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "directiveName" => directive_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "directiveNotUniqueForLocation" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb deleted file mode 100644 index ba6bb3c108a..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module VariableDefaultValuesAreCorrectlyTyped - def on_variable_definition(node, parent) - if !node.default_value.nil? - value = node.default_value - type = context.schema.type_from_ast(node.type, context: context) - if type.nil? - # This is handled by another validator - else - validation_result = context.validate_literal(value, type) - - if !validation_result.valid? - problems = validation_result.problems - first_problem = problems && problems.first - if first_problem - error_message = first_problem["explanation"] - end - - error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}" - add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new( - error_message, - nodes: node, - name: node.name, - type: type.to_type_signature, - error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE], - )) - end - end - end - - super - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb deleted file mode 100644 index 604fe358953..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class VariableDefaultValuesAreCorrectlyTypedError < StaticValidation::Error - attr_reader :variable_name - attr_reader :type_name - attr_reader :violation - - VIOLATIONS = { - :INVALID_TYPE => "defaultValueInvalidType", - :INVALID_ON_NON_NULL => "defaultValueInvalidOnNonNullVariable", - } - - def initialize(message, path: nil, nodes: [], name:, type: nil, error_type:) - super(message, path: path, nodes: nodes) - @variable_name = name - @type_name = type - raise("Unexpected error type: #{error_type}") if !VIOLATIONS.values.include?(error_type) - @violation = error_type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "variableName" => variable_name - }.tap { |h| h["typeName"] = type_name unless type_name.nil? } - - super.merge({ - "extensions" => extensions - }) - end - - def code - @violation - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique.rb deleted file mode 100644 index 8b1b44fb9f5..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module VariableNamesAreUnique - def on_operation_definition(node, parent) - var_defns = node.variables - if !var_defns.empty? - vars_by_name = Hash.new { |h, k| h[k] = [] } - var_defns.each { |v| vars_by_name[v.name] << v } - vars_by_name.each do |name, defns| - if defns.size > 1 - add_error(GraphQL::StaticValidation::VariableNamesAreUniqueError.new( - "There can only be one variable named \"#{name}\"", - nodes: defns, - name: name - )) - end - end - end - super - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb deleted file mode 100644 index f9f9cd2468d..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class VariableNamesAreUniqueError < StaticValidation::Error - attr_reader :variable_name - - def initialize(message, path: nil, nodes: [], name:) - super(message, path: path, nodes: nodes) - @variable_name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "variableName" => variable_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "variableNotUnique" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb deleted file mode 100644 index af2343753da..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +++ /dev/null @@ -1,159 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module VariableUsagesAreAllowed - def initialize(*) - super - # holds { name => ast_node } pairs - @declared_variables = {} - end - - def on_operation_definition(node, parent) - @declared_variables = node.variables.each_with_object({}) { |var, memo| memo[var.name] = var } - super - end - - def on_argument(node, parent) - node_values = if node.value.is_a?(Array) - node.value - else - [node.value] - end - node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier } - - if !node_values.empty? - argument_owner = case parent - when GraphQL::Language::Nodes::Field - context.field_definition - when GraphQL::Language::Nodes::Directive - context.directive_definition - when GraphQL::Language::Nodes::InputObject - arg_type = context.argument_definition.type.unwrap - if arg_type.kind.input_object? - arg_type - else - # This is some kind of error - nil - end - else - raise("Unexpected argument parent: #{parent}") - end - - node_values.each do |node_value| - var_defn_ast = @declared_variables[node_value.name] - # Might be undefined :( - # VariablesAreUsedAndDefined can't finalize its search until the end of the document. - var_defn_ast && argument_owner && validate_usage(argument_owner, node, var_defn_ast) - end - end - super - end - - private - - def validate_usage(argument_owner, arg_node, ast_var) - var_type = context.schema.type_from_ast(ast_var.type, context: context) - if var_type.nil? - return - end - if !ast_var.default_value.nil? - unless var_type.kind.non_null? - # If the value is required, but the argument is not, - # and yet there's a non-nil default, then we impliclty - # make the argument also a required type. - var_type = var_type.to_non_null_type - end - end - - arg_defn = @types.argument(argument_owner, arg_node.name) - arg_defn_type = arg_defn.type - - # If the argument is non-null, but it was given a default value, - # then treat it as nullable in practice, see https://github.com/rmosolgo/graphql-ruby/issues/3793 - if arg_defn_type.non_null? && arg_defn.default_value? - arg_defn_type = arg_defn_type.of_type - end - - var_inner_type = var_type.unwrap - arg_inner_type = arg_defn_type.unwrap - - var_type = wrap_var_type_with_depth_of_arg(var_type, arg_node) - - if var_inner_type != arg_inner_type - create_error("Type mismatch", var_type, ast_var, arg_defn, arg_node) - elsif list_dimension(var_type) != list_dimension(arg_defn_type) - create_error("List dimension mismatch", var_type, ast_var, arg_defn, arg_node) - elsif !non_null_levels_match(arg_defn_type, var_type) - create_error("Nullability mismatch", var_type, ast_var, arg_defn, arg_node) - end - end - - def create_error(error_message, var_type, ast_var, arg_defn, arg_node) - add_error(GraphQL::StaticValidation::VariableUsagesAreAllowedError.new( - "#{error_message} on variable $#{ast_var.name} and argument #{arg_node.name} (#{var_type.to_type_signature} / #{arg_defn.type.to_type_signature})", - nodes: arg_node, - name: ast_var.name, - type: var_type.to_type_signature, - argument: arg_node.name, - error: error_message - )) - end - - def wrap_var_type_with_depth_of_arg(var_type, arg_node) - arg_node_value = arg_node.value - return var_type unless arg_node_value.is_a?(Array) - new_var_type = var_type - - depth_of_array(arg_node_value).times do - # Since the array _is_ present, treat it like a non-null type - # (It satisfies a non-null requirement AND a nullable requirement) - new_var_type = new_var_type.to_list_type.to_non_null_type - end - - new_var_type - end - - # @return [Integer] Returns the max depth of `array`, or `0` if it isn't an array at all - def depth_of_array(array) - case array - when Array - max_child_depth = 0 - array.each do |item| - item_depth = depth_of_array(item) - if item_depth > max_child_depth - max_child_depth = item_depth - end - end - 1 + max_child_depth - else - 0 - end - end - - def list_dimension(type) - if type.kind.list? - 1 + list_dimension(type.of_type) - elsif type.kind.non_null? - list_dimension(type.of_type) - else - 0 - end - end - - def non_null_levels_match(arg_type, var_type) - if arg_type.kind.non_null? && !var_type.kind.non_null? - false - elsif arg_type.kind.wraps? && var_type.kind.wraps? - # If var_type is a non-null wrapper for a type, and arg_type is nullable, peel off the wrapper - # That way, a var_type of `[DairyAnimal]!` works with an arg_type of `[DairyAnimal]` - if var_type.kind.non_null? && !arg_type.kind.non_null? - var_type = var_type.of_type - end - non_null_levels_match(arg_type.of_type, var_type.of_type) - else - true - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb deleted file mode 100644 index b6a8eec2f4f..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class VariableUsagesAreAllowedError < StaticValidation::Error - attr_reader :type_name - attr_reader :variable_name - attr_reader :argument_name - attr_reader :error_message - - def initialize(message, path: nil, nodes: [], type:, name:, argument:, error:) - super(message, path: path, nodes: nodes) - @type_name = type - @variable_name = name - @argument_name = argument - @error_message = error - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "variableName" => variable_name, - "typeName" => type_name, - "argumentName" => argument_name, - "errorMessage" => error_message - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "variableMismatch" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types.rb deleted file mode 100644 index e4400fd2dde..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - module VariablesAreInputTypes - def on_variable_definition(node, parent) - type_name = get_type_name(node.type) - type = context.query.types.type(type_name) - - if type.nil? - @all_possible_input_type_names ||= begin - names = [] - context.types.all_types.each { |(t)| - if t.kind.input? - names << t.graphql_name - end - } - names - end - add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new( - "#{type_name} isn't a defined input type (on $#{node.name})#{context.did_you_mean_suggestion(type_name, @all_possible_input_type_names)}", - nodes: node, - name: node.name, - type: type_name - )) - elsif !type.kind.input? - add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new( - "#{type.graphql_name} isn't a valid input type (on $#{node.name})", - nodes: node, - name: node.name, - type: type_name - )) - end - - super - end - - private - - def get_type_name(ast_type) - if ast_type.respond_to?(:of_type) - get_type_name(ast_type.of_type) - else - ast_type.name - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types_error.rb deleted file mode 100644 index d3d6ce08b18..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class VariablesAreInputTypesError < StaticValidation::Error - attr_reader :type_name - attr_reader :variable_name - - def initialize(message, path: nil, nodes: [], type:, name:) - super(message, path: path, nodes: nodes) - @type_name = type - @variable_name = name - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "typeName" => type_name, - "variableName" => variable_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "variableRequiresValidType" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb deleted file mode 100644 index d1f80d09b5f..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # The problem is - # - Variable $usage must be determined at the OperationDefinition level - # - You can't tell how fragments use variables until you visit FragmentDefinitions (which may be at the end of the document) - # - # So, this validator includes some crazy logic to follow fragment spreads recursively, while avoiding infinite loops. - # - # `graphql-js` solves this problem by: - # - re-visiting the AST for each validator - # - allowing validators to say `followSpreads: true` - # - module VariablesAreUsedAndDefined - class VariableUsage - attr_accessor :ast_node, :used_by, :declared_by, :path - def used? - !!@used_by - end - - def declared? - !!@declared_by - end - end - - def initialize(*) - super - @variable_usages_for_context = Hash.new {|hash, key| hash[key] = Hash.new {|h, k| h[k] = VariableUsage.new } } - @spreads_for_context = Hash.new {|hash, key| hash[key] = [] } - @variable_context_stack = [] - end - - def on_operation_definition(node, parent) - # initialize the hash of vars for this context: - @variable_usages_for_context[node] - @variable_context_stack.push(node) - # mark variables as defined: - var_hash = @variable_usages_for_context[node] - node.variables.each { |var| - var_usage = var_hash[var.name] - var_usage.declared_by = node - var_usage.path = context.path - } - super - @variable_context_stack.pop - end - - def on_fragment_definition(node, parent) - # initialize the hash of vars for this context: - @variable_usages_for_context[node] - @variable_context_stack.push(node) - super - @variable_context_stack.pop - end - - # For FragmentSpreads: - # - find the context on the stack - # - mark the context as containing this spread - def on_fragment_spread(node, parent) - variable_context = @variable_context_stack.last - @spreads_for_context[variable_context] << node.name - super - end - - # For VariableIdentifiers: - # - mark the variable as used - # - assign its AST node - def on_variable_identifier(node, parent) - usage_context = @variable_context_stack.last - declared_variables = @variable_usages_for_context[usage_context] - usage = declared_variables[node.name] - usage.used_by = usage_context - usage.ast_node = node - usage.path = context.path - super - end - - def on_document(node, parent) - super - fragment_definitions = @variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::FragmentDefinition) } - operation_definitions = @variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::OperationDefinition) } - - operation_definitions.each do |node, node_variables| - follow_spreads(node, node_variables, @spreads_for_context, fragment_definitions, []) - create_errors(node_variables) - end - end - - private - - # Follow spreads in `node`, looking them up from `spreads_for_context` and finding their match in `fragment_definitions`. - # Use those fragments to update {VariableUsage}s in `parent_variables`. - # Avoid infinite loops by skipping anything in `visited_fragments`. - def follow_spreads(node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments) - spreads = spreads_for_context[node] - visited_fragments - spreads.each do |spread_name| - def_node = nil - variables = nil - # Implement `.find` by hand to avoid Ruby's internal allocations - fragment_definitions.each do |frag_def_node, vars| - if frag_def_node.name == spread_name - def_node = frag_def_node - variables = vars - break - end - end - - next if !def_node - visited_fragments << spread_name - variables.each do |name, child_usage| - parent_usage = parent_variables[name] - if child_usage.used? - parent_usage.ast_node = child_usage.ast_node - parent_usage.used_by = child_usage.used_by - parent_usage.path = child_usage.path - end - end - follow_spreads(def_node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments) - end - end - - # Determine all the error messages, - # Then push messages into the validation context - def create_errors(node_variables) - # Declared but not used: - node_variables - .select { |name, usage| usage.declared? && !usage.used? } - .each { |var_name, usage| - declared_by_error_name = usage.declared_by.name || "anonymous #{usage.declared_by.operation_type}" - add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new( - "Variable $#{var_name} is declared by #{declared_by_error_name} but not used", - nodes: usage.declared_by, - path: usage.path, - name: var_name, - error_type: VariablesAreUsedAndDefinedError::VIOLATIONS[:VARIABLE_NOT_USED] - )) - } - - # Used but not declared: - node_variables - .select { |name, usage| usage.used? && !usage.declared? } - .each { |var_name, usage| - used_by_error_name = usage.used_by.name || "anonymous #{usage.used_by.operation_type}" - add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new( - "Variable $#{var_name} is used by #{used_by_error_name} but not declared", - nodes: usage.ast_node, - path: usage.path, - name: var_name, - error_type: VariablesAreUsedAndDefinedError::VIOLATIONS[:VARIABLE_NOT_DEFINED] - )) - } - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb deleted file mode 100644 index b005b645edd..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class VariablesAreUsedAndDefinedError < StaticValidation::Error - attr_reader :variable_name - attr_reader :violation - - VIOLATIONS = { - :VARIABLE_NOT_USED => "variableNotUsed", - :VARIABLE_NOT_DEFINED => "variableNotDefined", - } - - def initialize(message, path: nil, nodes: [], name:, error_type:) - super(message, path: path, nodes: nodes) - @variable_name = name - raise("Unexpected error type: #{error_type}") if !VIOLATIONS.values.include?(error_type) - @violation = error_type - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code, - "variableName" => variable_name - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - @violation - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/validation_context.rb b/vendor/gems/graphql/lib/graphql/static_validation/validation_context.rb deleted file mode 100644 index c9fcd1cde72..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/validation_context.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - # The validation context gets passed to each validator. - # - # It exposes a {GraphQL::Language::Visitor} where validators may add hooks. ({Language::Visitor#visit} is called in {Validator#validate}) - # - # It provides access to the schema & fragments which validators may read from. - # - # It holds a list of errors which each validator may add to. - class ValidationContext - extend Forwardable - - attr_reader :query, :errors, :visitor, - :on_dependency_resolve_handlers, - :max_errors, :types, :schema - - - def_delegators :@query, :document, :fragments, :operations - - def initialize(query, visitor_class, max_errors) - @query = query - @types = query.types # TODO update migrated callers to use this accessor - @schema = query.schema - @literal_validator = LiteralValidator.new(context: query.context) - @errors = [] - @max_errors = max_errors || Float::INFINITY - @on_dependency_resolve_handlers = [] - @visitor = visitor_class.new(document, self) - end - - # TODO stop using def_delegators because of Array allocations - def_delegators :@visitor, - :path, :type_definition, :field_definition, :argument_definition, - :parent_type_definition, :directive_definition, :object_types, :dependencies - - def on_dependency_resolve(&handler) - @on_dependency_resolve_handlers << handler - end - - def validate_literal(ast_value, type) - @literal_validator.validate(ast_value, type) - end - - def too_many_errors? - @errors.length >= @max_errors - end - - def schema_directives - @schema_directives ||= schema.directives - end - - def did_you_mean_suggestion(name, options) - if did_you_mean = schema.did_you_mean - suggestions = did_you_mean::SpellChecker.new(dictionary: options).correct(name) - case suggestions.size - when 0 - "" - when 1 - " (Did you mean `#{suggestions.first}`?)" - else - last_sugg = suggestions.pop - " (Did you mean #{suggestions.map {|s| "`#{s}`"}.join(", ")} or `#{last_sugg}`?)" - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/validation_timeout_error.rb b/vendor/gems/graphql/lib/graphql/static_validation/validation_timeout_error.rb deleted file mode 100644 index e15cb8ea685..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/validation_timeout_error.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module StaticValidation - class ValidationTimeoutError < StaticValidation::Error - def initialize(message, path: nil, nodes: []) - super(message, path: path, nodes: nodes) - end - - # A hash representation of this Message - def to_h - extensions = { - "code" => code - } - - super.merge({ - "extensions" => extensions - }) - end - - def code - "validationTimeout" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/static_validation/validator.rb b/vendor/gems/graphql/lib/graphql/static_validation/validator.rb deleted file mode 100644 index 2cd83a4dab6..00000000000 --- a/vendor/gems/graphql/lib/graphql/static_validation/validator.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true -require "timeout" - -module GraphQL - module StaticValidation - # Initialized with a {GraphQL::Schema}, then it can validate {GraphQL::Language::Nodes::Documents}s based on that schema. - # - # By default, it's used by {GraphQL::Query} - # - # @example Validate a query - # validator = GraphQL::StaticValidation::Validator.new(schema: MySchema) - # query = GraphQL::Query.new(MySchema, query_string) - # errors = validator.validate(query)[:errors] - # - class Validator - # @param schema [GraphQL::Schema] - # @param rules [Array<#validate(context)>] a list of rules to use when validating - def initialize(schema:, rules: GraphQL::StaticValidation::ALL_RULES) - @schema = schema - @rules = rules - end - - # Validate `query` against the schema. Returns an array of message hashes. - # @param query [GraphQL::Query] - # @param validate [Boolean] - # @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds. - # @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. - # @return [Array] - def validate(query, validate: true, timeout: nil, max_errors: nil) - errors = nil - query.current_trace.begin_validate(query, validate) - query.current_trace.validate(validate: validate, query: query) do - begin_t = Time.now - errors = if validate == false - [] - else - rules_to_use = validate ? @rules : [] - visitor_class = BaseVisitor.including_rules(rules_to_use) - - context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors) - - begin - # 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. - # A timeout value of 0 or nil will execute the block without any timeout. - Timeout::timeout(timeout) do - catch(:too_many_validation_errors) do - context.visitor.visit - end - end - rescue Timeout::Error - handle_timeout(query, context) - end - - context.errors - end - - { - remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil, - errors: errors, - } - end - rescue GraphQL::ExecutionError => e - errors = [e] - { - remaining_timeout: nil, - errors: errors, - } - ensure - query.current_trace.end_validate(query, validate, errors) - end - - # Invoked when static validation times out. - # @param query [GraphQL::Query] - # @param context [GraphQL::StaticValidation::ValidationContext] - def handle_timeout(query, context) - context.errors << GraphQL::StaticValidation::ValidationTimeoutError.new( - "Timeout on validation of query" - ) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/string_encoding_error.rb b/vendor/gems/graphql/lib/graphql/string_encoding_error.rb deleted file mode 100644 index d177e45bbc5..00000000000 --- a/vendor/gems/graphql/lib/graphql/string_encoding_error.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class StringEncodingError < GraphQL::RuntimeTypeError - attr_reader :string, :field, :path - def initialize(str, context:) - @string = str - @field = context[:current_field] - @path = context[:current_path] - message = "String #{str.inspect} was encoded as #{str.encoding}".dup - if @path - message << " @ #{@path.join(".")}" - end - if @field - message << " (#{@field.path})" - end - message << ". GraphQL requires an encoding compatible with UTF-8." - super(message) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/subscriptions.rb b/vendor/gems/graphql/lib/graphql/subscriptions.rb deleted file mode 100644 index a39d422d87f..00000000000 --- a/vendor/gems/graphql/lib/graphql/subscriptions.rb +++ /dev/null @@ -1,315 +0,0 @@ -# frozen_string_literal: true -require "securerandom" -require "graphql/subscriptions/broadcast_analyzer" -require "graphql/subscriptions/event" -require "graphql/subscriptions/serialize" -require "graphql/subscriptions/action_cable_subscriptions" -require "graphql/subscriptions/default_subscription_resolve_extension" - -module GraphQL - class Subscriptions - # Raised when either: - # - the triggered `event_name` doesn't match a field in the schema; or - # - one or more arguments don't match the field arguments - class InvalidTriggerError < GraphQL::Error - end - - # Raised when either: - # - An initial subscription didn't have a value for `context[subscription_scope]` - # - Or, an update didn't pass `.trigger(..., scope:)` - # When raised, the initial subscription or update fails completely. - class SubscriptionScopeMissingError < GraphQL::Error - end - - # @see {Subscriptions#initialize} for options, concrete implementations may add options. - def self.use(defn, options = {}) - schema = defn.is_a?(Class) ? defn : defn.target - - if schema.subscriptions(inherited: false) - raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}" - end - - options[:schema] = schema - schema.subscriptions = self.new(**options) - schema.add_subscription_extension_if_necessary - nil - end - - # @param schema [Class] the GraphQL schema this manager belongs to - # @param validate_update [Boolean] If false, then validation is skipped when executing updates - def initialize(schema:, validate_update: true, broadcast: false, default_broadcastable: false, **rest) - if broadcast - schema.query_analyzer(Subscriptions::BroadcastAnalyzer) - end - @default_broadcastable = default_broadcastable - @schema = schema - @validate_update = validate_update - end - - # @return [Boolean] Used when fields don't have `broadcastable:` explicitly set - attr_reader :default_broadcastable - - # Fetch subscriptions matching this field + arguments pair - # And pass them off to the queue. - # @param event_name [String] - # @param args [Hash Object] - # @param object [Object] - # @param scope [Symbol, String] - # @param context [Hash] - # @return [void] - def trigger(event_name, args, object, scope: nil, context: {}) - # Make something as context-like as possible, even though there isn't a current query: - dummy_query = @schema.query_class.new(@schema, "{ __typename }", validate: false, context: context) - context = dummy_query.context - event_name = event_name.to_s - - # Try with the verbatim input first: - field = dummy_query.types.field(@schema.subscription, event_name) # rubocop:disable Development/ContextIsPassedCop - - if field.nil? - # And if it wasn't found, normalize it: - normalized_event_name = normalize_name(event_name) - field = dummy_query.types.field(@schema.subscription, normalized_event_name) # rubocop:disable Development/ContextIsPassedCop - if field.nil? - raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})" - end - else - # Since we found a field, the original input was already normalized - normalized_event_name = event_name - end - - # Normalize symbol-keyed args to strings, try camelizing them - # Should this accept a real context somehow? - normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext.instance) - - event = Subscriptions::Event.new( - name: normalized_event_name, - arguments: normalized_args, - field: field, - scope: scope, - context: context, - ) - execute_all(event, object) - end - - # `event` was triggered on `object`, and `subscription_id` was subscribed, - # so it should be updated. - # - # Load `subscription_id`'s GraphQL data, re-evaluate the query and return the result. - # - # @param subscription_id [String] - # @param event [GraphQL::Subscriptions::Event] The event which was triggered - # @param object [Object] The value for the subscription field - # @return [GraphQL::Query::Result] - def execute_update(subscription_id, event, object) - # Lookup the saved data for this subscription - query_data = read_subscription(subscription_id) - if query_data.nil? - delete_subscription(subscription_id) - return nil - end - - # Fetch the required keys from the saved data - query_string = query_data.fetch(:query_string) - variables = query_data.fetch(:variables) - context = query_data.fetch(:context) - operation_name = query_data.fetch(:operation_name) - execute_options = { - query: query_string, - context: context, - subscription_topic: event.topic, - operation_name: operation_name, - variables: variables, - root_value: object, - } - - # merge event's and query's context together - context.merge!(event.context) unless event.context.nil? || context.nil? - - execute_options[:validate] = validate_update?(**execute_options) - result = @schema.execute(**execute_options) - subscriptions_context = result.context.namespace(:subscriptions) - if subscriptions_context[:no_update] - result = nil - end - - if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update] - # `unsubscribe` was called, clean up on our side - # The transport should also send `{more: false}` to client - delete_subscription(subscription_id) - result = nil - end - - result - end - - # Define this method to customize whether to validate - # this subscription when executing an update. - # - # @return [Boolean] defaults to `true`, or false if `validate: false` is provided. - def validate_update?(query:, context:, root_value:, subscription_topic:, operation_name:, variables:) - @validate_update - end - - # Run the update query for this subscription and deliver it - # @see {#execute_update} - # @see {#deliver} - # @return [void] - def execute(subscription_id, event, object) - res = execute_update(subscription_id, event, object) - if !res.nil? - deliver(subscription_id, res) - - if res.context.namespace(:subscriptions)[:unsubscribed] - # `unsubscribe` was called, clean up on our side - # The transport should also send `{more: false}` to client - delete_subscription(subscription_id) - end - end - - end - - # Event `event` occurred on `object`, - # Update all subscribers. - # @param event [Subscriptions::Event] - # @param object [Object] - # @return [void] - def execute_all(event, object) - raise GraphQL::RequiredImplementationMissingError - end - - # The system wants to send an update to this subscription. - # Read its data and return it. - # @param subscription_id [String] - # @return [Hash] Containing required keys - def read_subscription(subscription_id) - raise GraphQL::RequiredImplementationMissingError - end - - # A subscription query was re-evaluated, returning `result`. - # The result should be send to `subscription_id`. - # @param subscription_id [String] - # @param result [Hash] - # @return [void] - def deliver(subscription_id, result) - raise GraphQL::RequiredImplementationMissingError - end - - # `query` was executed and found subscriptions to `events`. - # Update the database to reflect this new state. - # @param query [GraphQL::Query] - # @param events [Array] - # @return [void] - def write_subscription(query, events) - raise GraphQL::RequiredImplementationMissingError - end - - # A subscription was terminated server-side. - # Clean up the database. - # @param subscription_id [String] - # @return void. - def delete_subscription(subscription_id) - raise GraphQL::RequiredImplementationMissingError - end - - # @return [String] A new unique identifier for a subscription - def build_id - SecureRandom.uuid - end - - # Convert a user-provided event name or argument - # to the equivalent in GraphQL. - # - # By default, it converts the identifier to camelcase. - # Override this in a subclass to change the transformation. - # - # @param event_or_arg_name [String, Symbol] - # @return [String] - def normalize_name(event_or_arg_name) - Schema::Member::BuildType.camelize(event_or_arg_name.to_s) - end - - # @return [Boolean] if true, then a query like this one would be broadcasted - def broadcastable?(query_str, **query_options) - query = @schema.query_class.new(@schema, query_str, **query_options) - if !query.valid? - raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}" - end - GraphQL::Analysis.analyze_query(query, @schema.query_analyzers) - query.context.namespace(:subscriptions)[:subscription_broadcastable] - end - - private - - # Recursively normalize `args` as belonging to `arg_owner`: - # - convert symbols to strings, - # - if needed, camelize the string (using {#normalize_name}) - # @param arg_owner [GraphQL::Field, GraphQL::BaseType] - # @param args [Hash, Array, Any] some GraphQL input value to coerce as `arg_owner` - # @return [Any] normalized arguments value - def normalize_arguments(event_name, arg_owner, args, context) - case arg_owner - when GraphQL::Schema::Field, Class - return args if args.nil? - - if arg_owner.is_a?(Class) && !arg_owner.kind.input_object? - # it's a type, but not an input object - return args - end - normalized_args = {} - missing_arg_names = [] - args.each do |k, v| - arg_name = k.to_s - arg_defn = arg_owner.get_argument(arg_name, context) - if arg_defn - normalized_arg_name = arg_name - else - normalized_arg_name = normalize_name(arg_name) - arg_defn = arg_owner.get_argument(normalized_arg_name, context) - end - - if arg_defn - if arg_defn.loads - normalized_arg_name = arg_defn.keyword.to_s - end - normalized = normalize_arguments(event_name, arg_defn.type, v, context) - normalized_args[normalized_arg_name] = normalized - else - # Couldn't find a matching argument definition - missing_arg_names << arg_name - end - end - - # Backfill default values so that trigger arguments - # match query arguments. - arg_owner.arguments(context).each do |_name, arg_defn| - if arg_defn.default_value? && !normalized_args.key?(arg_defn.name) - default_value = arg_defn.default_value - # We don't have an underlying "object" here, so it can't call methods. - # This is broken. - normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: context) - end - end - - if !missing_arg_names.empty? - arg_owner_name = if arg_owner.is_a?(GraphQL::Schema::Field) - arg_owner.path - elsif arg_owner.is_a?(Class) - arg_owner.graphql_name - else - arg_owner.to_s - end - raise InvalidTriggerError, "Can't trigger Subscription.#{event_name}, received undefined arguments: #{missing_arg_names.join(", ")}. (Should match arguments of #{arg_owner_name}.)" - end - - normalized_args - when GraphQL::Schema::List - args&.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) } - when GraphQL::Schema::NonNull - normalize_arguments(event_name, arg_owner.of_type, args, context) - else - args - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/subscriptions/action_cable_subscriptions.rb b/vendor/gems/graphql/lib/graphql/subscriptions/action_cable_subscriptions.rb deleted file mode 100644 index 5c015178777..00000000000 --- a/vendor/gems/graphql/lib/graphql/subscriptions/action_cable_subscriptions.rb +++ /dev/null @@ -1,255 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Subscriptions - # A subscriptions implementation that sends data - # as ActionCable broadcastings. - # - # Some things to keep in mind: - # - # - No queueing system; ActiveJob should be added - # - Take care to reload context when re-delivering the subscription. (see {Query#subscription_update?}) - # - 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. - # - # @example Adding ActionCableSubscriptions to your schema - # class MySchema < GraphQL::Schema - # # ... - # use GraphQL::Subscriptions::ActionCableSubscriptions - # end - # - # @example Implementing a channel for GraphQL Subscriptions - # class GraphqlChannel < ApplicationCable::Channel - # def subscribed - # @subscription_ids = [] - # end - # - # def execute(data) - # query = data["query"] - # variables = ensure_hash(data["variables"]) - # operation_name = data["operationName"] - # context = { - # # Re-implement whatever context methods you need - # # in this channel or ApplicationCable::Channel - # # current_user: current_user, - # # Make sure the channel is in the context - # channel: self, - # } - # - # result = MySchema.execute( - # query, - # context: context, - # variables: variables, - # operation_name: operation_name - # ) - # - # payload = { - # result: result.to_h, - # more: result.subscription?, - # } - # - # # Track the subscription here so we can remove it - # # on unsubscribe. - # if result.context[:subscription_id] - # @subscription_ids << result.context[:subscription_id] - # end - # - # transmit(payload) - # end - # - # def unsubscribed - # @subscription_ids.each { |sid| - # MySchema.subscriptions.delete_subscription(sid) - # } - # end - # - # private - # - # def ensure_hash(ambiguous_param) - # case ambiguous_param - # when String - # if ambiguous_param.present? - # ensure_hash(JSON.parse(ambiguous_param)) - # else - # {} - # end - # when Hash, ActionController::Parameters - # ambiguous_param - # when nil - # {} - # else - # raise ArgumentError, "Unexpected parameter: #{ambiguous_param}" - # end - # end - # end - # - class ActionCableSubscriptions < GraphQL::Subscriptions - SUBSCRIPTION_PREFIX = "graphql-subscription:" - EVENT_PREFIX = "graphql-event:" - - # @param serializer [<#dump(obj), #load(string)] Used for serializing messages before handing them to `.broadcast(msg)` - # @param namespace [string] Used to namespace events and subscriptions (default: '') - def initialize(serializer: Serialize, namespace: '', action_cable: ActionCable, action_cable_coder: ActiveSupport::JSON, **rest) - # A per-process map of subscriptions to deliver. - # This is provided by Rails, so let's use it - @subscriptions = Concurrent::Map.new - @events = Concurrent::Map.new do |h, k| - h.compute_if_absent(k) do - Concurrent::Map.new do |h2, k2| - h2.compute_if_absent(k2) { Concurrent::Array.new } - end - end - end - @action_cable = action_cable - @action_cable_coder = action_cable_coder - @serializer = serializer - @serialize_with_context = case @serializer.method(:load).arity - when 1 - false - when 2 - true - else - raise ArgumentError, "#{@serializer} must respond to `.load` accepting one or two arguments" - end - @transmit_ns = namespace - super - end - - # An event was triggered; Push the data over ActionCable. - # Subscribers will re-evaluate locally. - def execute_all(event, object) - stream = stream_event_name(event) - message = @serializer.dump(object) - @action_cable.server.broadcast(stream, message) - end - - # This subscription was re-evaluated. - # Send it to the specific stream where this client was waiting. - def deliver(subscription_id, result) - has_more = !result.context.namespace(:subscriptions)[:final_update] - payload = { result: result.to_h, more: has_more } - @action_cable.server.broadcast(stream_subscription_name(subscription_id), payload) - end - - # A query was run where these events were subscribed to. - # Store them in memory in _this_ ActionCable frontend. - # It will receive notifications when events come in - # and re-evaluate the query locally. - def write_subscription(query, events) - unless (channel = query.context[:channel]) - raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\ - "by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\ - "Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\ - "Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\ - "GraphiQL via `graphiql-rails` may not work out of box (#1051)." - end - subscription_id = query.context[:subscription_id] ||= build_id - stream = stream_subscription_name(subscription_id) - channel.stream_from(stream) - @subscriptions[subscription_id] = query - events.each do |event| - # Setup a new listener to run all events with this topic in this process - setup_stream(channel, event) - # Add this event to the list of events to be updated - @events[event.topic][event.fingerprint] << event - end - end - - # Every subscribing channel is listening here, but only one of them takes any action. - # This is so we can reuse payloads when possible, and make one payload to send to - # all subscribers. - # - # But the problem is, any channel could close at any time, so each channel has to - # be ready to take over the primary position. - # - # To make sure there's always one-and-only-one channel building payloads, - # let the listener belonging to the first event on the list be - # the one to build and publish payloads. - # - def setup_stream(channel, initial_event) - topic = initial_event.topic - event_stream = stream_event_name(initial_event) - channel.stream_from(event_stream, coder: @action_cable_coder) do |message| - events_by_fingerprint = @events[topic] - object = nil - events_by_fingerprint.each do |_fingerprint, events| - if !events.empty? && events.first == initial_event - # The fingerprint has told us that this response should be shared by all subscribers, - # so just run it once, then deliver the result to every subscriber - first_event = events.first - first_subscription_id = first_event.context.fetch(:subscription_id) - object ||= load_action_cable_message(message, first_event.context) - result = execute_update(first_subscription_id, first_event, object) - if !result.nil? - # Having calculated the result _once_, send the same payload to all subscribers - events.each do |event| - subscription_id = event.context.fetch(:subscription_id) - deliver(subscription_id, result) - end - end - end - end - nil - end - end - - # This is called to turn an ActionCable-broadcasted string (JSON) - # into a query-ready application object. - # @param message [String] n ActionCable-broadcasted string (JSON) - # @param context [GraphQL::Query::Context] the context of the first event for a given subscription fingerprint - def load_action_cable_message(message, context) - if @serialize_with_context - @serializer.load(message, context) - else - @serializer.load(message) - end - end - - # Return the query from "storage" (in memory) - def read_subscription(subscription_id) - query = @subscriptions[subscription_id] - if query.nil? - # This can happen when a subscription is triggered from an unsubscribed channel, - # see https://github.com/rmosolgo/graphql-ruby/issues/2478. - # (This `nil` is handled by `#execute_update`) - nil - else - { - query_string: query.query_string, - variables: query.provided_variables, - context: query.context.to_h, - operation_name: query.operation_name, - } - end - end - - # The channel was closed, forget about it. - def delete_subscription(subscription_id) - query = @subscriptions.delete(subscription_id) - # In case this came from the server, tell the client to unsubscribe: - @action_cable.server.broadcast(stream_subscription_name(subscription_id), { more: false }) - # This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel, - # see https://github.com/rmosolgo/graphql-ruby/issues/2478 - if query - events = query.context.namespace(:subscriptions)[:events] - events.each do |event| - ev_by_fingerprint = @events[event.topic] - ev_for_fingerprint = ev_by_fingerprint[event.fingerprint] - ev_for_fingerprint.delete(event) - if ev_for_fingerprint.empty? - ev_by_fingerprint.delete(event.fingerprint) - end - end - end - end - - private - - def stream_subscription_name(subscription_id) - [SUBSCRIPTION_PREFIX, @transmit_ns, subscription_id].join - end - - def stream_event_name(event) - [EVENT_PREFIX, @transmit_ns, event.topic].join - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/subscriptions/broadcast_analyzer.rb b/vendor/gems/graphql/lib/graphql/subscriptions/broadcast_analyzer.rb deleted file mode 100644 index 3a6a43a8f92..00000000000 --- a/vendor/gems/graphql/lib/graphql/subscriptions/broadcast_analyzer.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - class Subscriptions - # Detect whether the current operation: - # - Is a subscription operation - # - Is completely broadcastable - # - # Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]` - # @api private - # @see Subscriptions#broadcastable? for a public API - class BroadcastAnalyzer < GraphQL::Analysis::Analyzer - def initialize(subject) - super - @default_broadcastable = subject.schema.subscriptions.default_broadcastable - # Maybe this will get set to false while analyzing - @subscription_broadcastable = true - end - - # Only analyze subscription operations - def analyze? - @query.subscription? - end - - def on_enter_field(node, parent, visitor) - if (@subscription_broadcastable == false) || visitor.skipping? - return - end - - current_field = visitor.field_definition - current_type = visitor.parent_type_definition - apply_broadcastable(current_type, current_field) - if current_type.kind.interface? - pt = @query.possible_types(current_type) - pt.each do |object_type| - ot_field = @query.get_field(object_type, current_field.graphql_name) - # Inherited fields would be exactly the same object; - # only check fields that are overrides of the inherited one - if ot_field && ot_field != current_field - apply_broadcastable(object_type, ot_field) - end - end - end - end - - # Assign the result to context. - # (This method is allowed to return an error, but we don't need to) - # @return [void] - def result - query.context.namespace(:subscriptions)[:subscription_broadcastable] = @subscription_broadcastable - nil - end - - private - - # Modify `@subscription_broadcastable` based on `field_defn`'s configuration (and/or the default value) - def apply_broadcastable(owner_type, field_defn) - current_field_broadcastable = field_defn.introspection? || field_defn.broadcastable? - - if current_field_broadcastable.nil? && owner_type.respond_to?(:default_broadcastable?) - current_field_broadcastable = owner_type.default_broadcastable? - end - - case current_field_broadcastable - when nil - query.logger.debug { "`broadcastable: nil` for field: #{field_defn.path}" } - # If the value wasn't set, mix in the default value: - # - If the default is false and the current value is true, make it false - # - If the default is true and the current value is true, it stays true - # - If the default is false and the current value is false, keep it false - # - If the default is true and the current value is false, keep it false - @subscription_broadcastable = @subscription_broadcastable && @default_broadcastable - when false - query.logger.debug { "`broadcastable: false` for field: #{field_defn.path}" } - # One non-broadcastable field is enough to make the whole subscription non-broadcastable - @subscription_broadcastable = false - when true - # Leave `@broadcastable_query` true if it's already true, - # but don't _set_ it to true if it was set to false by something else. - # Actually, just leave it! - else - raise ArgumentError, "Unexpected `.broadcastable?` value for #{field_defn.path}: #{current_field_broadcastable}" - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/subscriptions/default_subscription_resolve_extension.rb b/vendor/gems/graphql/lib/graphql/subscriptions/default_subscription_resolve_extension.rb deleted file mode 100644 index 38e457bea2d..00000000000 --- a/vendor/gems/graphql/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Subscriptions - class DefaultSubscriptionResolveExtension < GraphQL::Schema::FieldExtension - def resolve(context:, object:, arguments:) - has_override_implementation = @field.resolver || - object.respond_to?(@field.resolver_method) - - if !has_override_implementation - if context.query.subscription_update? - object.object - else - context.skip - end - else - yield(object, arguments) - end - end - - def after_resolve(value:, context:, object:, arguments:, **rest) - if value.is_a?(GraphQL::ExecutionError) - value - elsif @field.resolver&.method_defined?(:subscription_written?) && - (subscription_namespace = context.namespace(:subscriptions)) && - (subscriptions_by_path = subscription_namespace[:subscriptions]) - (subscription_instance = subscriptions_by_path[context.current_path]) - # If it was already written, don't append this event to be written later - if !subscription_instance.subscription_written? - events = context.namespace(:subscriptions)[:events] - events << subscription_instance.event - end - value - elsif (events = context.namespace(:subscriptions)[:events]) - # This is the first execution, so gather an Event - # for the backend to register: - event = Subscriptions::Event.new( - name: field.name, - arguments: arguments, - context: context, - field: field, - ) - events << event - value - elsif context.query.subscription_topic == Subscriptions::Event.serialize( - field.name, - arguments, - field, - scope: (field.subscription_scope ? context[field.subscription_scope] : nil), - ) - # This is a subscription update. The resolver returned `skip` if it should be skipped, - # or else it returned an object to resolve the update. - value - else - # This is a subscription update, but this event wasn't triggered. - context.skip - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/subscriptions/event.rb b/vendor/gems/graphql/lib/graphql/subscriptions/event.rb deleted file mode 100644 index d2d8ce7abb0..00000000000 --- a/vendor/gems/graphql/lib/graphql/subscriptions/event.rb +++ /dev/null @@ -1,156 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class Subscriptions - # This thing can be: - # - Subscribed to by `subscription { ... }` - # - Triggered by `MySchema.subscriber.trigger(name, arguments, obj)` - # - class Event - # @return [String] Corresponds to the Subscription root field name - attr_reader :name - - # @return [GraphQL::Execution::Interpreter::Arguments] - attr_reader :arguments - - # @return [GraphQL::Query::Context] - attr_reader :context - - # @return [String] An opaque string which identifies this event, derived from `name` and `arguments` - attr_reader :topic - - def initialize(name:, arguments:, field: nil, context: nil, scope: nil) - @name = name - @arguments = self.class.arguments_without_field_extras(arguments: arguments, field: field) - @context = context - field ||= context.field - scope_key = field.subscription_scope - scope_val = scope || (context && scope_key && context[scope_key]) - if scope_key && - (subscription = field.resolver) && - (subscription.respond_to?(:subscription_scope_optional?)) && - !subscription.subscription_scope_optional? && - scope_val.nil? - raise Subscriptions::SubscriptionScopeMissingError, "#{field.path} (#{subscription}) requires a `scope:` value to trigger updates (Set `subscription_scope ..., optional: true` to disable this requirement)" - end - - @topic = self.class.serialize(name, arguments, field, scope: scope_val, context: context) - end - - # @return [String] an identifier for this unit of subscription - def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance) - subscription = field.resolver || GraphQL::Schema::Subscription - arguments = arguments_without_field_extras(field: field, arguments: arguments) - normalized_args = stringify_args(field, arguments.to_h, context) - subscription.topic_for(arguments: normalized_args, field: field, scope: scope) - end - - # @return [String] a logical identifier for this event. (Stable when the query is broadcastable.) - def fingerprint - @fingerprint ||= begin - # When this query has been flagged as broadcastable, - # use a generalized, stable fingerprint so that - # duplicate subscriptions can be evaluated and distributed in bulk. - # (`@topic` includes field, args, and subscription scope already.) - if @context.namespace(:subscriptions)[:subscription_broadcastable] - "#{@topic}/#{@context.query.fingerprint}" - else - # not broadcastable, build a unique ID for this event - @context.schema.subscriptions.build_id - end - end - end - - class << self - def arguments_without_field_extras(arguments:, field:) - if !field.extras.empty? - arguments = arguments.dup - field.extras.each do |extra_key| - arguments.delete(extra_key) - end - end - arguments - end - - private - - # This method does not support cyclic references in the Hash, - # nor does it support Hashes whose keys are not sortable - # with respect to their peers ( cases where a <=> b might throw an error ) - def deep_sort_hash_keys(hash_to_sort) - raise ArgumentError.new("Argument must be a Hash") unless hash_to_sort.is_a?(Hash) - hash_to_sort.keys.sort.map do |k| - if hash_to_sort[k].is_a?(Hash) - [k, deep_sort_hash_keys(hash_to_sort[k])] - elsif hash_to_sort[k].is_a?(Array) - [k, deep_sort_array_hashes(hash_to_sort[k])] - else - [k, hash_to_sort[k]] - end - end.to_h - end - - def deep_sort_array_hashes(array_to_inspect) - raise ArgumentError.new("Argument must be an Array") unless array_to_inspect.is_a?(Array) - array_to_inspect.map do |v| - if v.is_a?(Hash) - deep_sort_hash_keys(v) - elsif v.is_a?(Array) - deep_sort_array_hashes(v) - else - v - end - end - end - - def stringify_args(arg_owner, args, context) - arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers - case args - when Hash - next_args = {} - args.each do |k, v| - arg_name = k.to_s - camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name) - arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context) - arg_defn ||= get_arg_definition(arg_owner, arg_name, context) - normalized_arg_name = arg_defn.graphql_name - arg_base_type = arg_defn.type.unwrap - # In the case where the value being emitted is seen as a "JSON" - # type, treat the value as one atomic unit of serialization - is_json_definition = arg_base_type && arg_base_type <= GraphQL::Types::JSON - if is_json_definition - sorted_value = if v.is_a?(Hash) - deep_sort_hash_keys(v) - elsif v.is_a?(Array) - deep_sort_array_hashes(v) - else - v - end - next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value - else - next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context) - end - end - # Make sure they're deeply sorted - next_args.sort.to_h - when Array - args.map { |a| stringify_args(arg_owner, a, context) } - when GraphQL::Schema::InputObject - stringify_args(arg_owner, args.to_h, context) - else - if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum - # `prepare:` may have made the value something other than - # a defined value of this enum -- use _that_ in this case. - arg_owner.coerce_isolated_input(args) || args - else - args - end - end - end - - def get_arg_definition(arg_owner, arg_name, context) - context.types.argument(arg_owner, arg_name) || context.types.arguments(arg_owner).find { |v| v.keyword.to_s == arg_name } - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/subscriptions/serialize.rb b/vendor/gems/graphql/lib/graphql/subscriptions/serialize.rb deleted file mode 100644 index 6b1d91becf9..00000000000 --- a/vendor/gems/graphql/lib/graphql/subscriptions/serialize.rb +++ /dev/null @@ -1,160 +0,0 @@ -# frozen_string_literal: true -require "set" -module GraphQL - class Subscriptions - # Serialization helpers for passing subscription data around. - # @api private - module Serialize - GLOBALID_KEY = "__gid__" - SYMBOL_KEY = "__sym__" - SYMBOL_KEYS_KEY = "__sym_keys__" - TIMESTAMP_KEY = "__timestamp__" - TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%z" # eg '2020-01-01 23:59:59.123456789+05:00' - OPEN_STRUCT_KEY = "__ostruct__" - - module_function - - # @param str [String] A serialized object from {.dump} - # @return [Object] An object equivalent to the one passed to {.dump} - def load(str) - parsed_obj = JSON.parse(str) - load_value(parsed_obj) - end - - # @param obj [Object] Some subscription-related data to dump - # @return [String] The stringified object - def dump(obj) - JSON.generate(dump_value(obj), quirks_mode: true) - end - - # This is for turning objects into subscription scopes. - # It's a one-way transformation, can't reload this :'( - # @param obj [Object] - # @return [String] - def dump_recursive(obj) - case - when obj.is_a?(Array) - obj.map { |i| dump_recursive(i) }.join(':') - when obj.is_a?(Hash) - obj.map { |k, v| "#{dump_recursive(k)}:#{dump_recursive(v)}" }.join(":") - when obj.is_a?(GraphQL::Schema::InputObject) - dump_recursive(obj.to_h) - when obj.respond_to?(:to_gid_param) - obj.to_gid_param - when obj.respond_to?(:to_param) - obj.to_param - else - obj.to_s - end - end - - class << self - private - - # @param value [Object] A parsed JSON object - # @return [Object] An object that load Global::Identification recursive - def load_value(value) - if value.is_a?(Array) - is_gids = (v1 = value[0]).is_a?(Hash) && v1.size == 1 && v1[GLOBALID_KEY] - if is_gids - # Assume it's an array of global IDs - ids = value.map { |v| v[GLOBALID_KEY] } - GlobalID::Locator.locate_many(ids) - else - value.map { |item| load_value(item) } - end - elsif value.is_a?(Hash) - if value.size == 1 - case value.keys.first # there's only 1 key - when GLOBALID_KEY - GlobalID::Locator.locate(value[GLOBALID_KEY]) - when SYMBOL_KEY - value[SYMBOL_KEY].to_sym - when TIMESTAMP_KEY - timestamp_class_name, *timestamp_args = value[TIMESTAMP_KEY] - timestamp_class = Object.const_get(timestamp_class_name) - if defined?(ActiveSupport::TimeWithZone) && timestamp_class <= ActiveSupport::TimeWithZone - zone_name, timestamp_s = timestamp_args - zone = ActiveSupport::TimeZone[zone_name] - raise "Zone #{zone_name} not found, unable to deserialize" unless zone - zone.strptime(timestamp_s, TIMESTAMP_FORMAT) - else - timestamp_s = timestamp_args.first - timestamp_class.strptime(timestamp_s, TIMESTAMP_FORMAT) - end - when OPEN_STRUCT_KEY - ostruct_values = load_value(value[OPEN_STRUCT_KEY]) - OpenStruct.new(ostruct_values) - else - key = value.keys.first - { key => load_value(value[key]) } - end - else - loaded_h = {} - sym_keys = value.fetch(SYMBOL_KEYS_KEY, []) - value.each do |k, v| - if k == SYMBOL_KEYS_KEY - next - end - if sym_keys.include?(k) - k = k.to_sym - end - loaded_h[k] = load_value(v) - end - loaded_h - end - else - value - end - end - - # @param obj [Object] Some subscription-related data to dump - # @return [Object] The object that converted Global::Identification - def dump_value(obj) - if obj.is_a?(Array) - obj.map{|item| dump_value(item)} - elsif obj.is_a?(Hash) - symbol_keys = nil - dumped_h = {} - obj.each do |k, v| - dumped_h[k.to_s] = dump_value(v) - if k.is_a?(Symbol) - symbol_keys ||= Set.new - symbol_keys << k.to_s - end - end - if symbol_keys - dumped_h[SYMBOL_KEYS_KEY] = symbol_keys.to_a - end - dumped_h - elsif obj.is_a?(Symbol) - { SYMBOL_KEY => obj.to_s } - elsif obj.respond_to?(:to_gid_param) - {GLOBALID_KEY => obj.to_gid_param} - elsif defined?(ActiveSupport::TimeWithZone) && obj.is_a?(ActiveSupport::TimeWithZone) && obj.class.name != Time.name - # This handles a case where Rails prior to 7 would - # make the class ActiveSupport::TimeWithZone return "Time" for - # its name. In Rails 7, it will now return "ActiveSupport::TimeWithZone", - # which happens to be incompatible with expectations we have - # with what a Time class supports ( notably, strptime in `load_value` ). - # - # This now passes along the name of the zone, such that a future deserialization - # of this string will use the correct time zone from the ActiveSupport TimeZone - # list to produce the time. - # - { TIMESTAMP_KEY => [obj.class.name, obj.time_zone.name, obj.strftime(TIMESTAMP_FORMAT)] } - elsif obj.is_a?(Date) || obj.is_a?(Time) - # DateTime extends Date; for TimeWithZone, call `.utc` first. - { TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] } - elsif defined?(OpenStruct) && obj.is_a?(OpenStruct) - { OPEN_STRUCT_KEY => dump_value(obj.to_h) } - elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation) - dump_value(obj.to_a) - else - obj - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/testing.rb b/vendor/gems/graphql/lib/graphql/testing.rb deleted file mode 100644 index fe48fe26059..00000000000 --- a/vendor/gems/graphql/lib/graphql/testing.rb +++ /dev/null @@ -1,2 +0,0 @@ -# frozen_string_literal: true -require "graphql/testing/helpers" diff --git a/vendor/gems/graphql/lib/graphql/testing/helpers.rb b/vendor/gems/graphql/lib/graphql/testing/helpers.rb deleted file mode 100644 index 0469d566f48..00000000000 --- a/vendor/gems/graphql/lib/graphql/testing/helpers.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Testing - module Helpers - # @param schema_class [Class] - # @return [Module] A helpers module which always uses the given schema - def self.for(schema_class) - SchemaHelpers.for(schema_class) - end - - class Error < GraphQL::Error - end - - class TypeNotVisibleError < Error - def initialize(type_name:) - message = "`#{type_name}` should be `visible?` this field resolution and `context`, but it was not" - super(message) - end - end - - class FieldNotVisibleError < Error - def initialize(type_name:, field_name:) - message = "`#{type_name}.#{field_name}` should be `visible?` for this resolution, but it was not" - super(message) - end - end - - class TypeNotDefinedError < Error - def initialize(type_name:) - message = "No type named `#{type_name}` is defined; choose another type name or define this type." - super(message) - end - end - - class FieldNotDefinedError < Error - def initialize(type_name:, field_name:) - message = "`#{type_name}` has no field named `#{field_name}`; pick another name or define this field." - super(message) - end - end - - def run_graphql_field(schema, field_path, object, arguments: {}, context: {}, ast_node: nil, lookahead: nil) - type_name, *field_names = field_path.split(".") - dummy_query = GraphQL::Query.new(schema, "{ __typename }", context: context) - query_context = dummy_query.context - object_type = dummy_query.types.type(type_name) # rubocop:disable Development/ContextIsPassedCop - if object_type - graphql_result = object - field_names.each do |field_name| - inner_object = graphql_result - graphql_result = object_type.wrap(inner_object, query_context) - if graphql_result.nil? - return nil - end - visible_field = dummy_query.types.field(object_type, field_name) # rubocop:disable Development/ContextIsPassedCop - if visible_field - dummy_query.context.dataloader.run_isolated { - query_context[:current_field] = visible_field - field_args = visible_field.coerce_arguments(graphql_result, arguments, query_context) - field_args = schema.sync_lazy(field_args) - if !visible_field.extras.empty? - extra_args = {} - visible_field.extras.each do |extra| - extra_args[extra] = case extra - when :ast_node - ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name) - when :lookahead - lookahead ||= begin - ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name) - Execution::Lookahead.new( - query: dummy_query, - ast_nodes: [ast_node], - field: visible_field, - ) - end - else - raise ArgumentError, "This extra isn't supported in `run_graphql_field` yet: `#{extra.inspect}`. Open an issue on GitHub to request it: https://github.com/rmosolgo/graphql-ruby/issues/new" - end - end - - field_args = field_args.merge_extras(extra_args) - end - graphql_result = visible_field.resolve(graphql_result, field_args.keyword_arguments, query_context) - graphql_result = schema.sync_lazy(graphql_result) - } - object_type = visible_field.type.unwrap - elsif object_type.all_field_definitions.any? { |f| f.graphql_name == field_name } - raise FieldNotVisibleError.new(field_name: field_name, type_name: type_name) - else - raise FieldNotDefinedError.new(type_name: type_name, field_name: field_name) - end - end - graphql_result - else - unfiltered_type = schema.use_visibility_profile? ? schema.visibility.get_type(type_name) : schema.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop - if unfiltered_type - raise TypeNotVisibleError.new(type_name: type_name) - else - raise TypeNotDefinedError.new(type_name: type_name) - end - end - end - - def with_resolution_context(schema, type:, object:, context:{}) - resolution_context = ResolutionAssertionContext.new( - self, - schema: schema, - type_name: type, - object: object, - context: context - ) - yield(resolution_context) - end - - class ResolutionAssertionContext - def initialize(test, type_name:, object:, schema:, context:) - @test = test - @type_name = type_name - @object = object - @schema = schema - @context = context - end - - - def run_graphql_field(field_name, arguments: {}) - if @schema - @test.run_graphql_field(@schema, "#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context) - else - @test.run_graphql_field("#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context) - end - end - end - - module SchemaHelpers - include Helpers - - def run_graphql_field(field_path, object, arguments: {}, context: {}) - super(@@schema_class_for_helpers, field_path, object, arguments: arguments, context: context) - end - - def with_resolution_context(*args, **kwargs, &block) - # schema will be added later - super(nil, *args, **kwargs, &block) - end - - def self.for(schema_class) - Module.new do - include SchemaHelpers - @@schema_class_for_helpers = schema_class - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing.rb b/vendor/gems/graphql/lib/graphql/tracing.rb deleted file mode 100644 index 1c117b9c9db..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - - -module GraphQL - module Tracing - autoload :Trace, "graphql/tracing/trace" - autoload :CallLegacyTracers, "graphql/tracing/call_legacy_tracers" - autoload :LegacyTrace, "graphql/tracing/legacy_trace" - autoload :LegacyHooksTrace, "graphql/tracing/legacy_hooks_trace" - autoload :NullTrace, "graphql/tracing/null_trace" - - autoload :ActiveSupportNotificationsTracing, "graphql/tracing/active_support_notifications_tracing" - autoload :PlatformTracing, "graphql/tracing/platform_tracing" - autoload :AppOpticsTracing, "graphql/tracing/appoptics_tracing" - autoload :AppsignalTracing, "graphql/tracing/appsignal_tracing" - autoload :DataDogTracing, "graphql/tracing/data_dog_tracing" - autoload :NewRelicTracing, "graphql/tracing/new_relic_tracing" - autoload :NotificationsTracing, "graphql/tracing/notifications_tracing" - autoload :ScoutTracing, "graphql/tracing/scout_tracing" - autoload :StatsdTracing, "graphql/tracing/statsd_tracing" - autoload :PrometheusTracing, "graphql/tracing/prometheus_tracing" - - autoload :ActiveSupportNotificationsTrace, "graphql/tracing/active_support_notifications_trace" - autoload :PlatformTrace, "graphql/tracing/platform_trace" - autoload :AppOpticsTrace, "graphql/tracing/appoptics_trace" - autoload :AppsignalTrace, "graphql/tracing/appsignal_trace" - autoload :DataDogTrace, "graphql/tracing/data_dog_trace" - autoload :NewRelicTrace, "graphql/tracing/new_relic_trace" - autoload :NotificationsTrace, "graphql/tracing/notifications_trace" - autoload :SentryTrace, "graphql/tracing/sentry_trace" - autoload :ScoutTrace, "graphql/tracing/scout_trace" - autoload :StatsdTrace, "graphql/tracing/statsd_trace" - autoload :PrometheusTrace, "graphql/tracing/prometheus_trace" - autoload :PerfettoTrace, "graphql/tracing/perfetto_trace" - autoload :DetailedTrace, "graphql/tracing/detailed_trace" - - # Objects may include traceable to gain a `.trace(...)` method. - # The object must have a `@tracers` ivar of type `Array<<#trace(k, d, &b)>>`. - # @api private - module Traceable - # @param key [String] The name of the event in GraphQL internals - # @param metadata [Hash] Event-related metadata (can be anything) - # @return [Object] Must return the value of the block - def trace(key, metadata, &block) - return yield if @tracers.empty? - call_tracers(0, key, metadata, &block) - end - - private - - # If there's a tracer at `idx`, call it and then increment `idx`. - # Otherwise, yield. - # - # @param idx [Integer] Which tracer to call - # @param key [String] The current event name - # @param metadata [Object] The current event object - # @return Whatever the block returns - def call_tracers(idx, key, metadata, &block) - if idx == @tracers.length - yield - else - @tracers[idx].trace(key, metadata) { call_tracers(idx + 1, key, metadata, &block) } - end - end - end - - module NullTracer - module_function - def trace(k, v) - yield - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_trace.rb deleted file mode 100644 index ef0d2752a17..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_trace.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/notifications_trace" - -module GraphQL - module Tracing - # This implementation forwards events to ActiveSupport::Notifications with a `graphql` suffix. - # - # @example Sending execution events to ActiveSupport::Notifications - # class MySchema < GraphQL::Schema - # trace_with(GraphQL::Tracing::ActiveSupportNotificationsTrace) - # end - module ActiveSupportNotificationsTrace - include NotificationsTrace - def initialize(engine: ActiveSupport::Notifications, **rest) - super - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_tracing.rb deleted file mode 100644 index 4f60c63a346..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/active_support_notifications_tracing.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/notifications_tracing" - -module GraphQL - module Tracing - # This implementation forwards events to ActiveSupport::Notifications - # with a `graphql` suffix. - # - # @see KEYS for event names - module ActiveSupportNotificationsTracing - # A cache of frequently-used keys to avoid needless string allocations - KEYS = NotificationsTracing::KEYS - NOTIFICATIONS_ENGINE = NotificationsTracing.new(ActiveSupport::Notifications) if defined?(ActiveSupport::Notifications) - - def self.trace(key, metadata, &blk) - NOTIFICATIONS_ENGINE.trace(key, metadata, &blk) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/appoptics_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/appoptics_trace.rb deleted file mode 100644 index 4fa246407a7..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/appoptics_trace.rb +++ /dev/null @@ -1,259 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - - # This class uses the AppopticsAPM SDK from the appoptics_apm gem to create - # traces for GraphQL. - # - # There are 4 configurations available. They can be set in the - # appoptics_apm config file or in code. Please see: - # {https://docs.appoptics.com/kb/apm_tracing/ruby/configure} - # - # AppOpticsAPM::Config[:graphql][:enabled] = true|false - # AppOpticsAPM::Config[:graphql][:transaction_name] = true|false - # AppOpticsAPM::Config[:graphql][:sanitize_query] = true|false - # AppOpticsAPM::Config[:graphql][:remove_comments] = true|false - module AppOpticsTrace - # These GraphQL events will show up as 'graphql.prep' spans - PREP_KEYS = ['lex', 'parse', 'validate', 'analyze_query', 'analyze_multiplex'].freeze - # These GraphQL events will show up as 'graphql.execute' spans - EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze - - - # During auto-instrumentation this version of AppOpticsTracing is compared - # with the version provided in the appoptics_apm gem, so that the newer - # version of the class can be used - - - def self.version - Gem::Version.new('1.0.0') - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - [ - 'lex', - 'parse', - 'validate', - 'analyze_query', - 'analyze_multiplex', - 'execute_multiplex', - 'execute_query', - 'execute_query_lazy', - ].each do |trace_method| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - layer = span_name("#{trace_method}") - kvs = metadata(data, layer) - kvs[:Key] = "#{trace_method}" if (PREP_KEYS + EXEC_KEYS).include?("#{trace_method}") - - transaction_name(kvs[:InboundQuery]) if kvs[:InboundQuery] && layer == 'graphql.execute' - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - super - end - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - def execute_field(query:, field:, ast_node:, arguments:, object:) - return_type = field.type.unwrap - trace_field = if return_type.kind.scalar? || return_type.kind.enum? - (field.trace.nil? && @trace_scalars) || field.trace - else - true - end - platform_key = if trace_field - @platform_key_cache[AppOpticsTrace].platform_field_key_cache[field] - else - nil - end - if platform_key && trace_field - return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - layer = platform_key - kvs = metadata({query: query, field: field, ast_node: ast_node, arguments: arguments, object: object}, layer) - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - super - end - else - super - end - end - - def execute_field_lazy(query:, field:, ast_node:, arguments:, object:) # rubocop:disable Development/TraceCallsSuperCop - execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object) - end - - def authorized(**data) - return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]] - kvs = metadata(data, layer) - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - super - end - end - - def authorized_lazy(**data) - return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]] - kvs = metadata(data, layer) - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - super - end - end - - def resolve_type(**data) - return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]] - - kvs = metadata(data, layer) - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - super - end - end - - def resolve_type_lazy(**data) - return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]] - kvs = metadata(data, layer) - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - super - end - end - - include PlatformTrace - - def platform_field_key(field) - "graphql.#{field.owner.graphql_name}.#{field.graphql_name}" - end - - def platform_authorized_key(type) - "graphql.authorized.#{type.graphql_name}" - end - - def platform_resolve_type_key(type) - "graphql.resolve_type.#{type.graphql_name}" - end - - private - - def gql_config - ::AppOpticsAPM::Config[:graphql] ||= {} - end - - def transaction_name(query) - return if gql_config[:transaction_name] == false || - ::AppOpticsAPM::SDK.get_transaction_name - - split_query = query.strip.split(/\W+/, 3) - split_query[0] = 'query' if split_query[0].empty? - name = "graphql.#{split_query[0..1].join('.')}" - - ::AppOpticsAPM::SDK.set_transaction_name(name) - end - - def multiplex_transaction_name(names) - return if gql_config[:transaction_name] == false || - ::AppOpticsAPM::SDK.get_transaction_name - - name = "graphql.multiplex.#{names.join('.')}" - name = "#{name[0..251]}..." if name.length > 254 - - ::AppOpticsAPM::SDK.set_transaction_name(name) - end - - def span_name(key) - return 'graphql.prep' if PREP_KEYS.include?(key) - return 'graphql.execute' if EXEC_KEYS.include?(key) - - key[/^graphql\./] ? key : "graphql.#{key}" - end - - # rubocop:disable Metrics/AbcSize, Metrics/MethodLength - def metadata(data, layer) - data.keys.map do |key| - case key - when :context - graphql_context(data[key], layer) - when :query - graphql_query(data[key]) - when :query_string - graphql_query_string(data[key]) - when :multiplex - graphql_multiplex(data[key]) - when :path - [key, data[key].join(".")] - else - [key, data[key]] - end - end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql') - end - # rubocop:enable Metrics/AbcSize, Metrics/MethodLength - - def graphql_context(context, layer) - context.errors && context.errors.each do |err| - AppOpticsAPM::API.log_exception(layer, err) - end - - [[:Path, context.path.join('.')]] - end - - def graphql_query(query) - return [] unless query - - query_string = query.query_string - query_string = remove_comments(query_string) if gql_config[:remove_comments] != false - query_string = sanitize(query_string) if gql_config[:sanitize_query] != false - - [[:InboundQuery, query_string], - [:Operation, query.selected_operation_name]] - end - - def graphql_query_string(query_string) - query_string = remove_comments(query_string) if gql_config[:remove_comments] != false - query_string = sanitize(query_string) if gql_config[:sanitize_query] != false - - [:InboundQuery, query_string] - end - - def graphql_multiplex(data) - names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!) - multiplex_transaction_name(names) if names.size > 1 - - [:Operations, names.join(', ')] - end - - def sanitize(query) - return unless query - - # remove arguments - query.gsub(/"[^"]*"/, '"?"') # strings - .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats - .gsub(/\[[^\]]*\]/, '[?]') # arrays - end - - def remove_comments(query) - return unless query - - query.gsub(/#[^\n\r]*/, '') - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/appoptics_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/appoptics_tracing.rb deleted file mode 100644 index 3ecebcfbfed..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/appoptics_tracing.rb +++ /dev/null @@ -1,175 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - - # This class uses the AppopticsAPM SDK from the appoptics_apm gem to create - # traces for GraphQL. - # - # There are 4 configurations available. They can be set in the - # appoptics_apm config file or in code. Please see: - # {https://docs.appoptics.com/kb/apm_tracing/ruby/configure} - # - # AppOpticsAPM::Config[:graphql][:enabled] = true|false - # AppOpticsAPM::Config[:graphql][:transaction_name] = true|false - # AppOpticsAPM::Config[:graphql][:sanitize_query] = true|false - # AppOpticsAPM::Config[:graphql][:remove_comments] = true|false - class AppOpticsTracing < GraphQL::Tracing::PlatformTracing - # These GraphQL events will show up as 'graphql.prep' spans - PREP_KEYS = ['lex', 'parse', 'validate', 'analyze_query', 'analyze_multiplex'].freeze - # These GraphQL events will show up as 'graphql.execute' spans - EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze - - # During auto-instrumentation this version of AppOpticsTracing is compared - # with the version provided in the appoptics_apm gem, so that the newer - # version of the class can be used - - def self.version - Gem::Version.new('1.0.0') - end - - self.platform_keys = { - 'lex' => 'lex', - 'parse' => 'parse', - 'validate' => 'validate', - 'analyze_query' => 'analyze_query', - 'analyze_multiplex' => 'analyze_multiplex', - 'execute_multiplex' => 'execute_multiplex', - 'execute_query' => 'execute_query', - 'execute_query_lazy' => 'execute_query_lazy' - } - - def platform_trace(platform_key, _key, data) - return yield if !defined?(AppOpticsAPM) || gql_config[:enabled] == false - - layer = span_name(platform_key) - kvs = metadata(data, layer) - kvs[:Key] = platform_key if (PREP_KEYS + EXEC_KEYS).include?(platform_key) - - transaction_name(kvs[:InboundQuery]) if kvs[:InboundQuery] && layer == 'graphql.execute' - - ::AppOpticsAPM::SDK.trace(layer, kvs) do - kvs.clear # we don't have to send them twice - yield - end - end - - def platform_field_key(type, field) - "graphql.#{type.graphql_name}.#{field.graphql_name}" - end - - def platform_authorized_key(type) - "graphql.authorized.#{type.graphql_name}" - end - - def platform_resolve_type_key(type) - "graphql.resolve_type.#{type.graphql_name}" - end - - private - - def gql_config - ::AppOpticsAPM::Config[:graphql] ||= {} - end - - def transaction_name(query) - return if gql_config[:transaction_name] == false || - ::AppOpticsAPM::SDK.get_transaction_name - - split_query = query.strip.split(/\W+/, 3) - split_query[0] = 'query' if split_query[0].empty? - name = "graphql.#{split_query[0..1].join('.')}" - - ::AppOpticsAPM::SDK.set_transaction_name(name) - end - - def multiplex_transaction_name(names) - return if gql_config[:transaction_name] == false || - ::AppOpticsAPM::SDK.get_transaction_name - - name = "graphql.multiplex.#{names.join('.')}" - name = "#{name[0..251]}..." if name.length > 254 - - ::AppOpticsAPM::SDK.set_transaction_name(name) - end - - def span_name(key) - return 'graphql.prep' if PREP_KEYS.include?(key) - return 'graphql.execute' if EXEC_KEYS.include?(key) - - key[/^graphql\./] ? key : "graphql.#{key}" - end - - # rubocop:disable Metrics/AbcSize, Metrics/MethodLength - def metadata(data, layer) - data.keys.map do |key| - case key - when :context - graphql_context(data[key], layer) - when :query - graphql_query(data[key]) - when :query_string - graphql_query_string(data[key]) - when :multiplex - graphql_multiplex(data[key]) - when :path - [key, data[key].join(".")] - else - [key, data[key]] - end - end.tap { _1.flatten!(2) }.each_slice(2).to_h.merge(Spec: 'graphql') - end - # rubocop:enable Metrics/AbcSize, Metrics/MethodLength - - def graphql_context(context, layer) - context.errors && context.errors.each do |err| - AppOpticsAPM::API.log_exception(layer, err) - end - - [[:Path, context.path.join('.')]] - end - - def graphql_query(query) - return [] unless query - - query_string = query.query_string - query_string = remove_comments(query_string) if gql_config[:remove_comments] != false - query_string = sanitize(query_string) if gql_config[:sanitize_query] != false - - [[:InboundQuery, query_string], - [:Operation, query.selected_operation_name]] - end - - def graphql_query_string(query_string) - query_string = remove_comments(query_string) if gql_config[:remove_comments] != false - query_string = sanitize(query_string) if gql_config[:sanitize_query] != false - - [:InboundQuery, query_string] - end - - def graphql_multiplex(data) - names = data.queries.map(&:operations).map!(&:keys).tap(&:flatten!).tap(&:compact!) - multiplex_transaction_name(names) if names.size > 1 - - [:Operations, names.join(', ')] - end - - def sanitize(query) - return unless query - - # remove arguments - query.gsub(/"[^"]*"/, '"?"') # strings - .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats - .gsub(/\[[^\]]*\]/, '[?]') # arrays - end - - def remove_comments(query) - return unless query - - query.gsub(/#[^\n\r]*/, '') - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/appsignal_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/appsignal_trace.rb deleted file mode 100644 index a790ce635f2..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/appsignal_trace.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # Instrumentation for reporting GraphQL-Ruby times to Appsignal. - # - # @example Installing the tracer - # class MySchema < GraphQL::Schema - # trace_with GraphQL::Tracing::AppsignalTrace - # end - module AppsignalTrace - include PlatformTrace - - # @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_appsignal_action_name]`. - def initialize(set_action_name: false, **rest) - @set_action_name = set_action_name - super - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - "lex" => "lex.graphql", - "parse" => "parse.graphql", - "validate" => "validate.graphql", - "analyze_query" => "analyze.graphql", - "analyze_multiplex" => "analyze.graphql", - "execute_multiplex" => "execute.graphql", - "execute_query" => "execute.graphql", - "execute_query_lazy" => "execute.graphql", - }.each do |trace_method, platform_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - #{ - if trace_method == "execute_query" - <<-RUBY - set_this_txn_name = data[:query].context[:set_appsignal_action_name] - if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name) - Appsignal::Transaction.current.set_action(transaction_name(data[:query])) - end - RUBY - end - } - - Appsignal.instrument("#{platform_key}") do - super - end - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - def platform_execute_field(platform_key) - Appsignal.instrument(platform_key) do - yield - end - end - - def platform_authorized(platform_key) - Appsignal.instrument(platform_key) do - yield - end - end - - def platform_resolve_type(platform_key) - Appsignal.instrument(platform_key) do - yield - end - end - - def platform_field_key(field) - "#{field.owner.graphql_name}.#{field.graphql_name}.graphql" - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized.graphql" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type.graphql" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/appsignal_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/appsignal_tracing.rb deleted file mode 100644 index cd552ee71a8..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/appsignal_tracing.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - class AppsignalTracing < PlatformTracing - self.platform_keys = { - "lex" => "lex.graphql", - "parse" => "parse.graphql", - "validate" => "validate.graphql", - "analyze_query" => "analyze.graphql", - "analyze_multiplex" => "analyze.graphql", - "execute_multiplex" => "execute.graphql", - "execute_query" => "execute.graphql", - "execute_query_lazy" => "execute.graphql", - } - - # @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_appsignal_action_name]`. - def initialize(options = {}) - @set_action_name = options.fetch(:set_action_name, false) - super - end - - def platform_trace(platform_key, key, data) - if key == "execute_query" - set_this_txn_name = data[:query].context[:set_appsignal_action_name] - if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name) - Appsignal::Transaction.current.set_action(transaction_name(data[:query])) - end - end - - Appsignal.instrument(platform_key) do - yield - end - end - - def platform_field_key(type, field) - "#{type.graphql_name}.#{field.graphql_name}.graphql" - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized.graphql" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type.graphql" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/call_legacy_tracers.rb b/vendor/gems/graphql/lib/graphql/tracing/call_legacy_tracers.rb deleted file mode 100644 index c42b99fa185..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/call_legacy_tracers.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Tracing - # This trace class calls legacy-style tracer with payload hashes. - # New-style `trace_with` modules significantly reduce the overhead of tracing, - # but that advantage is lost when legacy-style tracers are also used (since the payload hashes are still constructed). - module CallLegacyTracers - def lex(query_string:) - (@multiplex || @query).trace("lex", { query_string: query_string }) { super } - end - - def parse(query_string:) - (@multiplex || @query).trace("parse", { query_string: query_string }) { super } - end - - def validate(query:, validate:) - query.trace("validate", { validate: validate, query: query }) { super } - end - - def analyze_multiplex(multiplex:) - multiplex.trace("analyze_multiplex", { multiplex: multiplex }) { super } - end - - def analyze_query(query:) - query.trace("analyze_query", { query: query }) { super } - end - - def execute_multiplex(multiplex:) - multiplex.trace("execute_multiplex", { multiplex: multiplex }) { super } - end - - def execute_query(query:) - query.trace("execute_query", { query: query }) { super } - end - - def execute_query_lazy(query:, multiplex:) - multiplex.trace("execute_query_lazy", { multiplex: multiplex, query: query }) { super } - end - - def execute_field(field:, query:, ast_node:, arguments:, object:) - query.trace("execute_field", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super } - end - - def execute_field_lazy(field:, query:, ast_node:, arguments:, object:) - query.trace("execute_field_lazy", { field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, owner: field.owner, path: query.context[:current_path] }) { super } - end - - def authorized(query:, type:, object:) - query.trace("authorized", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super } - end - - def authorized_lazy(query:, type:, object:) - query.trace("authorized_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super } - end - - def resolve_type(query:, type:, object:) - query.trace("resolve_type", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super } - end - - def resolve_type_lazy(query:, type:, object:) - query.trace("resolve_type_lazy", { context: query.context, type: type, object: object, path: query.context[:current_path] }) { super } - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/data_dog_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/data_dog_trace.rb deleted file mode 100644 index fa5b08aba9a..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/data_dog_trace.rb +++ /dev/null @@ -1,194 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # A tracer for reporting to DataDog - # @example Adding this tracer to your schema - # class MySchema < GraphQL::Schema - # trace_with GraphQL::Tracing::DataDogTrace - # end - module DataDogTrace - # @param tracer [#trace] Deprecated - # @param analytics_enabled [Boolean] Deprecated - # @param analytics_sample_rate [Float] Deprecated - def initialize(tracer: nil, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **rest) - if tracer.nil? - tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer - end - @tracer = tracer - - @analytics_enabled = analytics_enabled - @analytics_sample_rate = analytics_sample_rate - - @service_name = service - @has_prepare_span = respond_to?(:prepare_span) - super - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - 'lex' => 'lex.graphql', - 'parse' => 'parse.graphql', - 'validate' => 'validate.graphql', - 'analyze_query' => 'analyze.graphql', - 'analyze_multiplex' => 'analyze.graphql', - 'execute_multiplex' => 'execute.graphql', - 'execute_query' => 'execute.graphql', - 'execute_query_lazy' => 'execute.graphql', - }.each do |trace_method, trace_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - @tracer.trace("#{trace_key}", service: @service_name, type: 'custom') do |span| - span.set_tag('component', 'graphql') - span.set_tag('operation', '#{trace_method}') - - #{ - if trace_method == 'execute_multiplex' - <<-RUBY - operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ') - - resource = if operations.empty? - first_query = data[:multiplex].queries.first - fallback_transaction_name(first_query && first_query.context) - else - operations - end - span.resource = resource if resource - - # [Deprecated] will be removed in the future - span.set_metric('_dd1.sr.eausr', @analytics_sample_rate) if @analytics_enabled - RUBY - elsif trace_method == 'execute_query' - <<-RUBY - span.set_tag(:selected_operation_name, data[:query].selected_operation_name) - span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type) - span.set_tag(:query_string, data[:query].query_string) - RUBY - end - } - if @has_prepare_span - prepare_span("#{trace_method.sub("platform_", "")}", data, span) - end - super - end - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - def execute_field_span(span_key, query, field, ast_node, arguments, object) - return_type = field.type.unwrap - trace_field = if return_type.kind.scalar? || return_type.kind.enum? - (field.trace.nil? && @trace_scalars) || field.trace - else - true - end - platform_key = if trace_field - @platform_key_cache[DataDogTrace].platform_field_key_cache[field] - else - nil - end - if platform_key && trace_field - @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span| - span.set_tag('component', 'graphql') - span.set_tag('operation', span_key) - - if @has_prepare_span - prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object } - prepare_span(span_key, prepare_span_data, span) - end - yield - end - else - yield - end - end - def execute_field(query:, field:, ast_node:, arguments:, object:) - execute_field_span("execute_field", query, field, ast_node, arguments, object) do - super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object) - end - end - - def execute_field_lazy(query:, field:, ast_node:, arguments:, object:) - execute_field_span("execute_field_lazy", query, field, ast_node, arguments, object) do - super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object) - end - end - - def authorized(query:, type:, object:) - authorized_span("authorized", object, type, query) do - super(query: query, type: type, object: object) - end - end - - def authorized_span(span_key, object, type, query) - platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type] - @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span| - span.set_tag('component', 'graphql') - span.set_tag('operation', span_key) - - if @has_prepare_span - prepare_span(span_key, {object: object, type: type, query: query}, span) - end - yield - end - end - - def authorized_lazy(object:, type:, query:) - authorized_span("authorized_lazy", object, type, query) do - super(query: query, type: type, object: object) - end - end - - def resolve_type(object:, type:, query:) - resolve_type_span("resolve_type", object, type, query) do - super(object: object, query: query, type: type) - end - end - - def resolve_type_lazy(object:, type:, query:) - resolve_type_span("resolve_type_lazy", object, type, query) do - super(object: object, query: query, type: type) - end - end - - def resolve_type_span(span_key, object, type, query) - platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type] - @tracer.trace(platform_key, service: @service_name, type: 'custom') do |span| - span.set_tag('component', 'graphql') - span.set_tag('operation', span_key) - - if @has_prepare_span - prepare_span(span_key, {object: object, type: type, query: query}, span) - end - yield - end - end - - include PlatformTrace - - # Implement this method in a subclass to apply custom tags to datadog spans - # @param key [String] The event being traced - # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event) - # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event - # def prepare_span(key, data, span) - # end - - def platform_field_key(field) - field.path - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/data_dog_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/data_dog_tracing.rb deleted file mode 100644 index b764c2ba26b..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/data_dog_tracing.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - class DataDogTracing < PlatformTracing - self.platform_keys = { - 'lex' => 'lex.graphql', - 'parse' => 'parse.graphql', - 'validate' => 'validate.graphql', - 'analyze_query' => 'analyze.graphql', - 'analyze_multiplex' => 'analyze.graphql', - 'execute_multiplex' => 'execute.graphql', - 'execute_query' => 'execute.graphql', - 'execute_query_lazy' => 'execute.graphql', - } - - def platform_trace(platform_key, key, data) - tracer.trace(platform_key, service: options[:service], type: 'custom') do |span| - span.set_tag('component', 'graphql') - span.set_tag('operation', key) - - if key == 'execute_multiplex' - operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ') - - resource = if operations.empty? - first_query = data[:multiplex].queries.first - fallback_transaction_name(first_query && first_query.context) - else - operations - end - span.resource = resource if resource - - # [Deprecated] will be removed in the future - span.set_metric('_dd1.sr.eausr', analytics_sample_rate) if analytics_enabled? - end - - if key == 'execute_query' - span.set_tag(:selected_operation_name, data[:query].selected_operation_name) - span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type) - span.set_tag(:query_string, data[:query].query_string) - end - - prepare_span(key, data, span) - - yield - end - end - - # Implement this method in a subclass to apply custom tags to datadog spans - # @param key [String] The event being traced - # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event) - # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event - def prepare_span(key, data, span) - end - - def tracer - default_tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer - - # [Deprecated] options[:tracer] will be removed in the future - options.fetch(:tracer, default_tracer) - end - - def analytics_enabled? - # [Deprecated] options[:analytics_enabled] will be removed in the future - options.fetch(:analytics_enabled, false) - end - - def analytics_sample_rate - # [Deprecated] options[:analytics_sample_rate] will be removed in the future - options.fetch(:analytics_sample_rate, 1.0) - end - - def platform_field_key(type, field) - "#{type.graphql_name}.#{field.graphql_name}" - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/detailed_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/detailed_trace.rb deleted file mode 100644 index 035ce7ad8aa..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/detailed_trace.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true -require "graphql/tracing/detailed_trace/memory_backend" -require "graphql/tracing/detailed_trace/redis_backend" - -module GraphQL - module Tracing - # `DetailedTrace` can make detailed profiles for a subset of production traffic. - # - # When `MySchema.detailed_trace?(query)` returns `true`, a profiler-specific `trace_mode: ...` will be used for the query, - # overriding the one in `context[:trace_mode]`. - # - # __Redis__: The sampler stores its results in a provided Redis database. Depending on your needs, - # You can configure this database to retail all data (persistent) or to expire data according to your rules. - # If you need to save traces indefinitely, you can download them from Perfetto after opening them there. - # - # @example Adding the sampler to your schema - # class MySchema < GraphQL::Schema - # # Add the sampler: - # use GraphQL::Tracing::DetailedTrace, redis: Redis.new(...), limit: 100 - # - # # And implement this hook to tell it when to take a sample: - # def self.detailed_trace?(query) - # # Could use `query.context`, `query.selected_operation_name`, `query.query_string` here - # # Could call out to Flipper, etc - # rand <= 0.000_1 # one in ten thousand - # end - # end - # - # @see Graphql::Dashboard GraphQL::Dashboard for viewing stored results - class DetailedTrace - # @param redis [Redis] If provided, profiles will be stored in Redis for later review - # @param limit [Integer] A maximum number of profiles to store - def self.use(schema, trace_mode: :profile_sample, memory: false, redis: nil, limit: nil) - storage = if redis - RedisBackend.new(redis: redis, limit: limit) - elsif memory - MemoryBackend.new(limit: limit) - else - raise ArgumentError, "Pass `redis: ...` to store traces in Redis for later review" - end - schema.detailed_trace = self.new(storage: storage, trace_mode: trace_mode) - schema.trace_with(PerfettoTrace, mode: trace_mode, save_profile: true) - end - - def initialize(storage:, trace_mode:) - @storage = storage - @trace_mode = trace_mode - end - - # @return [Symbol] The trace mode to use when {Schema.detailed_trace?} returns `true` - attr_reader :trace_mode - - # @return [String] ID of saved trace - def save_trace(operation_name, duration_ms, begin_ms, trace_data) - @storage.save_trace(operation_name, duration_ms, begin_ms, trace_data) - end - - # @param last [Integer] - # @param before [Integer] Timestamp in milliseconds since epoch - # @return [Enumerable] - def traces(last: nil, before: nil) - @storage.traces(last: last, before: before) - end - - # @return [StoredTrace, nil] - def find_trace(id) - @storage.find_trace(id) - end - - # @return [void] - def delete_trace(id) - @storage.delete_trace(id) - end - - # @return [void] - def delete_all_traces - @storage.delete_all_traces - end - - class StoredTrace - def initialize(id:, operation_name:, duration_ms:, begin_ms:, trace_data:) - @id = id - @operation_name = operation_name - @duration_ms = duration_ms - @begin_ms = begin_ms - @trace_data = trace_data - end - - attr_reader :id, :operation_name, :duration_ms, :begin_ms, :trace_data - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/detailed_trace/memory_backend.rb b/vendor/gems/graphql/lib/graphql/tracing/detailed_trace/memory_backend.rb deleted file mode 100644 index e22c5421588..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/detailed_trace/memory_backend.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Tracing - class DetailedTrace - # An in-memory trace storage backend. Suitable for testing and development only. - # It won't work for multi-process deployments and everything is erased when the app is restarted. - class MemoryBackend - def initialize(limit: nil) - @limit = limit - @traces = {} - @next_id = 0 - end - - def traces(last:, before:) - page = [] - @traces.values.reverse_each do |trace| - if page.size == last - break - elsif before.nil? || trace.begin_ms < before - page << trace - end - end - page - end - - def find_trace(id) - @traces[id] - end - - def delete_trace(id) - @traces.delete(id.to_i) - nil - end - - def delete_all_traces - @traces.clear - nil - end - - def save_trace(operation_name, duration, begin_ms, trace_data) - id = @next_id - @next_id += 1 - @traces[id] = DetailedTrace::StoredTrace.new( - id: id, - operation_name: operation_name, - duration_ms: duration, - begin_ms: begin_ms, - trace_data: trace_data - ) - if @limit && @traces.size > @limit - del_keys = @traces.keys[0...-@limit] - del_keys.each { |k| @traces.delete(k) } - end - id - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/detailed_trace/redis_backend.rb b/vendor/gems/graphql/lib/graphql/tracing/detailed_trace/redis_backend.rb deleted file mode 100644 index 242f85b4565..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/detailed_trace/redis_backend.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Tracing - class DetailedTrace - class RedisBackend - KEY_PREFIX = "gql:trace:" - def initialize(redis:, limit: nil) - @redis = redis - @key = KEY_PREFIX + "traces" - @remrangebyrank_limit = limit ? -limit - 1 : nil - end - - def traces(last:, before:) - before = case before - when Numeric - "(#{before}" - when nil - "+inf" - end - str_pairs = @redis.zrange(@key, before, 0, byscore: true, rev: true, limit: [0, last || 100], withscores: true) - str_pairs.map do |(str_data, score)| - entry_to_trace(score, str_data) - end - end - - def delete_trace(id) - @redis.zremrangebyscore(@key, id, id) - nil - end - - def delete_all_traces - @redis.del(@key) - end - - def find_trace(id) - str_data = @redis.zrange(@key, id, id, byscore: true).first - if str_data.nil? - nil - else - entry_to_trace(id, str_data) - end - end - - def save_trace(operation_name, duration_ms, begin_ms, trace_data) - id = begin_ms - data = JSON.dump({ "o" => operation_name, "d" => duration_ms, "b" => begin_ms, "t" => Base64.encode64(trace_data) }) - @redis.pipelined do |pipeline| - pipeline.zadd(@key, id, data) - if @remrangebyrank_limit - pipeline.zremrangebyrank(@key, 0, @remrangebyrank_limit) - end - end - id - end - - private - - def entry_to_trace(id, json_str) - data = JSON.parse(json_str) - StoredTrace.new( - id: id, - operation_name: data["o"], - duration_ms: data["d"].to_f, - begin_ms: data["b"].to_i, - trace_data: Base64.decode64(data["t"]), - ) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/legacy_hooks_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/legacy_hooks_trace.rb deleted file mode 100644 index d23fe190a14..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/legacy_hooks_trace.rb +++ /dev/null @@ -1,75 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Tracing - module LegacyHooksTrace - def execute_multiplex(multiplex:) - multiplex_instrumenters = multiplex.schema.instrumenters[:multiplex] - query_instrumenters = multiplex.schema.instrumenters[:query] - # First, run multiplex instrumentation, then query instrumentation for each query - RunHooks.call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do - RunHooks.each_query_call_hooks(query_instrumenters, multiplex.queries) do - super - end - end - end - - module RunHooks - module_function - # Call the before_ hooks of each query, - # Then yield if no errors. - # `call_hooks` takes care of appropriate cleanup. - def each_query_call_hooks(instrumenters, queries, i = 0) - if i >= queries.length - yield - else - query = queries[i] - call_hooks(instrumenters, query, :before_query, :after_query) { - each_query_call_hooks(instrumenters, queries, i + 1) { - yield - } - } - end - end - - # Call each before hook, and if they all succeed, yield. - # If they don't all succeed, call after_ for each one that succeeded. - def call_hooks(instrumenters, object, before_hook_name, after_hook_name) - begin - successful = [] - instrumenters.each do |instrumenter| - instrumenter.public_send(before_hook_name, object) - successful << instrumenter - end - - # if any before hooks raise an exception, quit calling before hooks, - # but call the after hooks on anything that succeeded but also - # raise the exception that came from the before hook. - rescue GraphQL::ExecutionError => err - object.context.errors << err - rescue => e - raise call_after_hooks(successful, object, after_hook_name, e) - end - - begin - yield # Call the user code - ensure - ex = call_after_hooks(successful, object, after_hook_name, nil) - raise ex if ex - end - end - - def call_after_hooks(instrumenters, object, after_hook_name, ex) - instrumenters.reverse_each do |instrumenter| - begin - instrumenter.public_send(after_hook_name, object) - rescue => e - ex = e - end - end - ex - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/legacy_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/legacy_trace.rb deleted file mode 100644 index e4b8207440c..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/legacy_trace.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/trace" -require "graphql/tracing/call_legacy_tracers" - -module GraphQL - module Tracing - class LegacyTrace < Trace - include CallLegacyTracers - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/new_relic_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/new_relic_trace.rb deleted file mode 100644 index 534f875c9f8..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/new_relic_trace.rb +++ /dev/null @@ -1,183 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # A tracer for reporting GraphQL-Ruby time to New Relic - # - # @example Installing the tracer - # class MySchema < GraphQL::Schema - # trace_with GraphQL::Tracing::NewRelicTrace - # - # # Optional, use the operation name to set the new relic transaction name: - # # trace_with GraphQL::Tracing::NewRelicTrace, set_transaction_name: true - # end - module NewRelicTrace - # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_new_relic_transaction_name]`. - # @param trace_authorized [Boolean] If `false`, skip tracing `authorized?` calls - # @param trace_resolve_type [Boolean] If `false`, skip tracing `resolve_type?` calls - def initialize(set_transaction_name: false, trace_authorized: true, trace_resolve_type: true, **_rest) - @set_transaction_name = set_transaction_name - @trace_authorized = trace_authorized - @trace_resolve_type = trace_resolve_type - @nr_field_names = Hash.new do |h, field| - h[field] = "GraphQL/#{field.owner.graphql_name}/#{field.graphql_name}" - end.compare_by_identity - - @nr_authorized_names = Hash.new do |h, type| - h[type] = "GraphQL/Authorized/#{type.graphql_name}" - end.compare_by_identity - - @nr_resolve_type_names = Hash.new do |h, type| - h[type] = "GraphQL/ResolveType/#{type.graphql_name}" - end.compare_by_identity - - @nr_source_names = Hash.new do |h, source_inst| - h[source_inst] = "GraphQL/Source/#{source_inst.class.name}" - end.compare_by_identity - - @nr_parse = @nr_validate = @nr_analyze = @nr_execute = nil - super - end - - def begin_parse(query_str) - @nr_parse = NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: "GraphQL/parse", category: :web) - super - end - - def end_parse(query_str) - @nr_parse.finish - super - end - - def begin_validate(query, validate) - @nr_validate = NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: "GraphQL/validate", category: :web) - super - end - - def end_validate(query, validate, validation_errors) - @nr_validate.finish - super - end - - def begin_analyze_multiplex(multiplex, analyzers) - @nr_analyze = NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: "GraphQL/analyze", category: :web) - super - end - - def end_analyze_multiplex(multiplex, analyzers) - @nr_analyze.finish - super - end - - def begin_execute_multiplex(multiplex) - query = multiplex.queries.first - set_this_txn_name = query.context[:set_new_relic_transaction_name] - if set_this_txn_name || (set_this_txn_name.nil? && @set_transaction_name) - NewRelic::Agent.set_transaction_name(transaction_name(query)) - end - @nr_execute = NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: "GraphQL/execute", category: :web) - super - end - - def end_execute_multiplex(multiplex) - @nr_execute.finish - super - end - - def begin_execute_field(field, object, arguments, query) - nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_field_names[field], category: :web) - super - end - - def end_execute_field(field, objects, arguments, query, result) - nr_segment_stack.pop.finish - super - end - - def begin_authorized(type, obj, ctx) - if @trace_authorized - nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_authorized_names[type], category: :web) - end - super - end - - def end_authorized(type, obj, ctx, is_authed) - if @trace_authorized - nr_segment_stack.pop.finish - end - super - end - - def begin_resolve_type(type, value, context) - if @trace_resolve_type - nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_resolve_type_names[type], category: :web) - end - super - end - - def end_resolve_type(type, value, context, resolved_type) - if @trace_resolve_type - nr_segment_stack.pop.finish - end - super - end - - def begin_dataloader(dl) - super - end - - def end_dataloader(dl) - super - end - - def begin_dataloader_source(source) - nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_source_names[source], category: :web) - super - end - - def end_dataloader_source(source) - nr_segment_stack.pop.finish - super - end - - def dataloader_fiber_yield(source) - current_segment = nr_segment_stack.last - current_segment.finish - super - end - - def dataloader_fiber_resume(source) - prev_segment = nr_segment_stack.pop - seg_partial_name = prev_segment.name.sub(/^.*(GraphQL.*)$/, '\1') - nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: seg_partial_name, category: :web) - super - end - - private - - def nr_segment_stack - Fiber[:graphql_nr_segment_stack] ||= [] - end - - def transaction_name(query) - selected_op = query.selected_operation - txn_name = if selected_op - op_type = selected_op.operation_type - op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous" - "#{op_type}.#{op_name}" - else - "query.anonymous" - end - "GraphQL/#{txn_name}" - end - - def fallback_transaction_name(context) - context[:tracing_fallback_transaction_name] - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/new_relic_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/new_relic_tracing.rb deleted file mode 100644 index a2d05b5203a..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/new_relic_tracing.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - class NewRelicTracing < PlatformTracing - self.platform_keys = { - "lex" => "GraphQL/lex", - "parse" => "GraphQL/parse", - "validate" => "GraphQL/validate", - "analyze_query" => "GraphQL/analyze", - "analyze_multiplex" => "GraphQL/analyze", - "execute_multiplex" => "GraphQL/execute", - "execute_query" => "GraphQL/execute", - "execute_query_lazy" => "GraphQL/execute", - } - - # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_new_relic_transaction_name]`. - def initialize(options = {}) - @set_transaction_name = options.fetch(:set_transaction_name, false) - super - end - - def platform_trace(platform_key, key, data) - if key == "execute_query" - set_this_txn_name = data[:query].context[:set_new_relic_transaction_name] - if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name) - NewRelic::Agent.set_transaction_name(transaction_name(data[:query])) - end - end - - NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped(platform_key) do - yield - end - end - - def platform_field_key(type, field) - "GraphQL/#{type.graphql_name}/#{field.graphql_name}" - end - - def platform_authorized_key(type) - "GraphQL/Authorize/#{type.graphql_name}" - end - - def platform_resolve_type_key(type) - "GraphQL/ResolveType/#{type.graphql_name}" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/notifications_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/notifications_trace.rb deleted file mode 100644 index 12b980cf358..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/notifications_trace.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # This implementation forwards events to a notification handler (i.e. - # ActiveSupport::Notifications or Dry::Monitor::Notifications) - # with a `graphql` suffix. - module NotificationsTrace - # Initialize a new NotificationsTracing instance - # - # @param engine [#instrument(key, metadata, block)] The notifications engine to use - def initialize(engine:, **rest) - @notifications_engine = engine - super - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - "lex" => "lex.graphql", - "parse" => "parse.graphql", - "validate" => "validate.graphql", - "analyze_multiplex" => "analyze_multiplex.graphql", - "analyze_query" => "analyze_query.graphql", - "execute_multiplex" => "execute_multiplex.graphql", - "execute_query" => "execute_query.graphql", - "execute_query_lazy" => "execute_query_lazy.graphql", - "execute_field" => "execute_field.graphql", - "execute_field_lazy" => "execute_field_lazy.graphql", - "authorized" => "authorized.graphql", - "authorized_lazy" => "authorized_lazy.graphql", - "resolve_type" => "resolve_type.graphql", - "resolve_type_lazy" => "resolve_type.graphql", - }.each do |trace_method, platform_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**metadata, &block) - @notifications_engine.instrument("#{platform_key}", metadata) { super(**metadata, &block) } - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - include PlatformTrace - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/notifications_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/notifications_tracing.rb deleted file mode 100644 index c8ad4f88612..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/notifications_tracing.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - # This implementation forwards events to a notification handler (i.e. - # ActiveSupport::Notifications or Dry::Monitor::Notifications) - # with a `graphql` suffix. - # - # @see KEYS for event names - class NotificationsTracing - # A cache of frequently-used keys to avoid needless string allocations - KEYS = { - "lex" => "lex.graphql", - "parse" => "parse.graphql", - "validate" => "validate.graphql", - "analyze_multiplex" => "analyze_multiplex.graphql", - "analyze_query" => "analyze_query.graphql", - "execute_query" => "execute_query.graphql", - "execute_query_lazy" => "execute_query_lazy.graphql", - "execute_field" => "execute_field.graphql", - "execute_field_lazy" => "execute_field_lazy.graphql", - "authorized" => "authorized.graphql", - "authorized_lazy" => "authorized_lazy.graphql", - "resolve_type" => "resolve_type.graphql", - "resolve_type_lazy" => "resolve_type.graphql", - } - - MAX_KEYS_SIZE = 100 - - # Initialize a new NotificationsTracing instance - # - # @param [Object] notifications_engine The notifications engine to use - def initialize(notifications_engine) - @notifications_engine = notifications_engine - end - - # Sends a GraphQL tracing event to the notification handler - # - # @example - # . notifications_engine = Dry::Monitor::Notifications.new(:graphql) - # . tracer = GraphQL::Tracing::NotificationsTracing.new(notifications_engine) - # . tracer.trace("lex") { ... } - # - # @param [string] key The key for the event - # @param [Hash] metadata The metadata for the event - # @yield The block to execute for the event - def trace(key, metadata, &blk) - prefixed_key = KEYS[key] || "#{key}.graphql" - - # Cache the new keys while making sure not to induce a memory leak - if KEYS.size < MAX_KEYS_SIZE - KEYS[key] ||= prefixed_key - end - - @notifications_engine.instrument(prefixed_key, metadata, &blk) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/null_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/null_trace.rb deleted file mode 100644 index 26e2b276955..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/null_trace.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/trace" - -module GraphQL - module Tracing - NullTrace = Trace.new - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace.rb deleted file mode 100644 index 3f7082ecd0c..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace.rb +++ /dev/null @@ -1,737 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Tracing - # This produces a trace file for inspecting in the [Perfetto Trace Viewer](https://ui.perfetto.dev). - # - # To get the file, call {#write} on the trace. - # - # Use "trace modes" to configure this to run on command or on a sample of traffic. - # - # @example Writing trace output - # - # result = MySchema.execute(...) - # result.query.trace.write(file: "tmp/trace.dump") - # - # @example Running this instrumenter when `trace: true` is present in the request - # - # class MySchema < GraphQL::Schema - # # Only run this tracer when `context[:trace_mode]` is `:trace` - # trace_with GraphQL::Tracing::Perfetto, mode: :trace - # end - # - # # In graphql_controller.rb: - # - # context[:trace_mode] = params[:trace] ? :trace : nil - # result = MySchema.execute(query_str, context: context, variables: variables, ...) - # if context[:trace_mode] == :trace - # result.trace.write(file: ...) - # end - # - module PerfettoTrace - # TODOs: - # - Make debug annotations visible on both parts when dataloader is involved - - PROTOBUF_AVAILABLE = begin - require "google/protobuf" - true - rescue LoadError - false - end - - if PROTOBUF_AVAILABLE - require "graphql/tracing/perfetto_trace/trace_pb" - end - - def self.included(_trace_class) - if !PROTOBUF_AVAILABLE - raise "#{self} can't be used because the `google-protobuf` gem wasn't available. Add it to your project, then try again." - end - end - - DATALOADER_CATEGORY_IIDS = [5] - FIELD_EXECUTE_CATEGORY_IIDS = [6] - ACTIVE_SUPPORT_NOTIFICATIONS_CATEGORY_IIDS = [7] - AUTHORIZED_CATEGORY_IIDS = [8] - RESOLVE_TYPE_CATEGORY_IIDS = [9] - - DA_OBJECT_IID = 10 - DA_RESULT_IID = 11 - DA_ARGUMENTS_IID = 12 - DA_FETCH_KEYS_IID = 13 - DA_STR_VAL_NIL_IID = 14 - - # @param active_support_notifications_pattern [String, RegExp, false] A filter for `ActiveSupport::Notifications`, if it's present. Or `false` to skip subscribing. - def initialize(active_support_notifications_pattern: nil, save_profile: false, **_rest) - super - @save_profile = save_profile - Fiber[:graphql_flow_stack] = nil - @sequence_id = object_id - @pid = Process.pid - @flow_ids = Hash.new { |h, source_inst| h[source_inst] = [] }.compare_by_identity - @new_interned_event_names = {} - @interned_event_name_iids = Hash.new { |h, k| - new_id = 100 + h.size - @new_interned_event_names[k] = new_id - h[k] = new_id - } - - @source_name_iids = Hash.new do |h, source_class| - h[source_class] = @interned_event_name_iids[source_class.name] - end.compare_by_identity - - @auth_name_iids = Hash.new do |h, graphql_type| - h[graphql_type] = @interned_event_name_iids["Authorize: #{graphql_type.graphql_name}"] - end.compare_by_identity - - @resolve_type_name_iids = Hash.new do |h, graphql_type| - h[graphql_type] = @interned_event_name_iids["Resolve Type: #{graphql_type.graphql_name}"] - end.compare_by_identity - - @new_interned_da_names = {} - @interned_da_name_ids = Hash.new { |h, k| - next_id = 100 + h.size - @new_interned_da_names[k] = next_id - h[k] = next_id - } - - @new_interned_da_string_values = {} - @interned_da_string_values = Hash.new do |h, k| - new_id = 100 + h.size - @new_interned_da_string_values[k] = new_id - h[k] = new_id - end - - @class_name_iids = Hash.new do |h, k| - h[k] = @interned_da_string_values[k.name] - end.compare_by_identity - - @starting_objects = GC.stat(:total_allocated_objects) - @objects_counter_id = :objects_counter.object_id - @fibers_counter_id = :fibers_counter.object_id - @fields_counter_id = :fields_counter.object_id - @begin_validate = nil - @begin_time = nil - @packets = [] - @packets << TracePacket.new( - track_descriptor: TrackDescriptor.new( - uuid: tid, - name: "Main Thread", - child_ordering: TrackDescriptor::ChildTracksOrdering::CHRONOLOGICAL, - ), - first_packet_on_sequence: true, - previous_packet_dropped: true, - trusted_packet_sequence_id: @sequence_id, - sequence_flags: 3, - ) - @packets << TracePacket.new( - interned_data: InternedData.new( - event_categories: [ - EventCategory.new(name: "Dataloader", iid: DATALOADER_CATEGORY_IIDS.first), - EventCategory.new(name: "Field Execution", iid: FIELD_EXECUTE_CATEGORY_IIDS.first), - EventCategory.new(name: "ActiveSupport::Notifications", iid: ACTIVE_SUPPORT_NOTIFICATIONS_CATEGORY_IIDS.first), - EventCategory.new(name: "Authorized", iid: AUTHORIZED_CATEGORY_IIDS.first), - EventCategory.new(name: "Resolve Type", iid: RESOLVE_TYPE_CATEGORY_IIDS.first), - ], - debug_annotation_names: [ - DebugAnnotationName.new(name: "object", iid: DA_OBJECT_IID), - DebugAnnotationName.new(name: "arguments", iid: DA_ARGUMENTS_IID), - DebugAnnotationName.new(name: "result", iid: DA_RESULT_IID), - DebugAnnotationName.new(name: "fetch keys", iid: DA_FETCH_KEYS_IID), - ], - debug_annotation_string_values: [ - InternedString.new(str: "(nil)", iid: DA_STR_VAL_NIL_IID), - ], - ), - trusted_packet_sequence_id: @sequence_id, - sequence_flags: 2, - ) - @main_fiber_id = fid - @packets << track_descriptor_packet(tid, fid, "Main Fiber") - @packets << track_descriptor_packet(tid, @objects_counter_id, "Allocated Objects", counter: {}) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_COUNTER, - track_uuid: @objects_counter_id, - counter_value: count_allocations, - ) - @packets << track_descriptor_packet(tid, @fibers_counter_id, "Active Fibers", counter: {}) - @fibers_count = 0 - @packets << trace_packet( - type: TrackEvent::Type::TYPE_COUNTER, - track_uuid: @fibers_counter_id, - counter_value: count_fibers(0), - ) - - @packets << track_descriptor_packet(tid, @fields_counter_id, "Resolved Fields", counter: {}) - @fields_count = -1 - @packets << trace_packet( - type: TrackEvent::Type::TYPE_COUNTER, - track_uuid: @fields_counter_id, - counter_value: count_fields, - ) - - if defined?(ActiveSupport::Notifications) && active_support_notifications_pattern != false - subscribe_to_active_support_notifications(active_support_notifications_pattern) - end - end - - def begin_execute_multiplex(m) - @operation_name = m.queries.map { |q| q.selected_operation_name || "anonymous" }.join(",") - @begin_time = Time.now - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - name: "Multiplex", - debug_annotations: [ - payload_to_debug("query_string", m.queries.map(&:sanitized_query_string).join("\n\n")) - ] - ) - super - end - - def end_execute_multiplex(m) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - ) - unsubscribe_from_active_support_notifications - if @save_profile - begin_ts = (@begin_time.to_f * 1000).round - end_ts = (Time.now.to_f * 1000).round - duration_ms = end_ts - begin_ts - m.schema.detailed_trace.save_trace(@operation_name, duration_ms, begin_ts, Trace.encode(Trace.new(packet: @packets))) - end - super - end - - def begin_execute_field(field, object, arguments, query) - packet = trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - name: query.context.current_path.join("."), - category_iids: FIELD_EXECUTE_CATEGORY_IIDS, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - @packets << packet - fiber_flow_stack << packet - super - end - - def end_execute_field(field, object, arguments, query, app_result) - start_field = fiber_flow_stack.pop - start_field.track_event = dup_with(start_field.track_event, { - debug_annotations: [ - payload_to_debug(nil, object.object, iid: DA_OBJECT_IID, intern_value: true), - payload_to_debug(nil, arguments, iid: DA_ARGUMENTS_IID), - payload_to_debug(nil, app_result, iid: DA_RESULT_IID, intern_value: true) - ] - }) - - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id, @fields_counter_id], - extra_counter_values: [count_allocations, count_fields], - ) - super - end - - def begin_analyze_multiplex(m, analyzers) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - name: "Analysis", - debug_annotations: [ - payload_to_debug("analyzers_count", analyzers.size), - payload_to_debug("analyzers", analyzers), - ] - ) - super - end - - def end_analyze_multiplex(m, analyzers) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - super - end - - def begin_parse(str) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - name: "Parse" - ) - super - end - - def end_parse(str) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - super - end - - def begin_validate(query, validate) - @packets << @begin_validate = trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - name: "Validate", - debug_annotations: [ - payload_to_debug("validate?", validate), - ] - ) - super - end - - def end_validate(query, validate, validation_errors) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - @begin_validate.track_event = dup_with( - @begin_validate.track_event, - { - debug_annotations: [ - @begin_validate.track_event.debug_annotations.first, - payload_to_debug("valid?", validation_errors.empty?) - ] - } - ) - super - end - - def dataloader_spawn_execution_fiber(jobs) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_INSTANT, - track_uuid: fid, - name: "Create Execution Fiber", - category_iids: DATALOADER_CATEGORY_IIDS, - extra_counter_track_uuids: [@fibers_counter_id, @objects_counter_id], - extra_counter_values: [count_fibers(1), count_allocations] - ) - @packets << track_descriptor_packet(@did, fid, "Exec Fiber ##{fid}") - super - end - - def dataloader_spawn_source_fiber(pending_sources) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_INSTANT, - track_uuid: fid, - name: "Create Source Fiber", - category_iids: DATALOADER_CATEGORY_IIDS, - extra_counter_track_uuids: [@fibers_counter_id, @objects_counter_id], - extra_counter_values: [count_fibers(1), count_allocations] - ) - @packets << track_descriptor_packet(@did, fid, "Source Fiber ##{fid}") - super - end - - def dataloader_fiber_yield(source) - ls = fiber_flow_stack.last - if (flow_id = ls.track_event.flow_ids.first) - # got it - else - flow_id = ls.track_event.name.object_id - ls.track_event = dup_with(ls.track_event, {flow_ids: [flow_id] }, delete_counters: true) - end - @flow_ids[source] << flow_id - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - ) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_INSTANT, - track_uuid: fid, - name: "Fiber Yield", - category_iids: DATALOADER_CATEGORY_IIDS, - ) - super - end - - def dataloader_fiber_resume(source) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_INSTANT, - track_uuid: fid, - name: "Fiber Resume", - category_iids: DATALOADER_CATEGORY_IIDS, - ) - - ls = fiber_flow_stack.pop - @packets << packet = TracePacket.new( - timestamp: ts, - track_event: dup_with(ls.track_event, { type: TrackEvent::Type::TYPE_SLICE_BEGIN }), - trusted_packet_sequence_id: @sequence_id, - ) - fiber_flow_stack << packet - - super - end - - def dataloader_fiber_exit - @packets << trace_packet( - type: TrackEvent::Type::TYPE_INSTANT, - track_uuid: fid, - name: "Fiber Exit", - category_iids: DATALOADER_CATEGORY_IIDS, - extra_counter_track_uuids: [@fibers_counter_id], - extra_counter_values: [count_fibers(-1)], - ) - super - end - - def begin_dataloader(dl) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_COUNTER, - track_uuid: @fibers_counter_id, - counter_value: count_fibers(1), - ) - @did = fid - @packets << track_descriptor_packet(@main_fiber_id, @did, "Dataloader Fiber ##{@did}") - super - end - - def end_dataloader(dl) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_COUNTER, - track_uuid: @fibers_counter_id, - counter_value: count_fibers(-1), - ) - super - end - - def begin_dataloader_source(source) - fds = @flow_ids[source] - fds_copy = fds.dup - fds.clear - packet = trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - name_iid: @source_name_iids[source.class], - category_iids: DATALOADER_CATEGORY_IIDS, - flow_ids: fds_copy, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - debug_annotations: [ - payload_to_debug(nil, source.pending.values, iid: DA_FETCH_KEYS_IID, intern_value: true), - *(source.instance_variables - [:@pending, :@fetching, :@results, :@dataloader]).map { |iv| - payload_to_debug(iv.to_s, source.instance_variable_get(iv), intern_value: true) - } - ] - ) - @packets << packet - fiber_flow_stack << packet - super - end - - def end_dataloader_source(source) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - fiber_flow_stack.pop - super - end - - def begin_authorized(type, obj, ctx) - packet = trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - category_iids: AUTHORIZED_CATEGORY_IIDS, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - name_iid: @auth_name_iids[type], - ) - @packets << packet - fiber_flow_stack << packet - super - end - - def end_authorized(type, obj, ctx, is_authorized) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - beg_auth = fiber_flow_stack.pop - beg_auth.track_event = dup_with(beg_auth.track_event, { debug_annotations: [payload_to_debug("authorized?", is_authorized)] }) - super - end - - def begin_resolve_type(type, value, context) - packet = trace_packet( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - category_iids: RESOLVE_TYPE_CATEGORY_IIDS, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - name_iid: @resolve_type_name_iids[type], - ) - @packets << packet - fiber_flow_stack << packet - super - end - - def end_resolve_type(type, value, context, resolved_type) - @packets << trace_packet( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations], - ) - rt_begin = fiber_flow_stack.pop - rt_begin.track_event = dup_with(rt_begin.track_event, { debug_annotations: [payload_to_debug("resolved_type", resolved_type, intern_value: true)] }) - super - end - - # Dump protobuf output in the specified file. - # @param file [String] path to a file in a directory that already exists - # @param debug_json [Boolean] True to print JSON instead of binary - # @return [nil, String, Hash] If `file` was given, `nil`. If `file` was `nil`, a Hash if `debug_json: true`, else binary data. - def write(file:, debug_json: false) - trace = Trace.new( - packet: @packets, - ) - data = if debug_json - small_json = Trace.encode_json(trace) - JSON.pretty_generate(JSON.parse(small_json)) - else - Trace.encode(trace) - end - - if file - File.write(file, data, mode: 'wb') - nil - else - data - end - end - - private - - def ts - Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) - end - - def tid - Thread.current.object_id - end - - def fid - Fiber.current.object_id - end - - def debug_annotation(iid, value_key, value) - if iid - DebugAnnotation.new(name_iid: iid, value_key => value) - else - DebugAnnotation.new(value_key => value) - end - end - - def payload_to_debug(k, v, iid: nil, intern_value: false) - if iid.nil? - iid = @interned_da_name_ids[k] - k = nil - end - case v - when String - if intern_value - v = @interned_da_string_values[v] - debug_annotation(iid, :string_value_iid, v) - else - debug_annotation(iid, :string_value, v) - end - when Float - debug_annotation(iid, :double_value, v) - when Integer - debug_annotation(iid, :int_value, v) - when true, false - debug_annotation(iid, :bool_value, v) - when nil - if iid - DebugAnnotation.new(name_iid: iid, string_value_iid: DA_STR_VAL_NIL_IID) - else - DebugAnnotation.new(name: k, string_value_iid: DA_STR_VAL_NIL_IID) - end - when Module - if intern_value - val_iid = @class_name_iids[v] - debug_annotation(iid, :string_value_iid, val_iid) - else - debug_annotation(iid, :string_value, v.name) - end - when Symbol - debug_annotation(iid, :string_value, v.inspect) - when Array - debug_annotation(iid, :array_values, v.map { |v2| payload_to_debug(nil, v2, intern_value: intern_value) }.compact) - when Hash - debug_annotation(iid, :dict_entries, v.map { |k2, v2| payload_to_debug(k2, v2, intern_value: intern_value) }.compact) - else - debug_str = if defined?(ActiveRecord::Relation) && v.is_a?(ActiveRecord::Relation) - "#{v.class}, .to_sql=#{v.to_sql.inspect}" - else - v.inspect - end - if intern_value - str_iid = @interned_da_string_values[debug_str] - debug_annotation(iid, :string_value_iid, str_iid) - else - debug_annotation(iid, :string_value, debug_str) - end - end - end - - def count_allocations - GC.stat(:total_allocated_objects) - @starting_objects - end - - def count_fibers(diff) - @fibers_count += diff - end - - def count_fields - @fields_count += 1 - end - - def dup_with(message, attrs, delete_counters: false) - new_attrs = message.to_h - if delete_counters - new_attrs.delete(:extra_counter_track_uuids) - new_attrs.delete(:extra_counter_values) - end - new_attrs.merge!(attrs) - message.class.new(**new_attrs) - end - - def fiber_flow_stack - Fiber[:graphql_flow_stack] ||= [] - end - - def trace_packet(event_attrs) - TracePacket.new( - timestamp: ts, - track_event: TrackEvent.new(event_attrs), - trusted_packet_sequence_id: @sequence_id, - sequence_flags: 2, - interned_data: new_interned_data - ) - end - - def new_interned_data - if !@new_interned_da_names.empty? - da_names = @new_interned_da_names.map { |(name, iid)| DebugAnnotationName.new(iid: iid, name: name) } - @new_interned_da_names.clear - end - - if !@new_interned_event_names.empty? - ev_names = @new_interned_event_names.map { |(name, iid)| EventName.new(iid: iid, name: name) } - @new_interned_event_names.clear - end - - if !@new_interned_da_string_values.empty? - str_vals = @new_interned_da_string_values.map { |name, iid| InternedString.new(iid: iid, str: name) } - @new_interned_da_string_values.clear - end - - if ev_names || da_names || str_vals - InternedData.new( - event_names: ev_names, - debug_annotation_names: da_names, - debug_annotation_string_values: str_vals, - ) - else - nil - end - end - - def track_descriptor_packet(parent_uuid, uuid, name, counter: nil) - td = if counter - TrackDescriptor.new( - parent_uuid: parent_uuid, - uuid: uuid, - name: name, - counter: counter - ) - else - TrackDescriptor.new( - parent_uuid: parent_uuid, - uuid: uuid, - name: name, - child_ordering: TrackDescriptor::ChildTracksOrdering::CHRONOLOGICAL, - ) - end - TracePacket.new( - track_descriptor: td, - trusted_packet_sequence_id: @sequence_id, - sequence_flags: 2, - ) - end - - def unsubscribe_from_active_support_notifications - if defined?(@as_subscriber) - ActiveSupport::Notifications.unsubscribe(@as_subscriber) - end - end - - def subscribe_to_active_support_notifications(pattern) - @as_subscriber = ActiveSupport::Notifications.monotonic_subscribe(pattern) do |name, start, finish, id, payload| - metadata = payload.map { |k, v| payload_to_debug(k, v, intern_value: true) } - metadata.compact! - te = if metadata.empty? - TrackEvent.new( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - category_iids: ACTIVE_SUPPORT_NOTIFICATIONS_CATEGORY_IIDS, - name: name, - ) - else - TrackEvent.new( - type: TrackEvent::Type::TYPE_SLICE_BEGIN, - track_uuid: fid, - name: name, - category_iids: ACTIVE_SUPPORT_NOTIFICATIONS_CATEGORY_IIDS, - debug_annotations: metadata, - ) - end - @packets << TracePacket.new( - timestamp: (start * 1_000_000_000).to_i, - track_event: te, - trusted_packet_sequence_id: @sequence_id, - sequence_flags: 2, - interned_data: new_interned_data - ) - @packets << TracePacket.new( - timestamp: (finish * 1_000_000_000).to_i, - track_event: TrackEvent.new( - type: TrackEvent::Type::TYPE_SLICE_END, - track_uuid: fid, - name: name, - extra_counter_track_uuids: [@objects_counter_id], - extra_counter_values: [count_allocations] - ), - trusted_packet_sequence_id: @sequence_id, - sequence_flags: 2, - ) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace.proto b/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace.proto deleted file mode 100644 index 50607f7ad30..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace.proto +++ /dev/null @@ -1,141 +0,0 @@ -// This is an abbreviated version of the full Perfetto schema. -// Most of them are for OS or Chrome traces and we'll never use them. -// Full doc: https://github.com/google/perfetto/tree/main/protos/perfetto -// -// Build it with -// protoc --ruby_out=lib/graphql/tracing/perfetto_trace --proto_path=lib/graphql/tracing/perfetto_trace trace.proto -syntax = "proto2"; -package perfetto_trace.protos; -option ruby_package = "GraphQL::Tracing::PerfettoTrace"; - -message Trace { - repeated TracePacket packet = 1; -} - -message TracePacket { - optional uint64 timestamp = 8; - oneof data { - TrackEvent track_event = 11; - TrackDescriptor track_descriptor = 60; - } - oneof optional_trusted_packet_sequence_id { - uint32 trusted_packet_sequence_id = 10; - } - optional InternedData interned_data = 12; - optional bool first_packet_on_sequence = 87; - optional bool previous_packet_dropped = 42; - optional uint32 sequence_flags = 13; -} - -message TrackEvent { - repeated uint64 category_iids = 3; - repeated string categories = 22; - oneof name_field { - uint64 name_iid = 10; - string name = 23; - } - enum Type { - TYPE_UNSPECIFIED = 0; - TYPE_SLICE_BEGIN = 1; - TYPE_SLICE_END = 2; - TYPE_INSTANT = 3; - TYPE_COUNTER = 4; - } - optional Type type = 9; - optional uint64 track_uuid = 11; - oneof counter_value_field { - int64 counter_value = 30; - double double_counter_value = 44; - } - repeated uint64 extra_counter_track_uuids = 31; - repeated int64 extra_counter_values = 12; - repeated uint64 extra_double_counter_track_uuids = 45; - repeated double extra_double_counter_values = 46; - repeated fixed64 flow_ids = 47; - repeated fixed64 terminating_flow_ids = 48; - repeated DebugAnnotation debug_annotations = 4; -} - -message DebugAnnotation { - oneof name_field { - uint64 name_iid = 1; - string name = 10; - } - oneof value { - bool bool_value = 2; - uint64 uint_value = 3; - int64 int_value = 4; - double double_value = 5; - string string_value = 6; - uint64 string_value_iid = 17; - } - repeated DebugAnnotation dict_entries = 11; - repeated DebugAnnotation array_values = 12; - uint64 string_value_iid = 17; -} - -message TrackDescriptor { - optional uint64 uuid = 1; - optional uint64 parent_uuid = 5; - - oneof static_or_dynamic_name { - string name = 2; - } - - optional CounterDescriptor counter = 8; - enum ChildTracksOrdering { - UNKNOWN = 0; - LEXICOGRAPHIC = 1; - CHRONOLOGICAL = 2; - EXPLICIT = 3; - } - optional ChildTracksOrdering child_ordering = 11; - optional int32 sibling_order_rank = 12; -} - -message CounterDescriptor { - enum BuiltinCounterType { - COUNTER_UNSPECIFIED = 0; - COUNTER_THREAD_TIME_NS = 1; - COUNTER_THREAD_INSTRUCTION_COUNT = 2; - } - enum Unit { - UNIT_UNSPECIFIED = 0; - UNIT_TIME_NS = 1; - UNIT_COUNT = 2; - UNIT_SIZE_BYTES = 3; - } - optional BuiltinCounterType type = 1; - repeated string categories = 2; - optional Unit unit = 3; - optional string unit_name = 6; - optional int64 unit_multiplier = 4; - optional bool is_incremental = 5; -} - -message InternedData { - repeated EventCategory event_categories = 1; - repeated EventName event_names = 2; - repeated DebugAnnotationName debug_annotation_names = 3; - repeated InternedString debug_annotation_string_values = 29; -} - -message InternedString { - optional uint64 iid = 1; - optional bytes str = 2; -} - -message EventCategory { - optional uint64 iid = 1; - optional string name = 2; -} - -message EventName { - optional uint64 iid = 1; - optional string name = 2; -} - -message DebugAnnotationName { - optional uint64 iid = 1; - optional string name = 2; -} diff --git a/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace_pb.rb b/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace_pb.rb deleted file mode 100644 index 8cb90133ddb..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/perfetto_trace/trace_pb.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: trace.proto - -require 'google/protobuf' - - -descriptor_data = "\n\x0btrace.proto\x12\x15perfetto_trace.protos\";\n\x05Trace\x12\x32\n\x06packet\x18\x01 \x03(\x0b\x32\".perfetto_trace.protos.TracePacket\"\x8a\x03\n\x0bTracePacket\x12\x11\n\ttimestamp\x18\x08 \x01(\x04\x12\x38\n\x0btrack_event\x18\x0b \x01(\x0b\x32!.perfetto_trace.protos.TrackEventH\x00\x12\x42\n\x10track_descriptor\x18< \x01(\x0b\x32&.perfetto_trace.protos.TrackDescriptorH\x00\x12$\n\x1atrusted_packet_sequence_id\x18\n \x01(\rH\x01\x12:\n\rinterned_data\x18\x0c \x01(\x0b\x32#.perfetto_trace.protos.InternedData\x12 \n\x18\x66irst_packet_on_sequence\x18W \x01(\x08\x12\x1f\n\x17previous_packet_dropped\x18* \x01(\x08\x12\x16\n\x0esequence_flags\x18\r \x01(\rB\x06\n\x04\x64\x61taB%\n#optional_trusted_packet_sequence_id\"\xf2\x04\n\nTrackEvent\x12\x15\n\rcategory_iids\x18\x03 \x03(\x04\x12\x12\n\ncategories\x18\x16 \x03(\t\x12\x12\n\x08name_iid\x18\n \x01(\x04H\x00\x12\x0e\n\x04name\x18\x17 \x01(\tH\x00\x12\x34\n\x04type\x18\t \x01(\x0e\x32&.perfetto_trace.protos.TrackEvent.Type\x12\x12\n\ntrack_uuid\x18\x0b \x01(\x04\x12\x17\n\rcounter_value\x18\x1e \x01(\x03H\x01\x12\x1e\n\x14\x64ouble_counter_value\x18, \x01(\x01H\x01\x12!\n\x19\x65xtra_counter_track_uuids\x18\x1f \x03(\x04\x12\x1c\n\x14\x65xtra_counter_values\x18\x0c \x03(\x03\x12(\n extra_double_counter_track_uuids\x18- \x03(\x04\x12#\n\x1b\x65xtra_double_counter_values\x18. \x03(\x01\x12\x10\n\x08\x66low_ids\x18/ \x03(\x06\x12\x1c\n\x14terminating_flow_ids\x18\x30 \x03(\x06\x12\x41\n\x11\x64\x65\x62ug_annotations\x18\x04 \x03(\x0b\x32&.perfetto_trace.protos.DebugAnnotation\"j\n\x04Type\x12\x14\n\x10TYPE_UNSPECIFIED\x10\x00\x12\x14\n\x10TYPE_SLICE_BEGIN\x10\x01\x12\x12\n\x0eTYPE_SLICE_END\x10\x02\x12\x10\n\x0cTYPE_INSTANT\x10\x03\x12\x10\n\x0cTYPE_COUNTER\x10\x04\x42\x0c\n\nname_fieldB\x15\n\x13\x63ounter_value_field\"\xd5\x02\n\x0f\x44\x65\x62ugAnnotation\x12\x12\n\x08name_iid\x18\x01 \x01(\x04H\x00\x12\x0e\n\x04name\x18\n \x01(\tH\x00\x12\x14\n\nbool_value\x18\x02 \x01(\x08H\x01\x12\x14\n\nuint_value\x18\x03 \x01(\x04H\x01\x12\x13\n\tint_value\x18\x04 \x01(\x03H\x01\x12\x16\n\x0c\x64ouble_value\x18\x05 \x01(\x01H\x01\x12\x16\n\x0cstring_value\x18\x06 \x01(\tH\x01\x12\x1a\n\x10string_value_iid\x18\x11 \x01(\x04H\x01\x12<\n\x0c\x64ict_entries\x18\x0b \x03(\x0b\x32&.perfetto_trace.protos.DebugAnnotation\x12<\n\x0c\x61rray_values\x18\x0c \x03(\x0b\x32&.perfetto_trace.protos.DebugAnnotationB\x0c\n\nname_fieldB\x07\n\x05value\"\xe1\x02\n\x0fTrackDescriptor\x12\x0c\n\x04uuid\x18\x01 \x01(\x04\x12\x13\n\x0bparent_uuid\x18\x05 \x01(\x04\x12\x0e\n\x04name\x18\x02 \x01(\tH\x00\x12\x39\n\x07\x63ounter\x18\x08 \x01(\x0b\x32(.perfetto_trace.protos.CounterDescriptor\x12R\n\x0e\x63hild_ordering\x18\x0b \x01(\x0e\x32:.perfetto_trace.protos.TrackDescriptor.ChildTracksOrdering\x12\x1a\n\x12sibling_order_rank\x18\x0c \x01(\x05\"V\n\x13\x43hildTracksOrdering\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x11\n\rLEXICOGRAPHIC\x10\x01\x12\x11\n\rCHRONOLOGICAL\x10\x02\x12\x0c\n\x08\x45XPLICIT\x10\x03\x42\x18\n\x16static_or_dynamic_name\"\xb9\x03\n\x11\x43ounterDescriptor\x12I\n\x04type\x18\x01 \x01(\x0e\x32;.perfetto_trace.protos.CounterDescriptor.BuiltinCounterType\x12\x12\n\ncategories\x18\x02 \x03(\t\x12;\n\x04unit\x18\x03 \x01(\x0e\x32-.perfetto_trace.protos.CounterDescriptor.Unit\x12\x11\n\tunit_name\x18\x06 \x01(\t\x12\x17\n\x0funit_multiplier\x18\x04 \x01(\x03\x12\x16\n\x0eis_incremental\x18\x05 \x01(\x08\"o\n\x12\x42uiltinCounterType\x12\x17\n\x13\x43OUNTER_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43OUNTER_THREAD_TIME_NS\x10\x01\x12$\n COUNTER_THREAD_INSTRUCTION_COUNT\x10\x02\"S\n\x04Unit\x12\x14\n\x10UNIT_UNSPECIFIED\x10\x00\x12\x10\n\x0cUNIT_TIME_NS\x10\x01\x12\x0e\n\nUNIT_COUNT\x10\x02\x12\x13\n\x0fUNIT_SIZE_BYTES\x10\x03\"\xa0\x02\n\x0cInternedData\x12>\n\x10\x65vent_categories\x18\x01 \x03(\x0b\x32$.perfetto_trace.protos.EventCategory\x12\x35\n\x0b\x65vent_names\x18\x02 \x03(\x0b\x32 .perfetto_trace.protos.EventName\x12J\n\x16\x64\x65\x62ug_annotation_names\x18\x03 \x03(\x0b\x32*.perfetto_trace.protos.DebugAnnotationName\x12M\n\x1e\x64\x65\x62ug_annotation_string_values\x18\x1d \x03(\x0b\x32%.perfetto_trace.protos.InternedString\"*\n\x0eInternedString\x12\x0b\n\x03iid\x18\x01 \x01(\x04\x12\x0b\n\x03str\x18\x02 \x01(\x0c\"*\n\rEventCategory\x12\x0b\n\x03iid\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\"&\n\tEventName\x12\x0b\n\x03iid\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\"0\n\x13\x44\x65\x62ugAnnotationName\x12\x0b\n\x03iid\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\tB\"\xea\x02\x1fGraphQL::Tracing::PerfettoTrace" - -pool = Google::Protobuf::DescriptorPool.generated_pool -pool.add_serialized_file(descriptor_data) - -module GraphQL - module Tracing - module PerfettoTrace - Trace = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.Trace").msgclass - TracePacket = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.TracePacket").msgclass - TrackEvent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.TrackEvent").msgclass - TrackEvent::Type = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.TrackEvent.Type").enummodule - DebugAnnotation = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.DebugAnnotation").msgclass - TrackDescriptor = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.TrackDescriptor").msgclass - TrackDescriptor::ChildTracksOrdering = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.TrackDescriptor.ChildTracksOrdering").enummodule - CounterDescriptor = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.CounterDescriptor").msgclass - CounterDescriptor::BuiltinCounterType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.CounterDescriptor.BuiltinCounterType").enummodule - CounterDescriptor::Unit = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.CounterDescriptor.Unit").enummodule - InternedData = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.InternedData").msgclass - InternedString = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.InternedString").msgclass - EventCategory = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.EventCategory").msgclass - EventName = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.EventName").msgclass - DebugAnnotationName = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("perfetto_trace.protos.DebugAnnotationName").msgclass - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/platform_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/platform_trace.rb deleted file mode 100644 index 895b7e25be5..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/platform_trace.rb +++ /dev/null @@ -1,123 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Tracing - module PlatformTrace - def initialize(trace_scalars: false, **_options) - @trace_scalars = trace_scalars - - @platform_key_cache = Hash.new { |h, mod| h[mod] = mod::KeyCache.new } - super - end - - module BaseKeyCache - def initialize - @platform_field_key_cache = Hash.new { |h, k| h[k] = platform_field_key(k) } - @platform_authorized_key_cache = Hash.new { |h, k| h[k] = platform_authorized_key(k) } - @platform_resolve_type_key_cache = Hash.new { |h, k| h[k] = platform_resolve_type_key(k) } - end - - attr_reader :platform_field_key_cache, :platform_authorized_key_cache, :platform_resolve_type_key_cache - end - - - def platform_execute_field_lazy(*args, &block) - platform_execute_field(*args, &block) - end - - def platform_authorized_lazy(key, &block) - platform_authorized(key, &block) - end - - def platform_resolve_type_lazy(key, &block) - platform_resolve_type(key, &block) - end - - def self.included(child_class) - key_methods_class = Class.new { - include(child_class) - include(BaseKeyCache) - } - child_class.const_set(:KeyCache, key_methods_class) - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - [:execute_field, :execute_field_lazy].each do |field_trace_method| - if !child_class.method_defined?(field_trace_method) - child_class.module_eval <<-RUBY, __FILE__, __LINE__ - def #{field_trace_method}(query:, field:, ast_node:, arguments:, object:) - return_type = field.type.unwrap - trace_field = if return_type.kind.scalar? || return_type.kind.enum? - (field.trace.nil? && @trace_scalars) || field.trace - else - true - end - platform_key = if trace_field - @platform_key_cache[#{child_class}].platform_field_key_cache[field] - else - nil - end - if platform_key && trace_field - platform_#{field_trace_method}(platform_key) do - super - end - else - super - end - end - RUBY - end - end - - - [:authorized, :authorized_lazy].each do |auth_trace_method| - if !child_class.method_defined?(auth_trace_method) - child_class.module_eval <<-RUBY, __FILE__, __LINE__ - def #{auth_trace_method}(type:, query:, object:) - platform_key = @platform_key_cache[#{child_class}].platform_authorized_key_cache[type] - platform_#{auth_trace_method}(platform_key) do - super - end - end - RUBY - end - end - - [:resolve_type, :resolve_type_lazy].each do |rt_trace_method| - if !child_class.method_defined?(rt_trace_method) - child_class.module_eval <<-RUBY, __FILE__, __LINE__ - def #{rt_trace_method}(query:, type:, object:) - platform_key = @platform_key_cache[#{child_class}].platform_resolve_type_key_cache[type] - platform_#{rt_trace_method}(platform_key) do - super - end - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - end - end - - private - - # Get the transaction name based on the operation type and name if possible, or fall back to a user provided - # one. Useful for anonymous queries. - def transaction_name(query) - selected_op = query.selected_operation - txn_name = if selected_op - op_type = selected_op.operation_type - op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous" - "#{op_type}.#{op_name}" - else - "query.anonymous" - end - "GraphQL/#{txn_name}" - end - - def fallback_transaction_name(context) - context[:tracing_fallback_transaction_name] - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/platform_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/platform_tracing.rb deleted file mode 100644 index 6f0984d92e2..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/platform_tracing.rb +++ /dev/null @@ -1,136 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Tracing - # Each platform provides: - # - `.platform_keys` - # - `#platform_trace` - # - `#platform_field_key(type, field)` - # @api private - class PlatformTracing - class << self - attr_accessor :platform_keys - - def inherited(child_class) - child_class.platform_keys = self.platform_keys - end - end - - def initialize(options = {}) - @options = options - @platform_keys = self.class.platform_keys - @trace_scalars = options.fetch(:trace_scalars, false) - end - - def trace(key, data) - case key - when "lex", "parse", "validate", "analyze_query", "analyze_multiplex", "execute_query", "execute_query_lazy", "execute_multiplex" - platform_key = @platform_keys.fetch(key) - platform_trace(platform_key, key, data) do - yield - end - when "execute_field", "execute_field_lazy" - field = data[:field] - return_type = field.type.unwrap - trace_field = if return_type.kind.scalar? || return_type.kind.enum? - (field.trace.nil? && @trace_scalars) || field.trace - else - true - end - - platform_key = if trace_field - context = data.fetch(:query).context - cached_platform_key(context, field, :field) { platform_field_key(field.owner, field) } - else - nil - end - - if platform_key && trace_field - platform_trace(platform_key, key, data) do - yield - end - else - yield - end - when "authorized", "authorized_lazy" - type = data.fetch(:type) - context = data.fetch(:context) - platform_key = cached_platform_key(context, type, :authorized) { platform_authorized_key(type) } - platform_trace(platform_key, key, data) do - yield - end - when "resolve_type", "resolve_type_lazy" - type = data.fetch(:type) - context = data.fetch(:context) - platform_key = cached_platform_key(context, type, :resolve_type) { platform_resolve_type_key(type) } - platform_trace(platform_key, key, data) do - yield - end - else - # it's a custom key - yield - end - end - - def self.use(schema_defn, options = {}) - if options[:legacy_tracing] - tracer = self.new(**options) - schema_defn.tracer(tracer) - else - tracing_name = self.name.split("::").last - trace_name = tracing_name.sub("Tracing", "Trace") - if GraphQL::Tracing.const_defined?(trace_name, false) - trace_module = GraphQL::Tracing.const_get(trace_name) - warn("`use(#{self.name})` is deprecated, use the equivalent `trace_with(#{trace_module.name})` instead. More info: https://graphql-ruby.org/queries/tracing.html") - schema_defn.trace_with(trace_module, **options) - else - warn("`use(#{self.name})` and `Tracing::PlatformTracing` are deprecated. Use a `trace_with(...)` module instead. More info: https://graphql-ruby.org/queries/tracing.html. Please open an issue on the GraphQL-Ruby repo if you want to discuss further!") - tracer = self.new(**options) - schema_defn.tracer(tracer, silence_deprecation_warning: true) - end - end - end - - private - - # Get the transaction name based on the operation type and name if possible, or fall back to a user provided - # one. Useful for anonymous queries. - def transaction_name(query) - selected_op = query.selected_operation - txn_name = if selected_op - op_type = selected_op.operation_type - op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous" - "#{op_type}.#{op_name}" - else - "query.anonymous" - end - "GraphQL/#{txn_name}" - end - - def fallback_transaction_name(context) - context[:tracing_fallback_transaction_name] - end - - attr_reader :options - - # Different kind of schema objects have different kinds of keys: - # - # - Object types: `.authorized` - # - Union/Interface types: `.resolve_type` - # - Fields: execution - # - # So, they can all share one cache. - # - # If the key isn't present, the given block is called and the result is cached for `key`. - # - # @param ctx [GraphQL::Query::Context] - # @param key [Class, GraphQL::Field] A part of the schema - # @param trace_phase [Symbol] The stage of execution being traced (used by OpenTelementry tracing) - # @return [String] - def cached_platform_key(ctx, key, trace_phase) - cache = ctx.namespace(self.class)[:platform_key_cache] ||= {} - cache.fetch(key) { cache[key] = yield } - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/prometheus_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/prometheus_trace.rb deleted file mode 100644 index b0b7fdc803e..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/prometheus_trace.rb +++ /dev/null @@ -1,120 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # A tracer for reporting GraphQL-Ruby times to Prometheus. - # - # The PrometheusExporter server must be run with a custom type collector that extends `GraphQL::Tracing::PrometheusTracing::GraphQLCollector`. - # - # @example Adding this trace to your schema - # require 'prometheus_exporter/client' - # - # class MySchema < GraphQL::Schema - # trace_with GraphQL::Tracing::PrometheusTrace - # end - # - # @example Running a custom type collector - # # lib/graphql_collector.rb - # if defined?(PrometheusExporter::Server) - # require 'graphql/tracing' - # - # class GraphQLCollector < GraphQL::Tracing::PrometheusTrace::GraphQLCollector - # end - # end - # - # # Then run: - # # bundle exec prometheus_exporter -a lib/graphql_collector.rb - module PrometheusTrace - if defined?(PrometheusExporter::Server) - autoload :GraphQLCollector, "graphql/tracing/prometheus_trace/graphql_collector" - end - include PlatformTrace - - def initialize(client: PrometheusExporter::Client.default, keys_whitelist: ["execute_field", "execute_field_lazy"], collector_type: "graphql", **rest) - @client = client - @keys_whitelist = keys_whitelist - @collector_type = collector_type - - super(**rest) - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - 'lex' => "graphql.lex", - 'parse' => "graphql.parse", - 'validate' => "graphql.validate", - 'analyze_query' => "graphql.analyze", - 'analyze_multiplex' => "graphql.analyze", - 'execute_multiplex' => "graphql.execute", - 'execute_query' => "graphql.execute", - 'execute_query_lazy' => "graphql.execute", - }.each do |trace_method, platform_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - instrument_prometheus_execution("#{platform_key}", "#{trace_method}") { super } - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - def platform_execute_field(platform_key, &block) - instrument_prometheus_execution(platform_key, "execute_field", &block) - end - - def platform_execute_field_lazy(platform_key, &block) - instrument_prometheus_execution(platform_key, "execute_field_lazy", &block) - end - - def platform_authorized(platform_key, &block) - instrument_prometheus_execution(platform_key, "authorized", &block) - end - - def platform_authorized_lazy(platform_key, &block) - instrument_prometheus_execution(platform_key, "authorized_lazy", &block) - end - - def platform_resolve_type(platform_key, &block) - instrument_prometheus_execution(platform_key, "resolve_type", &block) - end - - def platform_resolve_type_lazy(platform_key, &block) - instrument_prometheus_execution(platform_key, "resolve_type_lazy", &block) - end - - def platform_field_key(field) - field.path - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - - private - - def instrument_prometheus_execution(platform_key, key, &block) - if @keys_whitelist.include?(key) - start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC - result = block.call - duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start - @client.send_json( - type: @collector_type, - duration: duration, - platform_key: platform_key, - key: key - ) - result - else - yield - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/prometheus_trace/graphql_collector.rb b/vendor/gems/graphql/lib/graphql/tracing/prometheus_trace/graphql_collector.rb deleted file mode 100644 index 87304af0154..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing" - -module GraphQL - module Tracing - module PrometheusTrace - class GraphQLCollector < ::PrometheusExporter::Server::TypeCollector - def initialize - @graphql_gauge = PrometheusExporter::Metric::Base.default_aggregation.new( - 'graphql_duration_seconds', - 'Time spent in GraphQL operations, in seconds' - ) - end - - def type - 'graphql' - end - - def collect(object) - default_labels = { key: object['key'], platform_key: object['platform_key'] } - custom = object['custom_labels'] - labels = custom.nil? ? default_labels : default_labels.merge(custom) - - @graphql_gauge.observe object['duration'], labels - end - - def metrics - [@graphql_gauge] - end - end - end - # Backwards-compat: - PrometheusTracing::GraphQLCollector = PrometheusTrace::GraphQLCollector - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/prometheus_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/prometheus_tracing.rb deleted file mode 100644 index a10a0bc600c..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/prometheus_tracing.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - class PrometheusTracing < PlatformTracing - DEFAULT_WHITELIST = ['execute_field', 'execute_field_lazy'].freeze - DEFAULT_COLLECTOR_TYPE = 'graphql'.freeze - - self.platform_keys = { - 'lex' => "graphql.lex", - 'parse' => "graphql.parse", - 'validate' => "graphql.validate", - 'analyze_query' => "graphql.analyze", - 'analyze_multiplex' => "graphql.analyze", - 'execute_multiplex' => "graphql.execute", - 'execute_query' => "graphql.execute", - 'execute_query_lazy' => "graphql.execute", - 'execute_field' => "graphql.execute", - 'execute_field_lazy' => "graphql.execute" - } - - def initialize(opts = {}) - @client = opts[:client] || PrometheusExporter::Client.default - @keys_whitelist = opts[:keys_whitelist] || DEFAULT_WHITELIST - @collector_type = opts[:collector_type] || DEFAULT_COLLECTOR_TYPE - - super opts - end - - def platform_trace(platform_key, key, _data, &block) - return yield unless @keys_whitelist.include?(key) - instrument_execution(platform_key, key, &block) - end - - def platform_field_key(type, field) - "#{type.graphql_name}.#{field.graphql_name}" - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - - private - - def instrument_execution(platform_key, key, &block) - start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC - result = block.call - duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start - observe platform_key, key, duration - result - end - - def observe(platform_key, key, duration) - @client.send_json( - type: @collector_type, - duration: duration, - platform_key: platform_key, - key: key - ) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/scout_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/scout_trace.rb deleted file mode 100644 index 215a7d7c52c..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/scout_trace.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # A tracer for sending GraphQL-Ruby times to Scout - # - # @example Adding this tracer to your schema - # class MySchema < GraphQL::Schema - # trace_with GraphQL::Tracing::ScoutTrace - # end - module ScoutTrace - include PlatformTrace - - INSTRUMENT_OPTS = { scope: true } - - # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_scout_transaction_name]`. - def initialize(set_transaction_name: false, **_rest) - self.class.include(ScoutApm::Tracer) - @set_transaction_name = set_transaction_name - super - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - "lex" => "lex.graphql", - "parse" => "parse.graphql", - "validate" => "validate.graphql", - "analyze_query" => "analyze.graphql", - "analyze_multiplex" => "analyze.graphql", - "execute_multiplex" => "execute.graphql", - "execute_query" => "execute.graphql", - "execute_query_lazy" => "execute.graphql", - }.each do |trace_method, platform_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - #{ - if trace_method == "execute_query" - <<-RUBY - set_this_txn_name = data[:query].context[:set_scout_transaction_name] - if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name) - ScoutApm::Transaction.rename(transaction_name(data[:query])) - end - RUBY - end - } - - self.class.instrument("GraphQL", "#{platform_key}", INSTRUMENT_OPTS) do - super - end - end - RUBY - end - # rubocop:enable Development/NoEvalCop - - def platform_execute_field(platform_key, &block) - self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block) - end - - def platform_authorized(platform_key, &block) - self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block) - end - - alias :platform_resolve_type :platform_authorized - - def platform_field_key(field) - field.path - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/scout_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/scout_tracing.rb deleted file mode 100644 index c3b20b7ee98..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/scout_tracing.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - class ScoutTracing < PlatformTracing - INSTRUMENT_OPTS = { scope: true } - - self.platform_keys = { - "lex" => "lex.graphql", - "parse" => "parse.graphql", - "validate" => "validate.graphql", - "analyze_query" => "analyze.graphql", - "analyze_multiplex" => "analyze.graphql", - "execute_multiplex" => "execute.graphql", - "execute_query" => "execute.graphql", - "execute_query_lazy" => "execute.graphql", - } - - # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_scout_transaction_name]`. - def initialize(options = {}) - self.class.include ScoutApm::Tracer - @set_transaction_name = options.fetch(:set_transaction_name, false) - super(options) - end - - def platform_trace(platform_key, key, data) - if key == "execute_query" - set_this_txn_name = data[:query].context[:set_scout_transaction_name] - if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name) - ScoutApm::Transaction.rename(transaction_name(data[:query])) - end - end - - self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS) do - yield - end - end - - def platform_field_key(type, field) - "#{type.graphql_name}.#{field.graphql_name}" - end - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/sentry_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/sentry_trace.rb deleted file mode 100644 index 5313ccc87af..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/sentry_trace.rb +++ /dev/null @@ -1,123 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # A tracer for reporting GraphQL-Ruby times to Sentry. - # @example Installing the tracer - # class MySchema < GraphQL::Schema - # trace_with GraphQL::Tracing::SentryTrace - # end - module SentryTrace - include PlatformTrace - - # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name. - # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing. - # It can also be specified per-query with `context[:set_sentry_transaction_name]`. - def initialize(set_transaction_name: false, **_rest) - @set_transaction_name = set_transaction_name - super - end - - def execute_query(**data) - set_this_txn_name = data[:query].context[:set_sentry_transaction_name] - if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name) - Sentry.configure_scope do |scope| - scope.set_transaction_name(transaction_name(data[:query])) - end - end - instrument_sentry_execution("graphql.execute", "execute_query", data) { super } - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - "lex" => "graphql.lex", - "parse" => "graphql.parse", - "validate" => "graphql.validate", - "analyze_query" => "graphql.analyze", - "analyze_multiplex" => "graphql.analyze_multiplex", - "execute_multiplex" => "graphql.execute_multiplex", - "execute_query_lazy" => "graphql.execute" - }.each do |trace_method, platform_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - instrument_sentry_execution("#{platform_key}", "#{trace_method}", data) { super } - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - def platform_execute_field(platform_key, &block) - instrument_sentry_execution(platform_key, "execute_field", &block) - end - - def platform_execute_field_lazy(platform_key, &block) - instrument_sentry_execution(platform_key, "execute_field_lazy", &block) - end - - def platform_authorized(platform_key, &block) - instrument_sentry_execution(platform_key, "authorized", &block) - end - - def platform_authorized_lazy(platform_key, &block) - instrument_sentry_execution(platform_key, "authorized_lazy", &block) - end - - def platform_resolve_type(platform_key, &block) - instrument_sentry_execution(platform_key, "resolve_type", &block) - end - - def platform_resolve_type_lazy(platform_key, &block) - instrument_sentry_execution(platform_key, "resolve_type_lazy", &block) - end - - def platform_field_key(field) - "graphql.field.#{field.path}" - end - - def platform_authorized_key(type) - "graphql.authorized.#{type.graphql_name}" - end - - def platform_resolve_type_key(type) - "graphql.resolve_type.#{type.graphql_name}" - end - - private - - def instrument_sentry_execution(platform_key, trace_method, data=nil, &block) - return yield unless Sentry.initialized? - - Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span| - result = yield - return result unless span - - span.finish - if trace_method == "execute_multiplex" && data.key?(:multiplex) - operation_names = data[:multiplex].queries.map{|q| operation_name(q) } - span.set_description(operation_names.join(", ")) - elsif trace_method == "execute_query" && data.key?(:query) - span.set_description(operation_name(data[:query])) - span.set_data('graphql.document', data[:query].query_string) - span.set_data('graphql.operation.name', data[:query].selected_operation_name) if data[:query].selected_operation_name - span.set_data('graphql.operation.type', data[:query].selected_operation.operation_type) - end - - result - end - end - - def operation_name(query) - selected_op = query.selected_operation - if selected_op - [selected_op.operation_type, selected_op.name].compact.join(' ') - else - 'GraphQL Operation' - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/statsd_trace.rb b/vendor/gems/graphql/lib/graphql/tracing/statsd_trace.rb deleted file mode 100644 index 37c8d14e618..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/statsd_trace.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_trace" - -module GraphQL - module Tracing - # A tracer for reporting GraphQL-Ruby times to Statsd. - # Passing any Statsd client that implements `.time(name) { ... }` will work. - # - # @example Installing this tracer - # # eg: - # # $statsd = Statsd.new 'localhost', 9125 - # class MySchema < GraphQL::Schema - # use GraphQL::Tracing::StatsdTrace, statsd: $statsd - # end - module StatsdTrace - include PlatformTrace - - # @param statsd [Object] A statsd client - def initialize(statsd:, **rest) - @statsd = statsd - super(**rest) - end - - # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time - - { - 'lex' => "graphql.lex", - 'parse' => "graphql.parse", - 'validate' => "graphql.validate", - 'analyze_query' => "graphql.analyze_query", - 'analyze_multiplex' => "graphql.analyze_multiplex", - 'execute_multiplex' => "graphql.execute_multiplex", - 'execute_query' => "graphql.execute_query", - 'execute_query_lazy' => "graphql.execute_query_lazy", - }.each do |trace_method, platform_key| - module_eval <<-RUBY, __FILE__, __LINE__ - def #{trace_method}(**data) - @statsd.time("#{platform_key}") do - super - end - end - RUBY - end - - # rubocop:enable Development/NoEvalCop - - def platform_execute_field(platform_key, &block) - @statsd.time(platform_key, &block) - end - - def platform_authorized(key, &block) - @statsd.time(key, &block) - end - - alias :platform_resolve_type :platform_authorized - - def platform_field_key(field) - "graphql.#{field.path}" - end - - def platform_authorized_key(type) - "graphql.authorized.#{type.graphql_name}" - end - - def platform_resolve_type_key(type) - "graphql.resolve_type.#{type.graphql_name}" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/statsd_tracing.rb b/vendor/gems/graphql/lib/graphql/tracing/statsd_tracing.rb deleted file mode 100644 index 2b1ff10c8d0..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/statsd_tracing.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing/platform_tracing" - -module GraphQL - module Tracing - class StatsdTracing < PlatformTracing - self.platform_keys = { - 'lex' => "graphql.lex", - 'parse' => "graphql.parse", - 'validate' => "graphql.validate", - 'analyze_query' => "graphql.analyze_query", - 'analyze_multiplex' => "graphql.analyze_multiplex", - 'execute_multiplex' => "graphql.execute_multiplex", - 'execute_query' => "graphql.execute_query", - 'execute_query_lazy' => "graphql.execute_query_lazy", - } - - # @param statsd [Object] A statsd client - def initialize(statsd:, **rest) - @statsd = statsd - super(**rest) - end - - def platform_trace(platform_key, key, data) - @statsd.time(platform_key) do - yield - end - end - - def platform_field_key(type, field) - "graphql.#{type.graphql_name}.#{field.graphql_name}" - end - - def platform_authorized_key(type) - "graphql.authorized.#{type.graphql_name}" - end - - def platform_resolve_type_key(type) - "graphql.resolve_type.#{type.graphql_name}" - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/tracing/trace.rb b/vendor/gems/graphql/lib/graphql/tracing/trace.rb deleted file mode 100644 index cca09640605..00000000000 --- a/vendor/gems/graphql/lib/graphql/tracing/trace.rb +++ /dev/null @@ -1,203 +0,0 @@ -# frozen_string_literal: true - -require "graphql/tracing" - -module GraphQL - module Tracing - # This is the base class for a `trace` instance whose methods are called during query execution. - # "Trace modes" are subclasses of this with custom tracing modules mixed in. - # - # A trace module may implement any of the methods on `Trace`, being sure to call `super` - # to continue any tracing hooks and call the actual runtime behavior. - # - class Trace - # @param multiplex [GraphQL::Execution::Multiplex, nil] - # @param query [GraphQL::Query, nil] - def initialize(multiplex: nil, query: nil, **_options) - @multiplex = multiplex - @query = query - end - - # The Ruby parser doesn't call this method (`graphql/c_parser` does.) - def lex(query_string:) - yield - end - - # @param query_str [String] - # @return [void] - def begin_parse(query_str); end; - # @param query_str [String] - # @return [void] - def end_parse(query_str); end; - # @param query_string [String] - # @return [void] - def parse(query_string:) - yield - end - - def validate(query:, validate:) - yield - end - - def begin_validate(query, validate) - end - - def end_validate(query, validate, errors) - end - - # @param multiplex [GraphQL::Execution::Multiplex] - # @param analyzers [Array] - # @return [void] - def begin_analyze_multiplex(multiplex, analyzers); end - # @param multiplex [GraphQL::Execution::Multiplex] - # @param analyzers [Array] - # @return [void] - def end_analyze_multiplex(multiplex, analyzers); end - # @param multiplex [GraphQL::Execution::Multiplex] - # @return [void] - def analyze_multiplex(multiplex:) - yield - end - - def analyze_query(query:) - yield - end - - # This is the first event in the tracing lifecycle. - # Every Query is technically run _inside_ a {GraphQL::Multiplex}. - # @param multiplex [GraphQL::Execution::Multiplex] - # @return [void] - def begin_execute_multiplex(multiplex); end; - - # This is the last event of the tracing lifecycle. - # @param multiplex [GraphQL::Execution::Multiplex] - # @return [void] - def end_execute_multiplex(multiplex); end; - - # This wraps an entire `.execute` call. - # @param multiplex [GraphQL::Execution::Multiplex] - # @return [void] - def execute_multiplex(multiplex:) - yield - end - - def execute_query(query:) - yield - end - - def execute_query_lazy(query:, multiplex:) - yield - end - - # GraphQL is about to resolve this field - # @param field [GraphQL::Schema::Field] - # @param object [GraphQL::Schema::Object] - # @param arguments [Hash] - # @param query [GraphQL::Query] - def begin_execute_field(field, object, arguments, query); end - # GraphQL just finished resolving this field - # @param field [GraphQL::Schema::Field] - # @param object [GraphQL::Schema::Object] - # @param arguments [Hash] - # @param query [GraphQL::Query] - # @param result [Object] - def end_execute_field(field, object, arguments, query, result); end - - def execute_field(field:, query:, ast_node:, arguments:, object:) - yield - end - - def execute_field_lazy(field:, query:, ast_node:, arguments:, object:) - yield - end - - def authorized(query:, type:, object:) - yield - end - - # A call to `.authorized?` is starting - # @param type [Class] - # @param object [Object] - # @param context [GraphQL::Query::Context] - # @return [void] - def begin_authorized(type, object, context) - end - # A call to `.authorized?` just finished - # @param type [Class] - # @param object [Object] - # @param context [GraphQL::Query::Context] - # @param authorized_result [Boolean] - # @return [void] - def end_authorized(type, object, context, authorized_result) - end - - def authorized_lazy(query:, type:, object:) - yield - end - - def resolve_type(query:, type:, object:) - yield - end - - def resolve_type_lazy(query:, type:, object:) - yield - end - - # A call to `.resolve_type` is starting - # @param type [Class, Module] - # @param value [Object] - # @param context [GraphQL::Query::Context] - # @return [void] - def begin_resolve_type(type, value, context) - end - - # A call to `.resolve_type` just ended - # @param type [Class, Module] - # @param value [Object] - # @param context [GraphQL::Query::Context] - # @param resolved_type [Class] - # @return [void] - def end_resolve_type(type, value, context, resolved_type) - end - - # A dataloader run is starting - # @param dataloader [GraphQL::Dataloader] - # @return [void] - def begin_dataloader(dataloader); end - # A dataloader run has ended - # @param dataloder [GraphQL::Dataloader] - # @return [void] - def end_dataloader(dataloader); end - - # A source with pending keys is about to fetch - # @param source [GraphQL::Dataloader::Source] - # @return [void] - def begin_dataloader_source(source); end - # A fetch call has just ended - # @param source [GraphQL::Dataloader::Source] - # @return [void] - def end_dataloader_source(source); end - - # Called when Dataloader spins up a new fiber for GraphQL execution - # @param jobs [Array<#call>] Execution steps to run - # @return [void] - def dataloader_spawn_execution_fiber(jobs); end - # Called when Dataloader spins up a new fiber for fetching data - # @param pending_sources [GraphQL::Dataloader::Source] Instances with pending keys - # @return [void] - def dataloader_spawn_source_fiber(pending_sources); end - # Called when an execution or source fiber terminates - # @return [void] - def dataloader_fiber_exit; end - - # Called when a Dataloader fiber is paused to wait for data - # @param source [GraphQL::Dataloader::Source] The Source whose `load` call initiated this `yield` - # @return [void] - def dataloader_fiber_yield(source); end - # Called when a Dataloader fiber is resumed because data has been loaded - # @param source [GraphQL::Dataloader::Source] The Source whose `load` call previously caused this Fiber to wait - # @return [void] - def dataloader_fiber_resume(source); end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/type_kinds.rb b/vendor/gems/graphql/lib/graphql/type_kinds.rb deleted file mode 100644 index 1d9612ef75b..00000000000 --- a/vendor/gems/graphql/lib/graphql/type_kinds.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # Type kinds are the basic categories which a type may belong to (`Object`, `Scalar`, `Union`...) - module TypeKinds - # These objects are singletons, eg `GraphQL::TypeKinds::UNION`, `GraphQL::TypeKinds::SCALAR`. - class TypeKind - attr_reader :name, :description - def initialize(name, abstract: false, leaf: false, fields: false, wraps: false, input: false, description: nil) - @name = name - @abstract = abstract - @fields = fields - @wraps = wraps - @input = input - @leaf = leaf - @composite = fields? || abstract? - @description = description - end - - # Does this TypeKind have multiple possible implementers? - # @deprecated Use `abstract?` instead of `resolves?`. - def resolves?; @abstract; end - # Is this TypeKind abstract? - def abstract?; @abstract; end - # Does this TypeKind have queryable fields? - def fields?; @fields; end - # Does this TypeKind modify another type? - def wraps?; @wraps; end - # Is this TypeKind a valid query input? - def input?; @input; end - def to_s; @name; end - # Is this TypeKind a primitive value? - def leaf?; @leaf; end - # Is this TypeKind composed of many values? - def composite?; @composite; end - - def scalar? - self == TypeKinds::SCALAR - end - - def object? - self == TypeKinds::OBJECT - end - - def interface? - self == TypeKinds::INTERFACE - end - - def union? - self == TypeKinds::UNION - end - - def enum? - self == TypeKinds::ENUM - end - - def input_object? - self == TypeKinds::INPUT_OBJECT - end - - def list? - self == TypeKinds::LIST - end - - def non_null? - self == TypeKinds::NON_NULL - end - end - - TYPE_KINDS = [ - SCALAR = TypeKind.new("SCALAR", input: true, leaf: true, description: 'Indicates this type is a scalar.'), - OBJECT = TypeKind.new("OBJECT", fields: true, description: 'Indicates this type is an object. `fields` and `interfaces` are valid fields.'), - INTERFACE = TypeKind.new("INTERFACE", abstract: true, fields: true, description: 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.'), - UNION = TypeKind.new("UNION", abstract: true, description: 'Indicates this type is a union. `possibleTypes` is a valid field.'), - ENUM = TypeKind.new("ENUM", input: true, leaf: true, description: 'Indicates this type is an enum. `enumValues` is a valid field.'), - INPUT_OBJECT = TypeKind.new("INPUT_OBJECT", input: true, description: 'Indicates this type is an input object. `inputFields` is a valid field.'), - LIST = TypeKind.new("LIST", wraps: true, description: 'Indicates this type is a list. `ofType` is a valid field.'), - NON_NULL = TypeKind.new("NON_NULL", wraps: true, description: 'Indicates this type is a non-null. `ofType` is a valid field.'), - ] - end -end diff --git a/vendor/gems/graphql/lib/graphql/types.rb b/vendor/gems/graphql/lib/graphql/types.rb deleted file mode 100644 index f1dcaab99a9..00000000000 --- a/vendor/gems/graphql/lib/graphql/types.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - extend Autoload - - autoload :Boolean, "graphql/types/boolean" - autoload :BigInt, "graphql/types/big_int" - autoload :Float, "graphql/types/float" - autoload :ID, "graphql/types/id" - autoload :Int, "graphql/types/int" - autoload :JSON, "graphql/types/json" - autoload :String, "graphql/types/string" - autoload :ISO8601Date, "graphql/types/iso_8601_date" - autoload :ISO8601DateTime, "graphql/types/iso_8601_date_time" - autoload :ISO8601Duration, "graphql/types/iso_8601_duration" - autoload :Relay, "graphql/types/relay" - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/big_int.rb b/vendor/gems/graphql/lib/graphql/types/big_int.rb deleted file mode 100644 index 065996a51c5..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/big_int.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - class BigInt < GraphQL::Schema::Scalar - description "Represents non-fractional signed whole numeric values. Since the value may exceed the size of a 32-bit integer, it's encoded as a string." - - def self.coerce_input(value, _ctx) - value && parse_int(value) - rescue ArgumentError - nil - end - - def self.coerce_result(value, _ctx) - value.to_i.to_s - end - - def self.parse_int(value) - value.is_a?(Numeric) ? value : Integer(value, 10) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/boolean.rb b/vendor/gems/graphql/lib/graphql/types/boolean.rb deleted file mode 100644 index df23b29935b..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/boolean.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - class Boolean < GraphQL::Schema::Scalar - description "Represents `true` or `false` values." - - def self.coerce_input(value, _ctx) - (value == true || value == false) ? value : nil - end - - def self.coerce_result(value, _ctx) - !!value - end - - default_scalar true - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/float.rb b/vendor/gems/graphql/lib/graphql/types/float.rb deleted file mode 100644 index 7551899ed5a..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/float.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - class Float < GraphQL::Schema::Scalar - description "Represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point)." - - def self.coerce_input(value, _ctx) - value.is_a?(Numeric) ? value.to_f : nil - end - - def self.coerce_result(value, _ctx) - value.to_f - end - - default_scalar true - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/id.rb b/vendor/gems/graphql/lib/graphql/types/id.rb deleted file mode 100644 index 424b52d9779..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/id.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - class ID < GraphQL::Schema::Scalar - graphql_name "ID" - description "Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"VXNlci0xMA==\"`) or integer (such as `4`) input value will be accepted as an ID." - default_scalar true - def self.coerce_result(value, _ctx) - value.is_a?(::String) ? value : value.to_s - end - - def self.coerce_input(value, _ctx) - case value - when ::String - value - when Integer - value.to_s - else - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/int.rb b/vendor/gems/graphql/lib/graphql/types/int.rb deleted file mode 100644 index e9ec3d55311..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/int.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - # @see {Types::BigInt} for handling integers outside 32-bit range. - class Int < GraphQL::Schema::Scalar - description "Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1." - - MIN = -(2**31) - MAX = (2**31) - 1 - - def self.coerce_input(value, ctx) - return if !value.is_a?(Integer) - - if value >= MIN && value <= MAX - value - else - err = GraphQL::IntegerDecodingError.new(value) - ctx.schema.type_error(err, ctx) - end - end - - def self.coerce_result(value, ctx) - value = value.to_i - if value >= MIN && value <= MAX - value - else - err = GraphQL::IntegerEncodingError.new(value, context: ctx) - ctx.schema.type_error(err, ctx) - end - end - - default_scalar true - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/iso_8601_date.rb b/vendor/gems/graphql/lib/graphql/types/iso_8601_date.rb deleted file mode 100644 index f2b45071b35..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/iso_8601_date.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - # This scalar takes `Date`s and transmits them as strings, - # using ISO 8601 format. - # - # Use it for fields or arguments as follows: - # - # field :published_at, GraphQL::Types::ISO8601Date, null: false - # - # argument :deliver_at, GraphQL::Types::ISO8601Date, null: false - # - # Alternatively, use this built-in scalar as inspiration for your - # own Date type. - class ISO8601Date < GraphQL::Schema::Scalar - description "An ISO 8601-encoded date" - specified_by_url "https://tools.ietf.org/html/rfc3339" - - # @param value [Date,Time,DateTime,String] - # @return [String] - def self.coerce_result(value, _ctx) - Date.parse(value.to_s).iso8601 - end - - # @param str_value [String, Date, DateTime, Time] - # @return [Date, nil] - def self.coerce_input(value, ctx) - if value.is_a?(::Date) - value - elsif value.is_a?(::DateTime) - value.to_date - elsif value.is_a?(::Time) - value.to_date - elsif value.nil? - nil - else - Date.iso8601(value) - end - rescue ArgumentError, TypeError - err = GraphQL::DateEncodingError.new(value) - ctx.schema.type_error(err, ctx) - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/iso_8601_date_time.rb b/vendor/gems/graphql/lib/graphql/types/iso_8601_date_time.rb deleted file mode 100644 index 73421734e49..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/iso_8601_date_time.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require 'time' - -module GraphQL - module Types - # This scalar takes `Time`s and transmits them as strings, - # using ISO 8601 format. - # - # Use it for fields or arguments as follows: - # - # field :created_at, GraphQL::Types::ISO8601DateTime, null: false - # - # argument :deliver_at, GraphQL::Types::ISO8601DateTime, null: false - # - # Alternatively, use this built-in scalar as inspiration for your - # own DateTime type. - class ISO8601DateTime < GraphQL::Schema::Scalar - description "An ISO 8601-encoded datetime" - specified_by_url "https://tools.ietf.org/html/rfc3339" - - # It's not compatible with Rails' default, - # i.e. ActiveSupport::JSON::Encoder.time_precision (3 by default) - DEFAULT_TIME_PRECISION = 0 - - # @return [Integer] - def self.time_precision - @time_precision || DEFAULT_TIME_PRECISION - end - - # @param [Integer] value - def self.time_precision=(value) - @time_precision = value - end - - # @param value [Time,Date,DateTime,String] - # @return [String] - def self.coerce_result(value, _ctx) - case value - when Date - return value.to_time.iso8601(time_precision) - when ::String - return Time.parse(value).iso8601(time_precision) - else - # Time, DateTime or compatible is given: - return value.iso8601(time_precision) - end - rescue StandardError => error - raise GraphQL::Error, "An incompatible object (#{value.class}) was given to #{self}. Make sure that only Times, Dates, DateTimes, and well-formatted Strings are used with this type. (#{error.message})" - end - - # @param str_value [String] - # @return [Time] - def self.coerce_input(str_value, _ctx) - Time.iso8601(str_value) - rescue ArgumentError, TypeError - begin - dt = Date.iso8601(str_value).to_time - # For compatibility, continue accepting dates given without times - # But without this, it would zero out given any time part of `str_value` (hours and/or minutes) - if dt.iso8601.start_with?(str_value) - dt - elsif str_value.length == 8 && str_value.match?(/\A\d{8}\Z/) - # Allow dates that are missing the "-". eg. "20220404" - dt - else - nil - end - rescue ArgumentError, TypeError - # Invalid input - nil - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/iso_8601_duration.rb b/vendor/gems/graphql/lib/graphql/types/iso_8601_duration.rb deleted file mode 100644 index 9c128900d07..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/iso_8601_duration.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - # This scalar takes `Duration`s and transmits them as strings, - # using ISO 8601 format. ActiveSupport >= 5.0 must be loaded to use - # this scalar. - # - # Use it for fields or arguments as follows: - # - # field :age, GraphQL::Types::ISO8601Duration, null: false - # - # argument :interval, GraphQL::Types::ISO8601Duration, null: false - # - # Alternatively, use this built-in scalar as inspiration for your - # own Duration type. - class ISO8601Duration < GraphQL::Schema::Scalar - description "An ISO 8601-encoded duration" - - # @return [Integer, nil] - def self.seconds_precision - # ActiveSupport::Duration precision defaults to whatever input was given - @seconds_precision - end - - # @param [Integer, nil] value - def self.seconds_precision=(value) - @seconds_precision = value - end - - # @param value [ActiveSupport::Duration, String] - # @return [String] - # @raise [GraphQL::Error] if ActiveSupport::Duration is not defined or if an incompatible object is passed - def self.coerce_result(value, _ctx) - unless defined?(ActiveSupport::Duration) - raise GraphQL::Error, "ActiveSupport >= 5.0 must be loaded to use the built-in ISO8601Duration type." - end - - begin - case value - when ActiveSupport::Duration - value.iso8601(precision: seconds_precision) - when ::String - ActiveSupport::Duration.parse(value).iso8601(precision: seconds_precision) - else - # Try calling as ActiveSupport::Duration compatible as a fallback - value.iso8601(precision: seconds_precision) - end - rescue StandardError => error - raise GraphQL::Error, "An incompatible object (#{value.class}) was given to #{self}. Make sure that only ActiveSupport::Durations and well-formatted Strings are used with this type. (#{error.message})" - end - end - - # @param value [String, ActiveSupport::Duration] - # @return [ActiveSupport::Duration, nil] - # @raise [GraphQL::Error] if ActiveSupport::Duration is not defined - # @raise [GraphQL::DurationEncodingError] if duration cannot be parsed - def self.coerce_input(value, ctx) - unless defined?(ActiveSupport::Duration) - raise GraphQL::Error, "ActiveSupport >= 5.0 must be loaded to use the built-in ISO8601Duration type." - end - - begin - if value.is_a?(ActiveSupport::Duration) - value - elsif value.nil? - nil - else - ActiveSupport::Duration.parse(value) - end - rescue ArgumentError, TypeError - err = GraphQL::DurationEncodingError.new(value) - ctx.schema.type_error(err, ctx) - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/json.rb b/vendor/gems/graphql/lib/graphql/types/json.rb deleted file mode 100644 index c33a5d3ef85..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/json.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - # An untyped JSON scalar that maps to Ruby hashes, arrays, strings, integers, floats, booleans and nils. - # This should be used judiciously because it subverts the GraphQL type system. - # - # Use it for fields or arguments as follows: - # - # field :template_parameters, GraphQL::Types::JSON, null: false - # - # argument :template_parameters, GraphQL::Types::JSON, null: false - # - class JSON < GraphQL::Schema::Scalar - description "Represents untyped JSON" - - def self.coerce_input(value, _context) - value - end - - def self.coerce_result(value, _context) - value - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay.rb b/vendor/gems/graphql/lib/graphql/types/relay.rb deleted file mode 100644 index 8f932af67d4..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -# behavior modules: -require "graphql/types/relay/connection_behaviors" -require "graphql/types/relay/edge_behaviors" -require "graphql/types/relay/node_behaviors" -require "graphql/types/relay/page_info_behaviors" -require "graphql/types/relay/has_node_field" -require "graphql/types/relay/has_nodes_field" - -# concrete classes based on the gem defaults: -require "graphql/types/relay/page_info" -require "graphql/types/relay/base_connection" -require "graphql/types/relay/base_edge" -require "graphql/types/relay/node" - -module GraphQL - module Types - # This module contains some types and fields that could support Relay conventions in GraphQL. - # - # You can use these classes out of the box if you want, but if you want to use your _own_ - # GraphQL extensions along with the features in this code, you could also - # open up the source files and copy the relevant methods and configuration into - # your own classes. - # - # For example, the provided object types extend {Types::Relay::BaseObject}, - # but you might want to: - # - # 1. Migrate the extensions from {Types::Relay::BaseObject} into _your app's_ base object - # 2. Copy {Relay::BaseConnection}, {Relay::BaseEdge}, etc into _your app_, and - # change them to extend _your_ base object. - # - # Similarly, `BaseField`'s extensions could be migrated to your app - # and `Node` could be implemented to mix in your base interface module. - module Relay - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/base_connection.rb b/vendor/gems/graphql/lib/graphql/types/relay/base_connection.rb deleted file mode 100644 index 46d60e88e2e..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/base_connection.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - # Use this to implement Relay connections, or take it as inspiration - # for Relay classes in your own app. - # - # You may wish to copy this code into your own base class, - # so you can extend your own `BaseObject` instead of `GraphQL::Schema::Object`. - # - # @example Implementation a connection and edge - # class BaseObject < GraphQL::Schema::Object; end - # - # # Given some object in your app ... - # class Types::Post < BaseObject - # end - # - # # Make a couple of base classes: - # class Types::BaseEdge < GraphQL::Types::Relay::BaseEdge; end - # class Types::BaseConnection < GraphQL::Types::Relay::BaseConnection; end - # - # # Then extend them for the object in your app - # class Types::PostEdge < Types::BaseEdge - # node_type Types::Post - # end - # - # class Types::PostConnection < Types::BaseConnection - # edge_type Types::PostEdge, - # edges_nullable: true, - # edge_nullable: true, - # node_nullable: true, - # nodes_field: true - # - # # Alternatively, you can call the class methods followed by your edge type - # # edges_nullable true - # # edge_nullable true - # # node_nullable true - # # has_nodes_field true - # # edge_type Types::PostEdge - # end - # - # @see Relay::BaseEdge for edge types - class BaseConnection < Schema::Object - include ConnectionBehaviors - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/base_edge.rb b/vendor/gems/graphql/lib/graphql/types/relay/base_edge.rb deleted file mode 100644 index 046c733a0a3..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/base_edge.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - module Relay - # A class-based definition for Relay edges. - # - # Use this as a parent class in your app, or use it as inspiration for your - # own base `Edge` class. - # - # For example, you may want to extend your own `BaseObject` instead of the - # built-in `GraphQL::Schema::Object`. - # - # @example Making a UserEdge type - # # Make a base class for your app - # class Types::BaseEdge < GraphQL::Types::Relay::BaseEdge - # end - # - # # Then extend your own base class - # class Types::UserEdge < Types::BaseEdge - # node_type(Types::User) - # end - # - # @see {Relay::BaseConnection} for connection types - class BaseEdge < GraphQL::Schema::Object - include Types::Relay::EdgeBehaviors - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/connection_behaviors.rb b/vendor/gems/graphql/lib/graphql/types/relay/connection_behaviors.rb deleted file mode 100644 index b00ebcde861..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/connection_behaviors.rb +++ /dev/null @@ -1,216 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - module ConnectionBehaviors - extend Forwardable - def_delegators :@object, :cursor_from_node, :parent - - def self.included(child_class) - child_class.extend(ClassMethods) - child_class.has_nodes_field(true) - child_class.node_nullable(true) - child_class.edges_nullable(true) - child_class.edge_nullable(true) - child_class.module_exec { - self.edge_type = nil - self.node_type = nil - self.edge_class = nil - } - child_class.default_broadcastable(nil) - add_page_info_field(child_class) - end - - module ClassMethods - def inherited(child_class) - super - child_class.has_nodes_field(has_nodes_field) - child_class.node_nullable(node_nullable) - child_class.edges_nullable(edges_nullable) - child_class.edge_nullable(edge_nullable) - child_class.edge_type = nil - child_class.node_type = nil - child_class.edge_class = nil - child_class.default_broadcastable(default_broadcastable?) - end - - def default_relay? - true - end - - def default_broadcastable? - @default_broadcastable - end - - def default_broadcastable(new_value) - @default_broadcastable = new_value - end - - # @return [Class] - attr_reader :node_type - - # @return [Class] - attr_reader :edge_class - - # Configure this connection to return `edges` and `nodes` based on `edge_type_class`. - # - # This method will use the inputs to create: - # - `edges` field - # - `nodes` field - # - description - # - # It's called when you subclass this base connection, trying to use the - # class name to set defaults. You can call it again in the class definition - # to override the default (or provide a value, if the default lookup failed). - # @param field_options [Hash] Any extra keyword arguments to pass to the `field :edges, ...` and `field :nodes, ...` configurations - def edge_type(edge_type_class, edge_class: GraphQL::Pagination::Connection::Edge, node_type: edge_type_class.node_type, nodes_field: self.has_nodes_field, node_nullable: self.node_nullable, edges_nullable: self.edges_nullable, edge_nullable: self.edge_nullable, field_options: nil) - # Set this connection's graphql name - node_type_name = node_type.graphql_name - - @node_type = node_type - @edge_type = edge_type_class - @edge_class = edge_class - - base_field_options = { - name: :edges, - type: [edge_type_class, null: edge_nullable], - null: edges_nullable, - description: "A list of edges.", - scope: false, # Assume that the connection was already scoped. - connection: false, - } - - if field_options - base_field_options.merge!(field_options) - end - - field(**base_field_options) - - define_nodes_field(node_nullable, field_options: field_options) if nodes_field - - description("The connection type for #{node_type_name}.") - end - - # Filter this list according to the way its node type would scope them - def scope_items(items, context) - node_type.scope_items(items, context) - end - - # The connection will skip auth on its nodes if the node_type is configured for that - def reauthorize_scoped_objects(new_value = nil) - if new_value.nil? - if @reauthorize_scoped_objects != nil - @reauthorize_scoped_objects - else - node_type.reauthorize_scoped_objects - end - else - @reauthorize_scoped_objects = new_value - end - end - - # Add the shortcut `nodes` field to this connection and its subclasses - def nodes_field(node_nullable: self.node_nullable, field_options: nil) - define_nodes_field(node_nullable, field_options: field_options) - end - - def authorized?(obj, ctx) - true # Let nodes be filtered out - end - - def visible?(ctx) - # if this is an abstract base class, there may be no `node_type` - node_type ? node_type.visible?(ctx) : super - end - - # Set the default `node_nullable` for this class and its child classes. (Defaults to `true`.) - # Use `node_nullable(false)` in your base class to make non-null `node` and `nodes` fields. - def node_nullable(new_value = nil) - if new_value.nil? - defined?(@node_nullable) ? @node_nullable : superclass.node_nullable - else - @node_nullable = new_value - end - end - - # Set the default `edges_nullable` for this class and its child classes. (Defaults to `true`.) - # Use `edges_nullable(false)` in your base class to make non-null `edges` fields. - def edges_nullable(new_value = nil) - if new_value.nil? - defined?(@edges_nullable) ? @edges_nullable : superclass.edges_nullable - else - @edges_nullable = new_value - end - end - - # Set the default `edge_nullable` for this class and its child classes. (Defaults to `true`.) - # Use `edge_nullable(false)` in your base class to make non-null `edge` fields. - def edge_nullable(new_value = nil) - if new_value.nil? - defined?(@edge_nullable) ? @edge_nullable : superclass.edge_nullable - else - @edge_nullable = new_value - end - end - - # Set the default `nodes_field` for this class and its child classes. (Defaults to `true`.) - # Use `nodes_field(false)` in your base class to prevent adding of a nodes field. - def has_nodes_field(new_value = nil) - if new_value.nil? - defined?(@nodes_field) ? @nodes_field : superclass.has_nodes_field - else - @nodes_field = new_value - end - end - - protected - - attr_writer :edge_type, :node_type, :edge_class - - private - - def define_nodes_field(nullable, field_options: nil) - base_field_options = { - name: :nodes, - type: [@node_type, null: nullable], - null: nullable, - description: "A list of nodes.", - connection: false, - # Assume that the connection was scoped before this step: - scope: false, - } - if field_options - base_field_options.merge!(field_options) - end - field(**base_field_options) - end - end - - class << self - def add_page_info_field(obj_type) - obj_type.field :page_info, GraphQL::Types::Relay::PageInfo, null: false, description: "Information to aid in pagination." - end - end - - def edges - # Assume that whatever authorization needed to happen - # already happened at the connection level. - current_runtime_state = Fiber[:__graphql_runtime_info] - query_runtime_state = current_runtime_state[context.query] - query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items? - @object.edges - end - - def nodes - # Assume that whatever authorization needed to happen - # already happened at the connection level. - current_runtime_state = Fiber[:__graphql_runtime_info] - query_runtime_state = current_runtime_state[context.query] - query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items? - @object.nodes - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/edge_behaviors.rb b/vendor/gems/graphql/lib/graphql/types/relay/edge_behaviors.rb deleted file mode 100644 index 22261757b22..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/edge_behaviors.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - module EdgeBehaviors - def self.included(child_class) - child_class.description("An edge in a connection.") - child_class.field(:cursor, String, null: false, description: "A cursor for use in pagination.") - child_class.extend(ClassMethods) - child_class.class_exec { self.node_type = nil } - child_class.node_nullable(true) - child_class.default_broadcastable(nil) - end - - def node - current_runtime_state = Fiber[:__graphql_runtime_info] - query_runtime_state = current_runtime_state[context.query] - query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items? - @object.node - end - - module ClassMethods - def inherited(child_class) - super - child_class.node_type = nil - child_class.node_nullable = nil - child_class.default_broadcastable(default_broadcastable?) - end - - def default_relay? - true - end - - def default_broadcastable? - @default_broadcastable - end - - def default_broadcastable(new_value) - @default_broadcastable = new_value - end - - # Get or set the Object type that this edge wraps. - # - # @param node_type [Class] A `Schema::Object` subclass - # @param null [Boolean] - # @param field_options [Hash] Any extra arguments to pass to the `field :node` configuration - def node_type(node_type = nil, null: self.node_nullable, field_options: nil) - if node_type - @node_type = node_type - # Add a default `node` field - base_field_options = { - name: :node, - type: node_type, - null: null, - description: "The item at the end of the edge.", - connection: false, - } - if field_options - base_field_options.merge!(field_options) - end - field(**base_field_options) - end - @node_type - end - - def authorized?(obj, ctx) - true - end - - def visible?(ctx) - node_type.visible?(ctx) - end - - # Set the default `node_nullable` for this class and its child classes. (Defaults to `true`.) - # Use `node_nullable(false)` in your base class to make non-null `node` field. - def node_nullable(new_value = nil) - if new_value.nil? - @node_nullable != nil ? @node_nullable : superclass.node_nullable - else - @node_nullable = new_value - end - end - - protected - - attr_writer :node_type, :node_nullable - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/has_node_field.rb b/vendor/gems/graphql/lib/graphql/types/relay/has_node_field.rb deleted file mode 100644 index 21122a243e2..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/has_node_field.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - # Include this module to your root Query type to get a Relay-compliant `node(id: ID!): Node` field that uses the schema's `object_from_id` hook. - module HasNodeField - def self.included(child_class) - child_class.field(**field_options, &field_block) - end - - class << self - def field_options - { - name: "node", - type: GraphQL::Types::Relay::Node, - null: true, - description: "Fetches an object given its ID.", - relay_node_field: true, - } - end - - def field_block - Proc.new { - argument :id, "ID!", - description: "ID of the object." - - def resolve(obj, args, ctx) - ctx.schema.object_from_id(args[:id], ctx) - end - - def resolve_field(obj, args, ctx) - resolve(obj, args, ctx) - end - } - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/has_nodes_field.rb b/vendor/gems/graphql/lib/graphql/types/relay/has_nodes_field.rb deleted file mode 100644 index 43b7eac64f1..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/has_nodes_field.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - # Include this module to your root Query type to get a Relay-style `nodes(id: ID!): [Node]` field that uses the schema's `object_from_id` hook. - module HasNodesField - def self.included(child_class) - child_class.field(**field_options, &field_block) - end - - class << self - def field_options - { - name: "nodes", - type: [GraphQL::Types::Relay::Node, null: true], - null: false, - description: "Fetches a list of objects given a list of IDs.", - relay_nodes_field: true, - } - end - - def field_block - Proc.new { - argument :ids, "[ID!]!", - description: "IDs of the objects." - - def resolve(obj, args, ctx) - args[:ids].map { |id| ctx.schema.object_from_id(id, ctx) } - end - - def resolve_field(obj, args, ctx) - resolve(obj, args, ctx) - end - } - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/node.rb b/vendor/gems/graphql/lib/graphql/types/relay/node.rb deleted file mode 100644 index 07c0f072212..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/node.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - # This can be used for Relay's `Node` interface, - # or you can take it as inspiration for your own implementation - # of the `Node` interface. - module Node - include GraphQL::Schema::Interface - include Types::Relay::NodeBehaviors - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/node_behaviors.rb b/vendor/gems/graphql/lib/graphql/types/relay/node_behaviors.rb deleted file mode 100644 index 3b3e2770a97..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/node_behaviors.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - module Relay - module NodeBehaviors - def self.included(child_module) - child_module.extend(ClassMethods) - child_module.description("An object with an ID.") - child_module.field(:id, ID, null: false, description: "ID of the object.", resolver_method: :default_global_id) - end - - def default_global_id - context.schema.id_from_object(object, self.class, context) - end - - module ClassMethods - def default_relay? - true - end - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/page_info.rb b/vendor/gems/graphql/lib/graphql/types/relay/page_info.rb deleted file mode 100644 index b8175d286eb..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/page_info.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - module Relay - # The return type of a connection's `pageInfo` field - class PageInfo < GraphQL::Schema::Object - include PageInfoBehaviors - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/relay/page_info_behaviors.rb b/vendor/gems/graphql/lib/graphql/types/relay/page_info_behaviors.rb deleted file mode 100644 index 46e782567d0..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/relay/page_info_behaviors.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true -module GraphQL - module Types - module Relay - module PageInfoBehaviors - def self.included(child_class) - child_class.extend ClassMethods - child_class.description "Information about pagination in a connection." - child_class.field :has_next_page, Boolean, null: false, - description: "When paginating forwards, are there more items?" - - child_class.field :has_previous_page, Boolean, null: false, - description: "When paginating backwards, are there more items?" - - child_class.field :start_cursor, String, null: true, - description: "When paginating backwards, the cursor to continue." - - child_class.field :end_cursor, String, null: true, - description: "When paginating forwards, the cursor to continue." - end - end - - module ClassMethods - def default_relay? - true - end - - def default_broadcastable? - true - end - end - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/types/string.rb b/vendor/gems/graphql/lib/graphql/types/string.rb deleted file mode 100644 index 3158b971d40..00000000000 --- a/vendor/gems/graphql/lib/graphql/types/string.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module GraphQL - module Types - class String < GraphQL::Schema::Scalar - description "Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text." - - def self.coerce_result(value, ctx) - str = value.to_s - if str.encoding == Encoding::UTF_8 || str.ascii_only? - str - elsif str.frozen? - str.encode(Encoding::UTF_8) - else - str.encode!(Encoding::UTF_8) - end - rescue EncodingError - err = GraphQL::StringEncodingError.new(str, context: ctx) - ctx.schema.type_error(err, ctx) - end - - def self.coerce_input(value, _ctx) - value.is_a?(::String) ? value : nil - end - - default_scalar true - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/unauthorized_enum_value_error.rb b/vendor/gems/graphql/lib/graphql/unauthorized_enum_value_error.rb deleted file mode 100644 index f3bfc2acbf1..00000000000 --- a/vendor/gems/graphql/lib/graphql/unauthorized_enum_value_error.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class UnauthorizedEnumValueError < GraphQL::UnauthorizedError - # @return [GraphQL::Schema::EnumValue] The value whose `#authorized?` check returned false - attr_accessor :enum_value - - def initialize(type:, context:, enum_value:) - @enum_value = enum_value - message ||= "#{enum_value.path} failed authorization" - super(message, object: enum_value.value, type: type, context: context) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/unauthorized_error.rb b/vendor/gems/graphql/lib/graphql/unauthorized_error.rb deleted file mode 100644 index 81fdc344781..00000000000 --- a/vendor/gems/graphql/lib/graphql/unauthorized_error.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # When an `authorized?` hook returns false, this error is used to communicate the failure. - # It's passed to {Schema.unauthorized_object}. - # - # Alternatively, custom code in `authorized?` may raise this error. It will be routed the same way. - class UnauthorizedError < GraphQL::Error - # @return [Object] the application object that failed the authorization check - attr_reader :object - - # @return [Class] the GraphQL object type whose `.authorized?` method was called (and returned false) - attr_reader :type - - # @return [GraphQL::Query::Context] the context for the current query - attr_accessor :context - - def initialize(message = nil, object: nil, type: nil, context: nil) - if message.nil? && object.nil? && type.nil? - raise ArgumentError, "#{self.class.name} requires either a message or keywords" - end - - @object = object - @type = type - @context = context - message ||= "An instance of #{object.class} failed #{type.graphql_name}'s authorization check" - super(message) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/unauthorized_field_error.rb b/vendor/gems/graphql/lib/graphql/unauthorized_field_error.rb deleted file mode 100644 index 5d5c8bdb071..00000000000 --- a/vendor/gems/graphql/lib/graphql/unauthorized_field_error.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -module GraphQL - class UnauthorizedFieldError < GraphQL::UnauthorizedError - # @return [Field] the field that failed the authorization check - attr_accessor :field - - def initialize(message = nil, object: nil, type: nil, context: nil, field: nil) - if message.nil? && [field, type].any?(&:nil?) - raise ArgumentError, "#{self.class.name} requires either a message or keywords" - end - - @field = field - message ||= begin - if object - "An instance of #{object.class} failed #{type.name}'s authorization check on field #{field.name}" - else - "Failed #{type.name}'s authorization check on field #{field.name}" - end - end - super(message, object: object, type: type, context: context) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/unresolved_type_error.rb b/vendor/gems/graphql/lib/graphql/unresolved_type_error.rb deleted file mode 100644 index 3b8e95025b5..00000000000 --- a/vendor/gems/graphql/lib/graphql/unresolved_type_error.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true -module GraphQL - # Error raised when the value provided for a field - # can't be resolved to one of the possible types for the field. - class UnresolvedTypeError < GraphQL::RuntimeTypeError - # @return [Object] The runtime value which couldn't be successfully resolved with `resolve_type` - attr_reader :value - - # @return [GraphQL::Field] The field whose value couldn't be resolved (`field.type` is type which couldn't be resolved) - attr_reader :field - - # @return [GraphQL::BaseType] The owner of `field` - attr_reader :parent_type - - # @return [Object] The return of {Schema#resolve_type} for `value` - attr_reader :resolved_type - - # @return [Array] The allowed options for resolving `value` to `field.type` - attr_reader :possible_types - - def initialize(value, field, parent_type, resolved_type, possible_types) - @value = value - @field = field - @parent_type = parent_type - @resolved_type = resolved_type - @possible_types = possible_types - message = "The value from \"#{field.graphql_name}\" on \"#{parent_type.graphql_name}\" could not be resolved to \"#{field.type.to_type_signature}\". " \ - "(Received: `#{resolved_type.inspect}`, Expected: [#{possible_types.map(&:graphql_name).join(", ")}]) " \ - "Make sure you have defined a `resolve_type` proc on your schema and that value `#{value.inspect}` " \ - "gets resolved to a valid type. You may need to add your type to `orphan_types` if it implements an " \ - "interface but isn't a return type of any other field." - super(message) - end - end -end diff --git a/vendor/gems/graphql/lib/graphql/version.rb b/vendor/gems/graphql/lib/graphql/version.rb deleted file mode 100644 index 740a7da8d81..00000000000 --- a/vendor/gems/graphql/lib/graphql/version.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -module GraphQL - VERSION = "2.4.11" -end diff --git a/vendor/gems/graphql/readme.md b/vendor/gems/graphql/readme.md deleted file mode 100644 index b74b2490094..00000000000 --- a/vendor/gems/graphql/readme.md +++ /dev/null @@ -1,59 +0,0 @@ -# graphql graphql-ruby - -[![CI Suite](https://github.com/rmosolgo/graphql-ruby/actions/workflows/ci.yaml/badge.svg)](https://github.com/rmosolgo/graphql-ruby/actions/workflows/ci.yaml) -[![Gem Version](https://badge.fury.io/rb/graphql.svg)](https://rubygems.org/gems/graphql) - -A Ruby implementation of [GraphQL](https://graphql.org/). - -- [Website](https://graphql-ruby.org/) -- [API Documentation](https://www.rubydoc.info/github/rmosolgo/graphql-ruby) -- [Newsletter](https://buttondown.email/graphql-ruby) - -## Installation - -Install from RubyGems by adding it to your `Gemfile`, then bundling. - -```ruby -# Gemfile -gem 'graphql' -``` - -``` -$ bundle install -``` - -## Getting Started - -``` -$ rails generate graphql:install -``` - -After this, you may need to run `bundle install` again, as by default graphiql-rails is added on installation. - -Or, see ["Getting Started"](https://graphql-ruby.org/getting_started.html). - -## Upgrade - -I also sell [GraphQL::Pro](https://graphql.pro) which provides several features on top of the GraphQL runtime, including: - -- [Persisted queries](https://graphql-ruby.org/operation_store/overview) -- [API versioning](https://graphql-ruby.org/changesets/overview) -- [Streaming payloads](https://graphql-ruby.org/defer/overview) -- [Server-side caching](https://graphql-ruby.org/object_cache/overview) -- [Rate limiters](https://graphql-ruby.org/limiters/overview) -- Subscriptions backends for [Pusher](https://graphql-ruby.org/subscriptions/pusher_implementation) and [Ably](https://graphql-ruby.org/subscriptions/ably_implementation) -- Authorization plugins for [Pundit](https://graphql-ruby.org/authorization/pundit_integration) and [CanCan](https://graphql-ruby.org/authorization/can_can_integration) - -Besides that, Pro customers get email support and an opportunity to support graphql-ruby's development! - -## Goals - -- Implement the GraphQL spec & support a Relay front end -- Provide idiomatic, plain-Ruby API with similarities to reference implementation where possible -- Support Ruby on Rails and Relay - -## Getting Involved - -- __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9). -- __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues). -- __Start hacking__ with the [Development guide](https://graphql-ruby.org/development). diff --git a/vendor/gems/graphql/spec/dummy/.gitignore b/vendor/gems/graphql/spec/dummy/.gitignore deleted file mode 100644 index b1234fece4d..00000000000 --- a/vendor/gems/graphql/spec/dummy/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# See https://help.github.com/articles/ignoring-files for more about ignoring files. -# -# If you find yourself ignoring temporary files generated by your text editor -# or operating system, you probably want to add a global ignore instead: -# git config --global core.excludesfile '~/.gitignore_global' - -# Ignore bundler config. -/.bundle - -# Ignore all logfiles and tempfiles. -/log/* -/tmp/* - -/node_modules -/yarn-error.log - -.byebug_history diff --git a/vendor/gems/graphql/spec/dummy/Rakefile b/vendor/gems/graphql/spec/dummy/Rakefile deleted file mode 100644 index 84f2bc394b9..00000000000 --- a/vendor/gems/graphql/spec/dummy/Rakefile +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - -require_relative 'config/application' - -Rails.application.load_tasks diff --git a/vendor/gems/graphql/spec/dummy/app/assets/config/manifest.js b/vendor/gems/graphql/spec/dummy/app/assets/config/manifest.js deleted file mode 100644 index 48e139d2dea..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/assets/config/manifest.js +++ /dev/null @@ -1 +0,0 @@ -//= link_directory ../javascripts .js diff --git a/vendor/gems/graphql/spec/dummy/app/assets/javascripts/application.js b/vendor/gems/graphql/spec/dummy/app/assets/javascripts/application.js deleted file mode 100644 index 470abfb253a..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/assets/javascripts/application.js +++ /dev/null @@ -1,89 +0,0 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's -// vendor/assets/javascripts directory can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. JavaScript code in this file should be added after the last require_* statement. -// -// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details -// about supported directives. -// -//= require rails-ujs -// Action Cable provides the framework to deal with WebSockets in Rails. -// You can generate new channels where WebSocket features live using the `rails generate channel` command. -// -//= require action_cable -//= require_self - -(function() { - this.App || (this.App = {}); - - App.cable = ActionCable.createConsumer(); - - App.subscribe = function(options) { - var query = options.query - var variables = options.variables - var receivedCallback = options.received - // Unique-ish - var uuid = Math.round(Date.now() + Math.random() * 100000).toString(16) - var subscription = { - _subscribed: false, - subscription: App.cable.subscriptions.create({ - channel: "GraphqlChannel", - id: uuid, - }, { - connected: function() { - this.perform("execute", { - query: query, - variables: variables, - }) - console.log("Connected", query, variables) - }, - received: function(payload) { - subscription._subscribed = true - App.logToBody("ActionCable received: " + JSON.stringify(payload)) - if (payload.result) { - receivedCallback(payload) - } - if (!payload.more) { - this.unsubscribe() - App.logToBody("Remaining ActionCable subscriptions: " + App.cable.subscriptions.subscriptions.length) - } - } - } - ), - trigger: function(options) { - if (!subscription._subscribed) { - options.retries ||= 0 - options.retries++ - if (options.retries > 5) { - throw new Error("Retried 5 times, failed to trigger: " + JSON.stringify(options)) - } else { - App.logToBody("Retrying trigger " + options.retries + " : " + JSON.stringify(options)) - setTimeout(function() { - subscription.trigger(options) - }, 500) - } - } else { - App.logToBody("Triggering " + JSON.stringify(options)) - this.subscription.perform("make_trigger", options) - } - }, - unsubscribe: function() { - this.subscription.unsubscribe() - }, - } - return subscription - } - - // Add `text` to the HTML body, for debugging - App.logToBody = function(text) { - var bodyLog = document.getElementById("body-log") - var logEntry = document.createElement("p") - logEntry.innerText = text - bodyLog.appendChild(logEntry) - bodyLog.append("\n") - } -}).call(this); diff --git a/vendor/gems/graphql/spec/dummy/app/channels/application_cable/channel.rb b/vendor/gems/graphql/spec/dummy/app/channels/application_cable/channel.rb deleted file mode 100644 index 51e3e936bdd..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/channels/application_cable/channel.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -module ApplicationCable - class Channel < ActionCable::Channel::Base - end -end diff --git a/vendor/gems/graphql/spec/dummy/app/channels/application_cable/connection.rb b/vendor/gems/graphql/spec/dummy/app/channels/application_cable/connection.rb deleted file mode 100644 index fa70319da2d..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/channels/application_cable/connection.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -module ApplicationCable - class Connection < ActionCable::Connection::Base - end -end diff --git a/vendor/gems/graphql/spec/dummy/app/channels/graphql_channel.rb b/vendor/gems/graphql/spec/dummy/app/channels/graphql_channel.rb deleted file mode 100644 index 03a664fcea8..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/channels/graphql_channel.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true -class GraphqlChannel < ActionCable::Channel::Base - class QueryType < GraphQL::Schema::Object - field :value, Integer, null: false - def value - 3 - end - end - - class PayloadType < GraphQL::Schema::Object - field :value, Integer, null: false - end - - class CounterIncremented < GraphQL::Schema::Subscription - def self.reset_call_count - @@call_count = 0 - end - - reset_call_count - - field :new_value, Integer, null: false - - def update - if object - if object.value == "server-unsubscribe" - unsubscribe - elsif object.value == "server-unsubscribe-with-message" - unsubscribe({ new_value: 9999 }) - end - end - result = { - new_value: @@call_count += 1 - } - puts " -> CounterIncremented#update: #{result}" - result - end - end - - class SubscriptionType < GraphQL::Schema::Object - field :payload, PayloadType, null: false do - argument :id, ID - end - - field :counter_incremented, subscription: CounterIncremented - end - - # Wacky behavior around the number 4 - # so we can confirm it's used by the UI - module CustomSerializer - def self.load(value) - if value == "4x" - ExamplePayload.new(400) - else - GraphQL::Subscriptions::Serialize.load(value) - end - end - - def self.dump(obj) - if obj.is_a?(ExamplePayload) && obj.value == 4 - "4x" - else - GraphQL::Subscriptions::Serialize.dump(obj) - end - end - end - - class GraphQLSchema < GraphQL::Schema - query(QueryType) - subscription(SubscriptionType) - use GraphQL::Subscriptions::ActionCableSubscriptions, - serializer: CustomSerializer, - broadcast: true, - default_broadcastable: true - end - - def subscribed - @subscription_ids = [] - end - - def execute(data) - query = data["query"] - variables = data["variables"] || {} - operation_name = data["operationName"] - context = { - # Make sure the channel is in the context - channel: self, - } - - puts "[GraphQLSchema.execute] #{query} || #{variables}" - result = GraphQLSchema.execute( - query: query, - context: context, - variables: variables, - operation_name: operation_name - ) - - payload = { - result: result.to_h, - more: result.subscription?, - } - - # Track the subscription here so we can remove it - # on unsubscribe. - if result.context[:subscription_id] - @subscription_ids << result.context[:subscription_id] - end - puts " -> [transmit(#{result.context[:subscription_id]})] #{payload.inspect}" - transmit(payload) - end - - def make_trigger(data) - field = data["field"] - args = data["arguments"] - value = data["value"] - value = value && ExamplePayload.new(value) - puts "[make_trigger] #{[field, args, value]}" - GraphQLSchema.subscriptions.trigger(field, args, value) - end - - def unsubscribed - @subscription_ids.each { |sid| - puts "[delete_subscription] #{sid}" - GraphQLSchema.subscriptions.delete_subscription(sid) - } - end - - # This is to make sure that GlobalID is used to load and dump this object - class ExamplePayload - include GlobalID::Identification - def initialize(value) - @value = value - end - - def self.find(value) - self.new(value) - end - - attr_reader :value - alias :id :value - end -end diff --git a/vendor/gems/graphql/spec/dummy/app/controllers/application_controller.rb b/vendor/gems/graphql/spec/dummy/app/controllers/application_controller.rb deleted file mode 100644 index 6d369f83205..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/controllers/application_controller.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -class ApplicationController < ActionController::Base - protect_from_forgery with: :exception -end diff --git a/vendor/gems/graphql/spec/dummy/app/controllers/pages_controller.rb b/vendor/gems/graphql/spec/dummy/app/controllers/pages_controller.rb deleted file mode 100644 index c22b854af87..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/controllers/pages_controller.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -class PagesController < ApplicationController - def show - end -end diff --git a/vendor/gems/graphql/spec/dummy/app/graphql/dummy_schema.rb b/vendor/gems/graphql/spec/dummy/app/graphql/dummy_schema.rb deleted file mode 100644 index 249eef72f9e..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/graphql/dummy_schema.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class DummySchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :str, String, fallback_value: "hello" - end - - query(Query) - use GraphQL::Tracing::DetailedTrace, memory: true - - def self.detailed_trace?(query) - query.context[:profile] - end -end diff --git a/vendor/gems/graphql/spec/dummy/app/graphql/not_installed_schema.rb b/vendor/gems/graphql/spec/dummy/app/graphql/not_installed_schema.rb deleted file mode 100644 index 24e7beebdef..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/graphql/not_installed_schema.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class NotInstalledSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :str, String, fallback_value: "hello" - end - - query(Query) -end diff --git a/vendor/gems/graphql/spec/dummy/app/helpers/application_helper.rb b/vendor/gems/graphql/spec/dummy/app/helpers/application_helper.rb deleted file mode 100644 index 71249b93b23..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/helpers/application_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true -module ApplicationHelper -end diff --git a/vendor/gems/graphql/spec/dummy/app/jobs/application_job.rb b/vendor/gems/graphql/spec/dummy/app/jobs/application_job.rb deleted file mode 100644 index 32fe70b8e46..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/jobs/application_job.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true -class ApplicationJob < ActiveJob::Base -end diff --git a/vendor/gems/graphql/spec/dummy/app/views/layouts/application.html.erb b/vendor/gems/graphql/spec/dummy/app/views/layouts/application.html.erb deleted file mode 100644 index 465617889dc..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/views/layouts/application.html.erb +++ /dev/null @@ -1,12 +0,0 @@ - - - - Dummy - <%= csrf_meta_tags %> - <%= javascript_include_tag 'application' %> - - - - <%= yield %> - - diff --git a/vendor/gems/graphql/spec/dummy/app/views/pages/show.html b/vendor/gems/graphql/spec/dummy/app/views/pages/show.html deleted file mode 100644 index 67c80fb909c..00000000000 --- a/vendor/gems/graphql/spec/dummy/app/views/pages/show.html +++ /dev/null @@ -1,143 +0,0 @@ -

    ActionCable Test Page

    - - -

    ActionCable updates:

    -
      -
    - -
      -
    - - - - -
    - -

    Fingerprint test

    - - -
      -
    - - - - - -
    - - -
      -
    - - - -
    -
    - diff --git a/vendor/gems/graphql/spec/dummy/bin/bundle b/vendor/gems/graphql/spec/dummy/bin/bundle deleted file mode 100755 index 86f34665565..00000000000 --- a/vendor/gems/graphql/spec/dummy/bin/bundle +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -load Gem.bin_path('bundler', 'bundle') diff --git a/vendor/gems/graphql/spec/dummy/bin/rails b/vendor/gems/graphql/spec/dummy/bin/rails deleted file mode 100755 index dd027b406c9..00000000000 --- a/vendor/gems/graphql/spec/dummy/bin/rails +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' diff --git a/vendor/gems/graphql/spec/dummy/bin/rake b/vendor/gems/graphql/spec/dummy/bin/rake deleted file mode 100755 index 609af74703c..00000000000 --- a/vendor/gems/graphql/spec/dummy/bin/rake +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/vendor/gems/graphql/spec/dummy/bin/setup b/vendor/gems/graphql/spec/dummy/bin/setup deleted file mode 100755 index e9cf9f3b155..00000000000 --- a/vendor/gems/graphql/spec/dummy/bin/setup +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -require 'pathname' -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a starting point to setup your application. - # Add necessary setup steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') - - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/vendor/gems/graphql/spec/dummy/bin/update b/vendor/gems/graphql/spec/dummy/bin/update deleted file mode 100755 index f6dba31750e..00000000000 --- a/vendor/gems/graphql/spec/dummy/bin/update +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -require 'pathname' -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a way to update your development environment automatically. - # Add necessary update steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/vendor/gems/graphql/spec/dummy/bin/yarn b/vendor/gems/graphql/spec/dummy/bin/yarn deleted file mode 100755 index 3ca55b44835..00000000000 --- a/vendor/gems/graphql/spec/dummy/bin/yarn +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -VENDOR_PATH = File.expand_path('..', __dir__) -Dir.chdir(VENDOR_PATH) do - begin - exec "yarnpkg #{ARGV.join(" ")}" - rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" - exit 1 - end -end diff --git a/vendor/gems/graphql/spec/dummy/config.ru b/vendor/gems/graphql/spec/dummy/config.ru deleted file mode 100644 index 7eae2644f19..00000000000 --- a/vendor/gems/graphql/spec/dummy/config.ru +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# This file is used by Rack-based servers to start the application. - -require_relative 'config/environment' - -run Rails.application diff --git a/vendor/gems/graphql/spec/dummy/config/application.rb b/vendor/gems/graphql/spec/dummy/config/application.rb deleted file mode 100644 index 147ae683d6e..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/application.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -require_relative 'boot' - -require "rails" -# Pick the frameworks you want: -require "active_model/railtie" -require "active_job/railtie" -require "action_controller/railtie" -require "action_view/railtie" -require "action_cable/engine" -require "sprockets/railtie" -require "rails/test_unit/railtie" - -# Require the gems listed in Gemfile, including any gems -# you've limited to :test, :development, or :production. -Bundler.require(*Rails.groups) - -module Dummy - class Application < Rails::Application - # Don't generate system test files. - config.generators.system_tests = nil - end -end diff --git a/vendor/gems/graphql/spec/dummy/config/boot.rb b/vendor/gems/graphql/spec/dummy/config/boot.rb deleted file mode 100644 index a400808e01a..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/boot.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) - -require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap/setup' diff --git a/vendor/gems/graphql/spec/dummy/config/cable.yml b/vendor/gems/graphql/spec/dummy/config/cable.yml deleted file mode 100644 index d3dfabd577a..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/cable.yml +++ /dev/null @@ -1,10 +0,0 @@ -development: - adapter: async - -test: - adapter: async - -production: - adapter: redis - url: redis://localhost:6379/1 - channel_prefix: dummy_production diff --git a/vendor/gems/graphql/spec/dummy/config/database.yml b/vendor/gems/graphql/spec/dummy/config/database.yml deleted file mode 100644 index 01bebb5087c..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/database.yml +++ /dev/null @@ -1,32 +0,0 @@ -# SQLite. Versions 3.8.0 and up are supported. -# gem install sqlite3 -# -# Ensure the SQLite 3 gem is defined in your Gemfile -# gem "sqlite3" -# -default: &default - adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 - -development: - <<: *default - database: storage/development.sqlite3 - -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: - <<: *default - database: storage/test.sqlite3 - - -# SQLite3 write its data on the local filesystem, as such it requires -# persistent disks. If you are deploying to a managed service, you should -# make sure it provides disk persistence, as many don't. -# -# Similarly, if you deploy your application as a Docker container, you must -# ensure the database is located in a persisted volume. -production: - <<: *default - # database: path/to/persistent/storage/production.sqlite3 diff --git a/vendor/gems/graphql/spec/dummy/config/environment.rb b/vendor/gems/graphql/spec/dummy/config/environment.rb deleted file mode 100644 index 12ea62f8869..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/environment.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# Load the Rails application. -require_relative 'application' - -# Initialize the Rails application. -Rails.application.initialize! diff --git a/vendor/gems/graphql/spec/dummy/config/environments/development.rb b/vendor/gems/graphql/spec/dummy/config/environments/development.rb deleted file mode 100644 index d182e03e624..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/environments/development.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false - - # Do not eager load code on boot. - config.eager_load = false - - # Show full error reports. - config.consider_all_requests_local = true - - # Enable/disable caching. By default caching is disabled. - if Rails.root.join('tmp/caching-dev.txt').exist? - config.action_controller.perform_caching = true - - config.cache_store = :memory_store - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" - } - else - config.action_controller.perform_caching = false - - config.cache_store = :null_store - end - - # Print deprecation notices to the Rails logger. - config.active_support.deprecation = :log - - - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true - - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker -end diff --git a/vendor/gems/graphql/spec/dummy/config/environments/production.rb b/vendor/gems/graphql/spec/dummy/config/environments/production.rb deleted file mode 100644 index 41128d758ad..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/environments/production.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # Code is not reloaded between requests. - config.cache_classes = true - - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. - config.eager_load = true - - # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Attempt to read encrypted secrets from `config/secrets.yml.enc`. - # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or - # `config/secrets.yml.key`. - config.read_encrypted_secrets = true - - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - - - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' - - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - - # Mount Action Cable outside main process or domain - # config.action_cable.mount_path = nil - # config.action_cable.url = 'wss://example.com/cable' - # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] - - # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true - - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug - - # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] - - # Use a different cache store in production. - # config.cache_store = :mem_cache_store - - # Use a real queuing backend for Active Job (and separate queues per environment) - # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "dummy_#{Rails.env}" - - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = true - - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - - # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) - end -end diff --git a/vendor/gems/graphql/spec/dummy/config/environments/test.rb b/vendor/gems/graphql/spec/dummy/config/environments/test.rb deleted file mode 100644 index 42fb252e836..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/environments/test.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! - config.cache_classes = true - - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false - - # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.enabled = true - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" - } - - # Show full error reports and disable caching. - config.consider_all_requests_local = true - config.action_controller.perform_caching = false - - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false - - # Disable request forgery protection in test environment. - config.action_controller.allow_forgery_protection = false - - # Print deprecation notices to the stderr. - config.active_support.deprecation = :stderr - - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true -end diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/application_controller_renderer.rb b/vendor/gems/graphql/spec/dummy/config/initializers/application_controller_renderer.rb deleted file mode 100644 index f4556db3998..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/application_controller_renderer.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# ActiveSupport::Reloader.to_prepare do -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) -# end diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/backtrace_silencers.rb b/vendor/gems/graphql/spec/dummy/config/initializers/backtrace_silencers.rb deleted file mode 100644 index d0f0d3b5df2..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/backtrace_silencers.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } - -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/cookies_serializer.rb b/vendor/gems/graphql/spec/dummy/config/initializers/cookies_serializer.rb deleted file mode 100644 index 2a729595920..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/cookies_serializer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# Specify a serializer for the signed and encrypted cookie jars. -# Valid options are :json, :marshal, and :hybrid. -Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/filter_parameter_logging.rb b/vendor/gems/graphql/spec/dummy/config/initializers/filter_parameter_logging.rb deleted file mode 100644 index b7fe1231f09..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/filter_parameter_logging.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/graphql_dashboard.rb b/vendor/gems/graphql/spec/dummy/config/initializers/graphql_dashboard.rb deleted file mode 100644 index 8081bb79f20..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/graphql_dashboard.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -ActiveSupport.on_load(:graphql_dashboard_application_controller) do - def self.hook_was_called? - true - end -end diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/inflections.rb b/vendor/gems/graphql/spec/dummy/config/initializers/inflections.rb deleted file mode 100644 index aa7435fbc99..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/inflections.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# Add new inflection rules using the following format. Inflections -# are locale specific, and you may define rules for as many different -# locales as you wish. All of these examples are active by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' -# inflect.uncountable %w( fish sheep ) -# end - -# These inflection rules are supported but not enabled by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' -# end diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/mime_types.rb b/vendor/gems/graphql/spec/dummy/config/initializers/mime_types.rb deleted file mode 100644 index 6e1d16f0278..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/mime_types.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# Add new mime types for use in respond_to blocks: -# Mime::Type.register "text/richtext", :rtf diff --git a/vendor/gems/graphql/spec/dummy/config/initializers/wrap_parameters.rb b/vendor/gems/graphql/spec/dummy/config/initializers/wrap_parameters.rb deleted file mode 100644 index a3419d71da2..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/initializers/wrap_parameters.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# This file contains settings for ActionController::ParamsWrapper which -# is enabled by default. - -# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. -ActiveSupport.on_load(:action_controller) do - wrap_parameters format: [:json] -end diff --git a/vendor/gems/graphql/spec/dummy/config/locales/en.yml b/vendor/gems/graphql/spec/dummy/config/locales/en.yml deleted file mode 100644 index cf9b342d0ae..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/locales/en.yml +++ /dev/null @@ -1,33 +0,0 @@ -# Files in the config/locales directory are used for internationalization -# and are automatically loaded by Rails. If you want to use locales other -# than English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t 'hello' -# -# In views, this is aliased to just `t`: -# -# <%= t('hello') %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# The following keys must be escaped otherwise they will not be retrieved by -# the default I18n backend: -# -# true, false, on, off, yes, no -# -# Instead, surround them with single quotes. -# -# en: -# 'true': 'foo' -# -# To learn more, please read the Rails Internationalization guide -# available at https://guides.rubyonrails.org/i18n.html. - -en: - hello: "Hello world" diff --git a/vendor/gems/graphql/spec/dummy/config/puma.rb b/vendor/gems/graphql/spec/dummy/config/puma.rb deleted file mode 100644 index 6f164baca6d..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/puma.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -# -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -threads threads_count, threads_count - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. -# -port ENV.fetch("PORT") { 3000 } - -# Specifies the `environment` that Puma will run in. -# -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } - -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. If you use this option -# you need to make sure to reconnect any threads in the `on_worker_boot` -# block. -# -# preload_app! - -# If you are preloading your application and using Active Record, it's -# recommended that you close any connections to the database before workers -# are forked to prevent connection leakage. -# -# before_fork do -# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) -# end - -# The code in the `on_worker_boot` will be called if you are using -# clustered mode by specifying a number of `workers`. After each worker -# process is booted, this block will be run. If you are using the `preload_app!` -# option, you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, as Ruby -# cannot share connections between processes. -# -# on_worker_boot do -# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) -# end -# - -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart diff --git a/vendor/gems/graphql/spec/dummy/config/routes.rb b/vendor/gems/graphql/spec/dummy/config/routes.rb deleted file mode 100644 index 41590eace4b..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/routes.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -Rails.application.routes.draw do - root to: "pages#show" - mount GraphQL::Dashboard, at: "/dash", schema: "DummySchema" -end diff --git a/vendor/gems/graphql/spec/dummy/config/secrets.yml b/vendor/gems/graphql/spec/dummy/config/secrets.yml deleted file mode 100644 index 2eaa7194f1e..00000000000 --- a/vendor/gems/graphql/spec/dummy/config/secrets.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rails secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -# Shared secrets are available across all environments. - -# shared: -# api_key: a1B2c3D4e5F6 - -# Environmental secrets are only available for that specific environment. - -development: - secret_key_base: 32be83294ab8a3b21fce26b6e48db6c5d84cfa3c89e686dcb39369806e8daba53215caed302b89ce4a54b5c67ff2fa6be797f0f5c937a71fbb9f396ef108e867 - -test: - secret_key_base: 88c4ea138f1ad39c9eaa3196361a539e8aba1fcbffa3c2383045799cefe150fadaa7256ea680a912d27a878fdff55debd3f920388de68aa4f81828b0bd2ac072 - -# Do not keep production secrets in the unencrypted secrets file. -# Instead, either read values from the environment. -# Or, use `bin/rails secrets:setup` to configure encrypted secrets -# and move the `production:` environment over there. - -production: - secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/vendor/gems/graphql/spec/dummy/package.json b/vendor/gems/graphql/spec/dummy/package.json deleted file mode 100644 index caa2d7bb3f9..00000000000 --- a/vendor/gems/graphql/spec/dummy/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "dummy", - "private": true, - "dependencies": {} -} diff --git a/vendor/gems/graphql/spec/dummy/public/404.html b/vendor/gems/graphql/spec/dummy/public/404.html deleted file mode 100644 index 2be3af26fc5..00000000000 --- a/vendor/gems/graphql/spec/dummy/public/404.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The page you were looking for doesn't exist (404) - - - - - - -
    -
    -

    The page you were looking for doesn't exist.

    -

    You may have mistyped the address or the page may have moved.

    -
    -

    If you are the application owner check the logs for more information.

    -
    - - diff --git a/vendor/gems/graphql/spec/dummy/public/422.html b/vendor/gems/graphql/spec/dummy/public/422.html deleted file mode 100644 index c08eac0d1df..00000000000 --- a/vendor/gems/graphql/spec/dummy/public/422.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The change you wanted was rejected (422) - - - - - - -
    -
    -

    The change you wanted was rejected.

    -

    Maybe you tried to change something you didn't have access to.

    -
    -

    If you are the application owner check the logs for more information.

    -
    - - diff --git a/vendor/gems/graphql/spec/dummy/public/500.html b/vendor/gems/graphql/spec/dummy/public/500.html deleted file mode 100644 index 78a030af22e..00000000000 --- a/vendor/gems/graphql/spec/dummy/public/500.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - We're sorry, but something went wrong (500) - - - - - - -
    -
    -

    We're sorry, but something went wrong.

    -
    -

    If you are the application owner check the logs for more information.

    -
    - - diff --git a/vendor/gems/graphql/spec/dummy/public/apple-touch-icon-precomposed.png b/vendor/gems/graphql/spec/dummy/public/apple-touch-icon-precomposed.png deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/vendor/gems/graphql/spec/dummy/public/apple-touch-icon.png b/vendor/gems/graphql/spec/dummy/public/apple-touch-icon.png deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/vendor/gems/graphql/spec/dummy/public/favicon.ico b/vendor/gems/graphql/spec/dummy/public/favicon.ico deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/vendor/gems/graphql/spec/dummy/public/robots.txt b/vendor/gems/graphql/spec/dummy/public/robots.txt deleted file mode 100644 index 37b576a4a01..00000000000 --- a/vendor/gems/graphql/spec/dummy/public/robots.txt +++ /dev/null @@ -1 +0,0 @@ -# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/vendor/gems/graphql/spec/dummy/test/application_system_test_case.rb b/vendor/gems/graphql/spec/dummy/test/application_system_test_case.rb deleted file mode 100644 index acf86e36117..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/application_system_test_case.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -require "test_helper" - -class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400] -end diff --git a/vendor/gems/graphql/spec/dummy/test/channels/graphql_channel_test.rb b/vendor/gems/graphql/spec/dummy/test/channels/graphql_channel_test.rb deleted file mode 100644 index 46f1da6d5f1..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/channels/graphql_channel_test.rb +++ /dev/null @@ -1,159 +0,0 @@ -# frozen_string_literal: true -require "test_helper" - -class GraphqlChannelTest < ActionCable::Channel::TestCase - module RealChannelStub - def confirmed? - subscription_confirmation_sent? - end - - def real_streams - streams - end - end - - def assert_has_real_stream(stream_name) - assert subscription.real_streams.key?(stream_name), "Expected Stream #{stream_name.inspect} to be present in #{subscription.real_streams.keys}" - end - - def setup - @prev_server = ActionCable.server - @server = TestServer.new(subscription_adapter: ActionCable::SubscriptionAdapter::Async) - @server.config.allowed_request_origins = [ 'http://rubyonrails.com' ] - - ActionCable.instance_variable_set(:@server, @server) - end - - def teardown - ActionCable.instance_variable_set(:@server, @prev_server) - end - - def wait_for_async - wait_for_executor Concurrent.global_io_executor - end - - def run_in_eventmachine - yield - wait_for_async - end - - def wait_for_executor(executor) - # do not wait forever, wait 2s - timeout = 2 - until executor.completed_task_count == executor.scheduled_task_count - sleep 0.1 - timeout -= 0.1 - raise "Executor could not complete all tasks in 2 seconds" unless timeout > 0 - end - end - - class Connection < ActionCable::Connection::Base - attr_reader :websocket - - def send_async(method, *args) - send method, *args - end - - public :handle_close - end - - - module InterceptTransmit - def transmit(msg) - intercepted_messages << JSON.parse(msg) - super - end - - def intercepted_messages - @intercepted_messages ||= [] - end - end - - test "it subscribes and unsubscribes" do - run_in_eventmachine do - env = Rack::MockRequest.env_for "/test", "HTTP_HOST" => "localhost", "HTTP_CONNECTION" => "upgrade", "HTTP_UPGRADE" => "websocket", "HTTP_ORIGIN" => "http://rubyonrails.com" - - @connection = Connection.new(ActionCable.server, env).tap do |connection| - connection.process - assert_predicate connection.websocket, :possible? - - wait_for_async - assert_predicate connection.websocket, :alive? - connection.websocket.singleton_class.prepend(InterceptTransmit) - end - - @connection.subscriptions.add({"identifier" => "{\"channel\": \"GraphqlChannel\"}"}) - - @subscription = @connection.subscriptions.instance_variable_get(:@subscriptions).values.first - @subscription.singleton_class.prepend(RealChannelStub) - assert subscription.confirmed? - - subscription.execute({ - "query" => "subscription { payload(id: \"abc\") { value } }" - }) - wait_for_async - - sub_id = subscription.instance_variable_get(:@subscription_ids).first - subscription_stream = "graphql-subscription:#{sub_id}" - assert_has_real_stream subscription_stream - topic_stream = "graphql-event::payload:id:abc" - assert_has_real_stream topic_stream - - subscription.make_trigger({ "field" => "payload", "arguments" => { "id" => "abc"}, "value" => 19 }) - - wait_for_async - - @connection.handle_close - wait_for_async - - expected_data = [ - {"identifier"=>"{\"channel\": \"GraphqlChannel\"}", "type"=>"confirm_subscription"}, - {"identifier"=>"{\"channel\": \"GraphqlChannel\"}", "message"=>{"result"=>{"data"=>{}}, "more"=>true}}, - {"identifier"=>"{\"channel\": \"GraphqlChannel\"}", "message"=>{"result"=>{"data"=>{"payload"=>{"value"=>19}}}, "more"=>true}}, - {"identifier"=>"{\"channel\": \"GraphqlChannel\"}", "message"=>{"more"=>false}}, - ] - assert_equal expected_data, @connection.websocket.intercepted_messages - end - end - - class TestServer - include ActionCable::Server::Connections - include ActionCable::Server::Broadcasting - - attr_reader :logger, :config, :mutex - - class FakeConfiguration < ActionCable::Server::Configuration - attr_accessor :subscription_adapter, :log_tags, :filter_parameters - - def initialize(subscription_adapter:) - @log_tags = [] - @filter_parameters = [] - @subscription_adapter = subscription_adapter - end - - def pubsub_adapter - @subscription_adapter - end - end - - def initialize(subscription_adapter: SuccessAdapter) - @logger = ActiveSupport::TaggedLogging.new ActiveSupport::Logger.new(StringIO.new) - @config = FakeConfiguration.new(subscription_adapter: subscription_adapter) - @mutex = Monitor.new - end - - def pubsub - @pubsub ||= @config.subscription_adapter.new(self) - end - - def event_loop - @event_loop ||= ActionCable::Connection::StreamEventLoop.new.tap do |loop| - loop.instance_variable_set(:@executor, Concurrent.global_io_executor) - end - end - - def worker_pool - @worker_pool ||= ActionCable::Server::Worker.new(max_size: 5) - end - end -end diff --git a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/application_controller_test.rb b/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/application_controller_test.rb deleted file mode 100644 index ba20b65bef6..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/application_controller_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -require "test_helper" - -class DashboardApplicationControllerTest < ActionDispatch::IntegrationTest - def test_it_calls_on_load_hook - assert_equal true, GraphQL::Dashboard::ApplicationController.hook_was_called? - end -end diff --git a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/landings_controller_test.rb b/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/landings_controller_test.rb deleted file mode 100644 index 97fd9822bc6..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/landings_controller_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -require "test_helper" - -class DashboardLandingsControllerTest < ActionDispatch::IntegrationTest - def test_it_shows_a_landing_page - get graphql_dashboard.root_path - assert_includes response.body, "Welcome to the GraphQL-Ruby Dashboard" - end - - def test_it_shows_version_and_schema_info - get graphql_dashboard.root_path - assert_includes response.body, "GraphQL-Ruby v#{GraphQL::VERSION}" - assert_includes response.body, "DummySchema" - get graphql_dashboard.root_path, params: { schema: "NotInstalledSchema" } - assert_includes response.body, "NotInstalledSchema" - end -end diff --git a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/statics_controller_test.rb b/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/statics_controller_test.rb deleted file mode 100644 index c0b02303bc6..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/statics_controller_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -require "test_helper" - -class DashboardStaticsControllerTest < ActionDispatch::IntegrationTest - def test_it_serves_assets - get graphql_dashboard.static_path("dashboard.css") - assert_includes response.body, "#header-icon {" - assert_equal response.headers["Cache-Control"], "max-age=31556952, public" - end - - def test_it_responds_404_for_others - get graphql_dashboard.static_path("other.rb") - assert_equal 404, response.status - - assert_raises ActionController::UrlGenerationError do - graphql_dashboard.static_path("invalid~char.js") - end - - get graphql_dashboard.static_path("invalid-char.js").sub("-char", "~char") - assert_equal 404, response.status - end -end diff --git a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/traces_controller_test.rb b/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/traces_controller_test.rb deleted file mode 100644 index 39b6cffb6d2..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/controllers/dashboard/traces_controller_test.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true -require "test_helper" - -class DashboardTracesControllerTest < ActionDispatch::IntegrationTest - def teardown - DummySchema.detailed_trace.delete_all_traces - end - - def test_it_renders_not_installed - get graphql_dashboard.traces_path, params: { schema: "NotInstalledSchema" } - assert_includes response.body, "Traces aren't installed yet" - assert_includes response.body, "NotInstalledSchema" - end - - def test_it_renders_blank_state - get graphql_dashboard.traces_path - assert_includes response.body, "No traces saved yet." - assert_includes response.body, "DummySchema" - end - - def test_it_renders_trace_listing_with_pagination - 20.times do |n| - sleep 0.05 - DummySchema.execute("query Query#{n} { str }", context: { profile: true }) - end - assert_equal 20, DummySchema.detailed_trace.traces.size - - get graphql_dashboard.traces_path, params: { last: 10 } - - assert_includes response.body, "Query19" - assert_includes response.body, "Query10" - refute_includes response.body, "Query9" - last_trace = DummySchema.detailed_trace.traces[9] - last_ts = last_trace.begin_ms - assert_includes response.body, "#{Time.at(last_ts / 1000.0).strftime("%Y-%m-%d %H:%M:%S.%L")}" - assert_includes response.body, "Previous >" - get graphql_dashboard.traces_path, params: { last: 10, before: last_ts } - assert_includes response.body, "Query9" - assert_includes response.body, "Query0" - refute_includes response.body, "Query10" - very_last_trace = DummySchema.detailed_trace.traces.last - very_last_ts = very_last_trace.begin_ms - very_last_td = "#{Time.at(very_last_ts / 1000.0).strftime("%Y-%m-%d %H:%M:%S.%L")}" - assert_includes response.body, very_last_td - very_last_previous_link = "Previous >" - assert_includes response.body, very_last_previous_link - - # Go beyond last trace: - get graphql_dashboard.traces_path, params: { last: 11, before: last_ts } - assert_includes response.body, very_last_td - refute_includes response.body, very_last_previous_link - end - - def test_it_deletes_one_trace - DummySchema.execute("{ str }", context: { profile: true }) - assert_equal 1, DummySchema.detailed_trace.traces.size - id = DummySchema.detailed_trace.traces.first.id - delete graphql_dashboard.trace_path(id) - assert_equal 0, DummySchema.detailed_trace.traces.size - end - - def test_it_deletes_all_traces - DummySchema.execute("{ str }", context: { profile: true }) - assert_equal 1, DummySchema.detailed_trace.traces.size - delete graphql_dashboard.traces_delete_all_path - assert_equal 0, DummySchema.detailed_trace.traces.size - end -end diff --git a/vendor/gems/graphql/spec/dummy/test/system/action_cable_subscription_test.rb b/vendor/gems/graphql/spec/dummy/test/system/action_cable_subscription_test.rb deleted file mode 100644 index a7ce09ba3ca..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/system/action_cable_subscription_test.rb +++ /dev/null @@ -1,175 +0,0 @@ -# frozen_string_literal: true -require "application_system_test_case" - -class ActionCableSubscriptionsTest < ApplicationSystemTestCase - setup do - ActionCable.server.config.logger = Logger.new(STDOUT) - end - # This test covers a lot of ground! - test "it handles subscriptions" do - # Load the page and let the subscriptions happen - visit "/" - # make sure they connect successfully - assert_selector "#updates-1-connected" - assert_selector "#updates-2-connected" - - # Trigger a few updates, make sure we get a client update: - click_on("Trigger 1") - click_on("Trigger 1") - click_on("Trigger 1") - assert_selector "#updates-1-3", text: "3" - # Make sure there aren't any unexpected elements: - refute_selector "#updates-1-4" - refute_selector "#updates-2-1" - - # Now, trigger updates to a different stream - # and make sure the previous stream is not affected - click_on("Trigger 2") - click_on("Trigger 2") - assert_selector "#updates-2-1", text: "1" - assert_selector "#updates-2-2", text: "2" - refute_selector "#updates-2-3" - refute_selector "#updates-1-4" - - # Now unsubscribe one, it should not receive updates but the other should - click_on("Unsubscribe 1") - click_on("Trigger 1") - # This should not have changed - refute_selector "#updates-1-4" - - click_on("Trigger 2") - assert_selector "#updates-2-3", text: "3" - refute_selector "#updates-1-4" - - # wacky behavior to make sure the custom serializer is used: - click_on("Trigger 2") - assert_selector "#updates-2-400", text: "400" - end - - # Wrap an `assert_selector` call in a debugging check - def debug_assert_selector(selector) - if !page.has_css?(selector) - puts "[debug_assert_selector(#{selector.inspect})] Failed to find #{selector.inspect} in:" - puts page.html - else - puts "[debug_assert_selector(#{selector.inspect})] Found #{selector.inspect}" - end - assert_selector(selector) - end - - # It seems like ActionCable's order of evaluation here is non-deterministic, - # so detect which order to make the assertions. - # (They still both have to pass, but we don't know exactly what order the evaluations went in.) - def detect_update_values(possibility_1, possibility_2) - if page.has_css?("#fingerprint-updates-1-update-1-value-#{possibility_1}") - [possibility_1, possibility_2] - else - [possibility_2, possibility_1] - end - end - - - test "it only re-runs queries once for subscriptions with matching fingerprints" do - GraphqlChannel::CounterIncremented.reset_call_count - visit "/" - using_wait_time 10 do - sleep 1 - # Make 3 subscriptions to the same payload - click_on("Subscribe with fingerprint 1") - - debug_assert_selector "#fingerprint-updates-1-connected-1" - - click_on("Subscribe with fingerprint 1") - debug_assert_selector "#fingerprint-updates-1-connected-2" - click_on("Subscribe with fingerprint 1") - debug_assert_selector "#fingerprint-updates-1-connected-3" - - # And two to the next payload - click_on("Subscribe with fingerprint 2") - debug_assert_selector "#fingerprint-updates-2-connected-1" - click_on("Subscribe with fingerprint 2") - debug_assert_selector "#fingerprint-updates-2-connected-2" - - # Now trigger. We expect a total of two updates: - # - One is built & delivered to the first three subscribers - # - Another is built & delivered to the next two - click_on("Trigger with fingerprint 2") - - # The order here is random, I think depending on ActionCable's internal storage: - fingerprint_1_value, fingerprint_2_value = detect_update_values(1, 2) - - # These all share the first value: - debug_assert_selector "#fingerprint-updates-1-update-1-value-#{fingerprint_1_value}" - debug_assert_selector "#fingerprint-updates-1-update-2-value-#{fingerprint_1_value}" - debug_assert_selector "#fingerprint-updates-1-update-3-value-#{fingerprint_1_value}" - # and these share the second value: - debug_assert_selector "#fingerprint-updates-2-update-1-value-#{fingerprint_2_value}" - debug_assert_selector "#fingerprint-updates-2-update-2-value-#{fingerprint_2_value}" - - click_on("Unsubscribe with fingerprint 2") - click_on("Trigger with fingerprint 1") - - fingerprint_1_value_2, fingerprint_2_value_2 = detect_update_values(3, 4) - - # These get an update - debug_assert_selector "#fingerprint-updates-1-update-1-value-#{fingerprint_1_value_2}" - debug_assert_selector "#fingerprint-updates-1-update-2-value-#{fingerprint_1_value_2}" - debug_assert_selector "#fingerprint-updates-1-update-3-value-#{fingerprint_1_value_2}" - # But these are unsubscribed: - refute_selector "#fingerprint-updates-2-update-1-value-#{fingerprint_2_value_2}" - refute_selector "#fingerprint-updates-2-update-2-value-#{fingerprint_2_value_2}" - click_on("Unsubscribe with fingerprint 1") - # Make a new subscription and make sure it's updated: - click_on("Subscribe with fingerprint 2") - click_on("Trigger with fingerprint 2") - debug_assert_selector "#fingerprint-updates-2-update-1-value-#{fingerprint_2_value_2}" - # But this one was unsubscribed: - refute_selector "#fingerprint-updates-1-update-1-value-#{fingerprint_1_value_2 + 1}" - refute_selector "#fingerprint-updates-1-update-1-value-#{fingerprint_1_value_2 + 2}" - end - end - - test "it unsubscribes from the server" do - GraphqlChannel::CounterIncremented.reset_call_count - visit "/" - using_wait_time 10 do - sleep 1 - # Establish the connection - click_on("Subscribe with fingerprint 1") - debug_assert_selector "#fingerprint-updates-1-connected-1" - # Trigger once - click_on("Trigger with fingerprint 1") - debug_assert_selector "#fingerprint-updates-1-update-1-value-1" - - # Server unsubscribe - click_on("Server-side unsubscribe with fingerprint 1") - # Subsequent updates should fail - click_on("Trigger with fingerprint 1") - refute_selector "#fingerprint-updates-1-update-2-value-2" - - # The client has only 2 connections (from the initial 2) - assert_text "Remaining ActionCable subscriptions: 2" - end - end - - test "it unsubscribes with a message" do - GraphqlChannel::CounterIncremented.reset_call_count - visit "/" - using_wait_time 10 do - sleep 1 - # Establish the connection - click_on("Subscribe with fingerprint 1") - debug_assert_selector "#fingerprint-updates-1-connected-1" - # Trigger once - click_on("Trigger with fingerprint 1") - debug_assert_selector "#fingerprint-updates-1-update-1-value-1" - - # Server unsubscribe - click_on("Unsubscribe with message with fingerprint 1") - # Magic value from unsubscribe hook: - debug_assert_selector "#fingerprint-updates-1-update-1-value-9999" - # The client has only 2 connections (from the initial 2) - assert_text "Remaining ActionCable subscriptions: 2" - end - end -end diff --git a/vendor/gems/graphql/spec/dummy/test/test_helper.rb b/vendor/gems/graphql/spec/dummy/test/test_helper.rb deleted file mode 100644 index 4ccb83c0f45..00000000000 --- a/vendor/gems/graphql/spec/dummy/test/test_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true -require File.expand_path('../../config/environment', __FILE__) -require 'rails/test_help' diff --git a/vendor/gems/graphql/spec/fixtures/cop/field_type.rb b/vendor/gems/graphql/spec/fixtures/cop/field_type.rb deleted file mode 100644 index 815d0ab6a8b..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/field_type.rb +++ /dev/null @@ -1,18 +0,0 @@ -class Types::Query - field :current_account, Types::Account, null: false, description: "The account of the current viewer" - - field :find_account, Types::Account do - argument :id, ID - end - - # Don't modify these: - field :current_time, String, description: "The current time in the viewer's timezone" - field :current_time, Integer, description: "The current time in the viewer's timezone" - field :current_time, Int, description: "The current time in the viewer's timezone" - field :current_time, Float, description: "The current time in the viewer's timezone" - field :current_time, Boolean, description: "The current time in the viewer's timezone" - - field(:all_accounts, [Types::Account, null: false]) { - argument :active, Boolean, default_value: false - } -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/field_type_array.rb b/vendor/gems/graphql/spec/fixtures/cop/field_type_array.rb deleted file mode 100644 index 9a7cee1bac4..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/field_type_array.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Types::FooType < Types::BaseObject - field :other, [String] - field :bar, [Thing], null: false do - argument :baz, String - end -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/field_type_array_corrected.rb b/vendor/gems/graphql/spec/fixtures/cop/field_type_array_corrected.rb deleted file mode 100644 index 9458e81f3b7..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/field_type_array_corrected.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Types::FooType < Types::BaseObject - field :other, [String] - field :bar, null: false do - type [Thing] - argument :baz, String - end -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/field_type_corrected.rb b/vendor/gems/graphql/spec/fixtures/cop/field_type_corrected.rb deleted file mode 100644 index bd4e66cde79..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/field_type_corrected.rb +++ /dev/null @@ -1,22 +0,0 @@ -class Types::Query - field :current_account, null: false, description: "The account of the current viewer" do - type Types::Account - end - - field :find_account do - type Types::Account - argument :id, ID - end - - # Don't modify these: - field :current_time, String, description: "The current time in the viewer's timezone" - field :current_time, Integer, description: "The current time in the viewer's timezone" - field :current_time, Int, description: "The current time in the viewer's timezone" - field :current_time, Float, description: "The current time in the viewer's timezone" - field :current_time, Boolean, description: "The current time in the viewer's timezone" - - field(:all_accounts) { - type [Types::Account, null: false] - argument :active, Boolean, default_value: false - } -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/field_type_interface.rb b/vendor/gems/graphql/spec/fixtures/cop/field_type_interface.rb deleted file mode 100644 index 8f2e8833209..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/field_type_interface.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Types::FooType - include Types::BaseInterface - - field :thing, Thing -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/field_type_interface_corrected.rb b/vendor/gems/graphql/spec/fixtures/cop/field_type_interface_corrected.rb deleted file mode 100644 index 5bcf7251b9f..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/field_type_interface_corrected.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Types::FooType - include Types::BaseInterface - - field :thing do - type Thing - end -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/null_true.rb b/vendor/gems/graphql/spec/fixtures/cop/null_true.rb deleted file mode 100644 index 08e67091694..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/null_true.rb +++ /dev/null @@ -1,13 +0,0 @@ -class Types::Something < Types::BaseObject - field :name, String, null: true - - field :other_name, String, - null: true, - description: "Here's a description" - - field :described, [String, null: true], null: true, description: "Something" - - field :ok_field, String - - field :also_ok_field, String, null: false -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/null_true_corrected.rb b/vendor/gems/graphql/spec/fixtures/cop/null_true_corrected.rb deleted file mode 100644 index e5933dcb206..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/null_true_corrected.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Types::Something < Types::BaseObject - field :name, String - - field :other_name, String, - description: "Here's a description" - - field :described, [String, null: true], description: "Something" - - field :ok_field, String - - field :also_ok_field, String, null: false -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/required_true.rb b/vendor/gems/graphql/spec/fixtures/cop/required_true.rb deleted file mode 100644 index eb47a0f918a..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/required_true.rb +++ /dev/null @@ -1,20 +0,0 @@ -class Types::Something < Types::BaseObject - field :name, String do - argument :id_1, ID, required: true - - argument :id_2, - ID, - required: true, - description: "Described" - - argument :id_3, ID, other_config: { something: false, required: true }, required: true, description: "Something" - - argument :id_4, ID, required: false - - argument :id_5, ID - end - - field :name2, String do |f| - f.argument(:id_1, ID, required: true) - end -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/required_true_corrected.rb b/vendor/gems/graphql/spec/fixtures/cop/required_true_corrected.rb deleted file mode 100644 index 938d1267f67..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/required_true_corrected.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Types::Something < Types::BaseObject - field :name, String do - argument :id_1, ID - - argument :id_2, - ID, - description: "Described" - - argument :id_3, ID, other_config: { something: false, required: true }, description: "Something" - - argument :id_4, ID, required: false - - argument :id_5, ID - end - - field :name2, String do |f| - f.argument(:id_1, ID) - end -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/root_types.rb b/vendor/gems/graphql/spec/fixtures/cop/root_types.rb deleted file mode 100644 index 2d873fc4901..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/root_types.rb +++ /dev/null @@ -1,5 +0,0 @@ -class MyAppSchema < GraphQL::Schema - query Types::Query - mutation Types::Mutation - subscription Types::Subscription -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/root_types_corrected.rb b/vendor/gems/graphql/spec/fixtures/cop/root_types_corrected.rb deleted file mode 100644 index d4d1796c63a..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/root_types_corrected.rb +++ /dev/null @@ -1,5 +0,0 @@ -class MyAppSchema < GraphQL::Schema - query { Types::Query } - mutation { Types::Mutation } - subscription { Types::Subscription } -end diff --git a/vendor/gems/graphql/spec/fixtures/cop/small_field_type.rb b/vendor/gems/graphql/spec/fixtures/cop/small_field_type.rb deleted file mode 100644 index eda3b3008ec..00000000000 --- a/vendor/gems/graphql/spec/fixtures/cop/small_field_type.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Types::Admin::FooType < Types::FooType - field :bar, Types::BarType -end diff --git a/vendor/gems/graphql/spec/fixtures/eager_module/eager_class.rb b/vendor/gems/graphql/spec/fixtures/eager_module/eager_class.rb deleted file mode 100644 index 95d713fa727..00000000000 --- a/vendor/gems/graphql/spec/fixtures/eager_module/eager_class.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EagerModule - module EagerClass - end -end diff --git a/vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module.rb b/vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module.rb deleted file mode 100644 index 2a2b613bf46..00000000000 --- a/vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -module EagerModule - module NestedEagerModule - extend GraphQL::Autoload - autoload(:NestedEagerClass, "fixtures/eager_module/nested_eager_module/nested_eager_class") - end -end diff --git a/vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module/nested_eager_class.rb b/vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module/nested_eager_class.rb deleted file mode 100644 index 214ac3bf4b6..00000000000 --- a/vendor/gems/graphql/spec/fixtures/eager_module/nested_eager_module/nested_eager_class.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -module EagerModule - module NestedEagerModule - class NestedEagerClass - end - end -end diff --git a/vendor/gems/graphql/spec/fixtures/eager_module/other_eager_class.rb b/vendor/gems/graphql/spec/fixtures/eager_module/other_eager_class.rb deleted file mode 100644 index e2ee5d53588..00000000000 --- a/vendor/gems/graphql/spec/fixtures/eager_module/other_eager_class.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module EagerModule - module OtherEagerClass - end -end diff --git a/vendor/gems/graphql/spec/fixtures/lazy_module/lazy_class.rb b/vendor/gems/graphql/spec/fixtures/lazy_module/lazy_class.rb deleted file mode 100644 index 27facc5e632..00000000000 --- a/vendor/gems/graphql/spec/fixtures/lazy_module/lazy_class.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module LazyModule - module LazyClass - end -end diff --git a/vendor/gems/graphql/spec/fixtures/unicode_escapes/query1.graphql b/vendor/gems/graphql/spec/fixtures/unicode_escapes/query1.graphql deleted file mode 100644 index 684fd7148e3..00000000000 --- a/vendor/gems/graphql/spec/fixtures/unicode_escapes/query1.graphql +++ /dev/null @@ -1,6 +0,0 @@ -{ - example1: getString(string: "\u0064") # should return "d" - example2: getString(string: "\\u0064") # should return "\\u0064" - # example3: getString(string: "\u006") # validation error - example4: getString(string: "\\u006") # should return "\\u006" -} diff --git a/vendor/gems/graphql/spec/fixtures/unicode_escapes/query2.graphql b/vendor/gems/graphql/spec/fixtures/unicode_escapes/query2.graphql deleted file mode 100644 index 87de1742610..00000000000 --- a/vendor/gems/graphql/spec/fixtures/unicode_escapes/query2.graphql +++ /dev/null @@ -1,6 +0,0 @@ -query bug2 { - example1: getString(string: """\a""") # should be "\\a" - example2: getString(string: """\u006""") # should be "\\u006" - example3: getString(string: """\n""") # should be "\\n" - example4: getString(string: """\u0064""") # should be "\\u0064" -} diff --git a/vendor/gems/graphql/spec/graphql/analysis/field_usage_spec.rb b/vendor/gems/graphql/spec/graphql/analysis/field_usage_spec.rb deleted file mode 100644 index 2d82b7a7ef2..00000000000 --- a/vendor/gems/graphql/spec/graphql/analysis/field_usage_spec.rb +++ /dev/null @@ -1,295 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Analysis::FieldUsage do - let(:result) { GraphQL::Analysis.analyze_query(query, [GraphQL::Analysis::FieldUsage]).first } - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables) } - let(:variables) { {} } - - describe "query with deprecated fields" do - let(:query_string) {%| - query { - cheese(id: 1) { - id - fatContent - } - } - |} - - it "keeps track of used fields" do - assert_equal ['Cheese.id', 'Cheese.fatContent', 'Query.cheese'], result[:used_fields] - end - - it "keeps track of deprecated fields" do - assert_equal ['Cheese.fatContent'], result[:used_deprecated_fields] - end - end - - describe "query with deprecated fields used more than once" do - let(:query_string) {%| - query { - cheese1: cheese(id: 1) { - id - fatContent - } - - cheese2: cheese(id: 2) { - id - fatContent - } - } - |} - - it "omits duplicate usage of a field" do - assert_equal ['Cheese.id', 'Cheese.fatContent', 'Query.cheese'], result[:used_fields] - end - - it "omits duplicate usage of a deprecated field" do - assert_equal ['Cheese.fatContent'], result[:used_deprecated_fields] - end - end - - describe "query with deprecated fields in a fragment" do - let(:query_string) {%| - query { - cheese(id: 1) { - id - ...CheeseSelections - } - } - fragment CheeseSelections on Cheese { - fatContent - } - |} - - it "keeps track of fields used in the fragment" do - assert_equal ['Cheese.id', 'Cheese.fatContent', 'Query.cheese'], result[:used_fields] - end - - it "keeps track of deprecated fields used in the fragment" do - assert_equal ['Cheese.fatContent'], result[:used_deprecated_fields] - end - end - - describe "query with deprecated fields in an inline fragment" do - let(:query_string) {%| - query { - cheese(id: 1) { - id - ... on Cheese { - fatContent - } - } - } - |} - - it "keeps track of fields used in the fragment" do - assert_equal ['Cheese.id', 'Cheese.fatContent', 'Query.cheese'], result[:used_fields] - end - - it "keeps track of deprecated fields used in the fragment" do - assert_equal ['Cheese.fatContent'], result[:used_deprecated_fields] - end - end - - describe "query with deprecated arguments" do - let(:query_string) {%| - query { - fromSource(oldSource: "deprecated") { - id - } - } - |} - - it "keeps track of deprecated arguments" do - assert_equal ['Query.fromSource.oldSource'], result[:used_deprecated_arguments] - end - end - - describe "query with deprecated arguments used more than once" do - let(:query_string) {%| - query { - fromSource(oldSource: "deprecated1") { - id - } - - fromSource(oldSource: "deprecated2") { - id - } - } - |} - - it "omits duplicate usage of a deprecated argument" do - assert_equal ['Query.fromSource.oldSource'], result[:used_deprecated_arguments] - end - end - - describe "query with deprecated arguments nested in an array argument" do - let(:query_string) {%| - query { - searchDairy(product: [{ oldSource: "deprecated" }]) { - __typename - } - } - |} - - it "keeps track of nested deprecated arguments" do - assert_equal ['DairyProductInput.oldSource'], result[:used_deprecated_arguments] - end - end - - describe "query with deprecated enum argument" do - let(:query_string) {%| - query { - fromSource(source: YAK) { - id - } - } - |} - - it "keeps track of deprecated arguments" do - assert_equal ['DairyAnimal.YAK'], result[:used_deprecated_enum_values] - end - - describe "tracks non-null/list enums" do - let(:query_string) {%| - query { - cheese(id: 1) { - similarCheese(source: [YAK]) { - id - } - } - } - |} - - it "keeps track of deprecated arguments" do - assert_equal ['DairyAnimal.YAK'], result[:used_deprecated_enum_values] - end - end - end - - describe "query with an array argument sent as null" do - let(:query_string) {%| - query { - searchDairy(product: null) { - __typename - } - } - |} - - it "tolerates null for array argument" do - result - end - end - - describe "query with an input object sent in as null" do - let(:query_string) {%| - query { - cheese(id: 1) { - id - dairyProduct(input: null) { - __typename - } - } - } - |} - - it "tolerates null for object argument" do - result - end - end - - describe "query with deprecated arguments nested in an argument" do - let(:query_string) {%| - query { - searchDairy(singleProduct: { oldSource: "deprecated" }) { - __typename - } - } - |} - - it "keeps track of nested deprecated arguments" do - assert_equal ['DairyProductInput.oldSource'], result[:used_deprecated_arguments] - end - end - - describe "query with arguments nested in a deprecated argument" do - let(:query_string) {%| - query { - searchDairy(oldProduct: [{ source: "sheep" }]) { - __typename - } - } - |} - - it "keeps track of top-level deprecated arguments" do - assert_equal ['Query.searchDairy.oldProduct'], result[:used_deprecated_arguments] - end - end - - describe "query with scalar arguments nested in a deprecated argument" do - let(:query_string) {%| - query { - searchDairy(productIds: ["123"]) { - __typename - } - } - |} - - it "keeps track of top-level deprecated arguments" do - assert_equal ['Query.searchDairy.productIds'], result[:used_deprecated_arguments] - end - end - - - describe "mutation with deprecated argument" do - let(:query_string) {%| - mutation { - pushValue(deprecatedTestInput: { oldSource: "deprecated" }) - } - |} - - it "keeps track of nested deprecated arguments" do - assert_equal ['DairyProductInput.oldSource'], result[:used_deprecated_arguments] - end - end - - describe "mutation with deprecated arguments with prepared values" do - let(:query_string) {%| - mutation { - pushValue(preparedTestInput: { deprecatedDate: "2020-10-10" }) - } - |} - - it "keeps track of nested deprecated arguments" do - assert_equal ['PreparedDateInput.deprecatedDate'], result[:used_deprecated_arguments] - end - end - - describe "when an argument prepare raises a GraphQL::ExecutionError" do - class ArgumentErrorFieldUsageSchema < GraphQL::Schema - class FieldUsage < GraphQL::Analysis::FieldUsage - def result - values = super - query.context[:field_usage] = values - nil - end - end - - class Query < GraphQL::Schema::Object - field :f, Int do - argument :i, Int, prepare: ->(*) { raise GraphQL::ExecutionError.new("boom!") } - end - end - - query(Query) - query_analyzer(FieldUsage) - end - - it "skips analysis of those arguments" do - res = ArgumentErrorFieldUsageSchema.execute("{ f(i: 1) }") - assert_equal ["boom!"], res["errors"].map { |e| e["message"] } - assert_equal({used_fields: ["Query.f"], used_deprecated_arguments: [], used_deprecated_fields: [], used_deprecated_enum_values: []}, res.context[:field_usage]) - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/analysis/max_query_complexity_spec.rb b/vendor/gems/graphql/spec/graphql/analysis/max_query_complexity_spec.rb deleted file mode 100644 index 6a794ddcbd5..00000000000 --- a/vendor/gems/graphql/spec/graphql/analysis/max_query_complexity_spec.rb +++ /dev/null @@ -1,223 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Analysis::MaxQueryComplexity do - let(:schema) { Class.new(Dummy::Schema) } - let(:query_string) {%| - { - a: cheese(id: 1) { id } - b: cheese(id: 1) { id } - c: cheese(id: 1) { id } - d: cheese(id: 1) { id } - e: cheese(id: 1) { id } - } - |} - let(:query) { GraphQL::Query.new(schema, query_string, variables: {}, max_complexity: max_complexity) } - let(:result) { - GraphQL::Analysis.analyze_query(query, [GraphQL::Analysis::MaxQueryComplexity]).first - } - - - describe "when a query goes over max complexity" do - let(:max_complexity) { 9 } - - it "returns an error" do - assert_equal GraphQL::AnalysisError, result.class - assert_equal "Query has complexity of 10, which exceeds max complexity of 9", result.message - end - end - - describe "when there is no max complexity" do - let(:max_complexity) { nil } - - it "doesn't error" do - assert_nil result - end - end - - describe "when the query is less than the max complexity" do - let(:max_complexity) { 99 } - - it "doesn't error" do - assert_nil result - end - end - - describe "when max_complexity is decreased at query-level" do - before do - schema.max_complexity(100) - end - - let(:max_complexity) { 7 } - - it "is applied" do - assert_equal GraphQL::AnalysisError, result.class - assert_equal "Query has complexity of 10, which exceeds max complexity of 7", result.message - end - end - - describe "when max_complexity is increased at query-level" do - before do - schema.max_complexity(1) - end - - let(:max_complexity) { 10 } - - it "doesn't error" do - assert_nil result - end - end - - describe "when max_complexity is nil at query-level" do - let(:max_complexity) { nil } - - before do - schema.max_complexity(1) - end - - it "is applied" do - assert_nil result - end - end - - describe "when used with the max_depth plugin" do - let(:schema) do - Class.new(GraphQL::Schema) do - query Dummy::DairyAppQuery - - max_depth 3 - max_complexity 1 - end - end - - let(:query_string) {%| - { - a: cheese(id: 1) { ...cheeseFields } - b: cheese(id: 1) { ...cheeseFields } - c: cheese(id: 1) { ...cheeseFields } - d: cheese(id: 1) { ...cheeseFields } - e: cheese(id: 1) { ...cheeseFields } - } - - fragment cheeseFields on Cheese { id } - |} - let(:result) { schema.execute(query_string) } - - it "returns a complexity error" do - assert_equal "Query has complexity of 10, which exceeds max complexity of 1", result["errors"].first["message"] - end - end - - describe "count_introspection_fields: false" do - let(:schema) { Class.new(Dummy::Schema) { max_complexity(5) } } - let(:skip_introspection_schema) { Class.new(Dummy::Schema) do - max_complexity 5, count_introspection_fields: false - end - } - - it "skips introspection fields when configured" do - query_string = "{ c1: cheese(id: 1) { id __typename } c2: cheese(id: 2) { id __typename } }" - res = schema.execute(query_string) - expected_msg = "Query has complexity of 6, which exceeds max complexity of 5" - assert_equal [expected_msg], res["errors"].map { |e| e["message"]} - - res2 = skip_introspection_schema.execute(query_string) - assert_equal 2, res2["data"].size - refute res2.key?("errors") - end - end - - describe "across a multiplex" do - before do - schema.analysis_engine = GraphQL::Analysis::AST - end - - let(:queries) { - 5.times.map { |n| - GraphQL::Query.new(schema, "{ cheese(id: #{n}) { id } }", variables: {}) - } - } - - let(:max_complexity) { 9 } - let(:multiplex) { GraphQL::Execution::Multiplex.new(schema: schema, queries: queries, context: {}, max_complexity: max_complexity) } - let(:analyze_multiplex) { - GraphQL::Analysis.analyze_multiplex(multiplex, [GraphQL::Analysis::MaxQueryComplexity]) - } - - it "returns errors for all queries" do - analyze_multiplex - err_msg = "Query has complexity of 10, which exceeds max complexity of 9" - queries.each do |query| - assert_equal err_msg, query.analysis_errors[0].message - end - end - - describe "with a local override" do - let(:max_complexity) { 10 } - - it "uses the override" do - analyze_multiplex - - queries.each do |query| - assert query.analysis_errors.empty? - end - end - end - end - - describe "when an argument is unauthorized by type" do - class AuthorizedTypeSchema < GraphQL::Schema - class Thing < GraphQL::Schema::Object - def self.authorized?(obj, ctx) - !!ctx[:authorized] && super - end - field :name, String - end - - class Query < GraphQL::Schema::Object - field :things, Thing.connection_type do - argument :thing_id, ID, loads: Thing - end - - def things(thing:) - [thing] - end - end - - query(Query) - def self.resolve_type(abs_type, object, ctx) - Thing - end - - def self.object_from_id(id, ctx) - if id == "13" - raise GraphQL::ExecutionError, "No Thing ##{id}" - else - { name: "Loaded thing #{id}" } - end - end - - def self.unauthorized_object(err) - raise GraphQL::ExecutionError, "Unauthorized Object: #{err.object[:name].inspect}" - end - - default_max_page_size 30 - max_complexity 10 - end - - it "when the arg is unauthorized, returns an authorization error, not a complexity error" do - query_str = "{ things(thingId: \"123\", first: 1) { nodes { name } } }" - res = AuthorizedTypeSchema.execute(query_str, context: { authorized: true }) - assert_equal "Loaded thing 123", res["data"]["things"]["nodes"].first["name"] - - res2 = AuthorizedTypeSchema.execute(query_str) - assert_equal ["Unauthorized Object: \"Loaded thing 123\""], res2["errors"].map { |e| e["message"] } - end - - it "returns the right error when the loaded object raises an error" do - query_str = "{ things(thingId: \"13\", first: 1) { nodes { name } } }" - res = AuthorizedTypeSchema.execute(query_str, context: { authorized: true }) - assert_equal ["No Thing #13"], res["errors"].map { |e| e["message"] } - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/analysis/max_query_depth_spec.rb b/vendor/gems/graphql/spec/graphql/analysis/max_query_depth_spec.rb deleted file mode 100644 index de14b63a18c..00000000000 --- a/vendor/gems/graphql/spec/graphql/analysis/max_query_depth_spec.rb +++ /dev/null @@ -1,179 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Analysis::MaxQueryDepth do - let(:schema) { - schema = Class.new(Dummy::Schema) - schema.analysis_engine = GraphQL::Analysis::AST - schema - } - let(:query_string) { " - { - cheese(id: 1) { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - id - } - } - } - } - } - } - } - "} - let(:max_depth) { nil } - let(:query) { - # Don't override `schema.max_depth` with `nil` - options = max_depth ? { max_depth: max_depth } : {} - GraphQL::Query.new( - schema, - query_string, - variables: {}, - **options - ) - } - let(:result) { - GraphQL::Analysis.analyze_query(query, [GraphQL::Analysis::MaxQueryDepth]).first - } - let(:multiplex) { - GraphQL::Execution::Multiplex.new( - schema: schema, - queries: [query.dup, query.dup], - context: {}, - max_complexity: nil - ) - } - let(:multiplex_result) { - GraphQL::Analysis.analyze_multiplex(multiplex, [GraphQL::Analysis::MaxQueryDepth]).first - } - - describe "when the query is deeper than max depth" do - let(:max_depth) { 5 } - - it "adds an error message for a too-deep query" do - assert_equal "Query has depth of 7, which exceeds max depth of 5", result.message - end - end - - describe "when a multiplex queries is deeper than max depth" do - before do - schema.max_depth = 5 - end - - it "adds an error message for a too-deep query on from multiplex analyzer" do - assert_equal "Query has depth of 7, which exceeds max depth of 5", multiplex_result.message - end - end - - describe "when the query specifies a different max_depth" do - let(:max_depth) { 100 } - - it "obeys that max_depth" do - assert_nil result - end - end - - describe "When the query is not deeper than max_depth" do - before do - schema.max_depth = 100 - end - - it "doesn't add an error" do - assert_nil result - end - end - - describe "when the max depth isn't set" do - before do - schema.max_depth = nil - end - - it "doesn't add an error message" do - assert_nil result - end - end - - describe "when a fragment exceeds max depth" do - before do - schema.max_depth = 4 - end - - let(:query_string) { " - { - cheese(id: 1) { - ...moreFields - } - } - - fragment moreFields on Cheese { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - ...evenMoreFields - } - } - } - } - - fragment evenMoreFields on Cheese { - similarCheese(source: SHEEP) { - similarCheese(source: SHEEP) { - id - } - } - } - "} - - it "adds an error message for a too-deep query" do - assert_equal "Query has depth of 7, which exceeds max depth of 4", result.message - end - end - - describe "when the query would cause a stack error" do - let(:query_string) { - str = "query { cheese(id: 1) { ".dup - n = 10_000 - n.times { str << "similarCheese(source: SHEEP) { " } - str << "id " - n.times { str << "} " } - str << "} }" - str - } - - it "returns an error" do - assert_equal ["This query is too large to execute."], query.result["errors"].map { |err| err["message"] } - - # Make sure `Schema.execute` works too - execute_result = schema.execute(query_string) - assert_equal ["This query is too large to execute."], execute_result["errors"].map { |err| err["message"] } - end - end - - it "counts introspection fields by default, but can be set to skip" do - schema.max_depth = 3 - query_str = <<-GRAPHQL - { - __type(name: \"Abc\") { - fields { - type { - ofType { - name - } - } - } - } - } - GRAPHQL - - result = schema.execute(query_str) - assert_equal ["Query has depth of 5, which exceeds max depth of 3"], result["errors"].map { |e| e["message"] } - - schema.max_depth(3, count_introspection_fields: false) - - result = schema.execute(query_str) - assert_equal({ "__type" => nil }, result["data"]) - end -end diff --git a/vendor/gems/graphql/spec/graphql/analysis/query_complexity_spec.rb b/vendor/gems/graphql/spec/graphql/analysis/query_complexity_spec.rb deleted file mode 100644 index 931329a1e9e..00000000000 --- a/vendor/gems/graphql/spec/graphql/analysis/query_complexity_spec.rb +++ /dev/null @@ -1,732 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Analysis::QueryComplexity do - let(:schema) { Dummy::Schema } - let(:reduce_result) { GraphQL::Analysis.analyze_query(query, [GraphQL::Analysis::QueryComplexity]) } - let(:reduce_multiplex_result) { - GraphQL::Analysis.analyze_multiplex(multiplex, [GraphQL::Analysis::QueryComplexity]) - } - let(:variables) { {} } - let(:query_context) { {} } - let(:query) { GraphQL::Query.new(schema, query_string, context: query_context, variables: variables) } - let(:multiplex) { - GraphQL::Execution::Multiplex.new( - schema: schema, - queries: [query.dup, query.dup], - context: {}, - max_complexity: 10 - ) - } - - describe "simple queries" do - let(:query_string) {%| - query cheeses { - # complexity of 3 - cheese1: cheese(id: 1) { - id - flavor - __typename - } - - # complexity of 4 - cheese2: cheese(id: 2) { - similarCheese(source: SHEEP) { - ... on Cheese { - similarCheese(source: SHEEP) { - id - } - } - } - } - } - |} - - it "sums the complexity" do - complexities = reduce_result.first - assert_equal 8, complexities - end - end - - describe "with skip/include" do - let(:query_string) {%| - query cheeses($skip: Boolean = false, $include: Boolean = true) { - fields: cheese(id: 1) { - flavor - origin @skip(if: $skip) - source @include(if: $include) - } - inlineFragments: cheese(id: 1) { - ...on Cheese { flavor } - ...on Cheese @skip(if: $skip) { origin } - ...on Cheese @include(if: $include) { source } - } - fragmentSpreads: cheese(id: 1) { - ...Flavorful - ...Original @skip(if: $skip) - ...Sourced @include(if: $include) - } - } - fragment Flavorful on Cheese { flavor } - fragment Original on Cheese { origin } - fragment Sourced on Cheese { source } - |} - - it "sums up all included complexities" do - assert_equal 12, reduce_result.first - end - - describe "when skipped by directives" do - let(:variables) { { "skip" => true, "include" => false } } - it "doesn't include skipped fields and fragments" do - assert_equal 6, reduce_result.first - end - end - end - - describe "query with fragments" do - let(:query_string) {%| - { - # complexity of 3 - cheese1: cheese(id: 1) { - id - flavor - } - - # complexity of 7 - cheese2: cheese(id: 2) { - ... cheeseFields1 - ... cheeseFields2 - } - } - - fragment cheeseFields1 on Cheese { - similarCow: similarCheese(source: COW) { - id - ... cheeseFields2 - } - } - - fragment cheeseFields2 on Cheese { - similarSheep: similarCheese(source: SHEEP) { - id - } - } - |} - - it "counts all fragment usages, not the definitions" do - complexity = reduce_result.first - assert_equal 10, complexity - end - - describe "mutually exclusive types" do - let(:query_string) {%| - { - favoriteEdible { - # 1 for everybody - fatContent - - # 1 for everybody - ... on Edible { - origin - } - - # 1 for honey, aspartame - ... on Sweetener { - sweetness - } - - # 2 for milk - ... milkFields - # 1 for cheese - ... cheeseFields - # 1 for honey - ... honeyFields - # 1 for milk + cheese - ... dairyProductFields - } - } - - fragment milkFields on Milk { - id - source - } - - fragment cheeseFields on Cheese { - source - } - - fragment honeyFields on Honey { - flowerType - } - - fragment dairyProductFields on DairyProduct { - ... on Cheese { - flavor - } - - ... on Milk { - flavors - } - } - |} - - it "gets the max among options" do - complexity = reduce_result.first - assert_equal 6, complexity - end - end - - - describe "when there are no selections on any object types" do - let(:query_string) {%| - { - # 1 for everybody - favoriteEdible { - # 1 for everybody - fatContent - - # 1 for everybody - ... on Edible { origin } - - # 1 for honey, aspartame - ... on Sweetener { sweetness } - } - } - |} - - it "gets the max among interface types" do - complexity = reduce_result.first - assert_equal 4, complexity - end - end - - describe "redundant fields" do - let(:query_string) {%| - { - favoriteEdible { - fatContent - # this is executed separately and counts separately: - aliasedFatContent: fatContent - - ... on Edible { - fatContent - } - - ... edibleFields - } - } - - fragment edibleFields on Edible { - fatContent - } - |} - - it "only counts them once" do - complexity = reduce_result.first - assert_equal 3, complexity - end - end - - describe "redundant fields not within a fragment" do - let(:query_string) {%| - { - cheese { - id - } - - cheese { - id - } - } - |} - - it "only counts them once" do - complexity = reduce_result.first - assert_equal 2, complexity - end - end - end - - describe "relay types" do - let(:query) { GraphQL::Query.new(StarWars::Schema, query_string) } - let(:query_string) {%| - { - rebels { - ships(first: 1) { - edges { - node { - id - } - } - nodes { - id - } - pageInfo { - hasNextPage - } - } - } - } - |} - - it "gets the complexity" do - complexity = reduce_result.first - expected_complexity = 1 + # rebels - 1 + # ships - 1 + # edges - 1 + # nodes - 1 + 1 + # pageInfo, hasNextPage - 1 + 1 + 1 # node, id, id - assert_equal expected_complexity, complexity - end - - describe "first/last" do - let(:query_string) {%| - { - rebels { - s1: ships(first: 5) { - edges { - node { - id - } - } - pageInfo { - hasNextPage - } - } - - s2: ships(last: 3) { - nodes { id } - } - } - } - |} - - it "uses first/last for calculating complexity" do - complexity = reduce_result.first - - expected_complexity = ( - 1 + # rebels - (1 + 1 + (5 * 2) + 2) + # s1 - (1 + 1 + (3 * 1) + 0) # s2 - ) - assert_equal expected_complexity, complexity - end - end - - describe "Field-level max_page_size" do - let(:query_string) {%| - { - rebels { - ships { - nodes { id } - } - } - } - |} - - it "uses field max_page_size" do - complexity = reduce_result.first - assert_equal 1 + 1 + 1 + (1000 * 1), complexity - end - end - - describe "Schema-level default_max_page_size" do - let(:query_string) {%| - { - rebels { - bases { - nodes { id } - totalCount - } - } - } - |} - - it "uses schema default_max_page_size" do - complexity = reduce_result.first - assert_equal 1 + 1 + 1 + (3 * 1) + 1, complexity - end - end - - describe "Field-level default_page_size" do - let(:query_string) {%| - { - rebels { - shipsWithDefaultPageSize { - nodes { id } - } - } - } - |} - - it "uses field default_page_size" do - complexity = reduce_result.first - assert_equal 1 + 1 + 1 + (500 * 1), complexity - end - end - - describe "Schema-level default_page_size" do - let(:query) { GraphQL::Query.new(StarWars::SchemaWithDefaultPageSize, query_string) } - let(:query_string) {%| - { - rebels { - bases { - nodes { id } - totalCount - } - } - } - |} - - it "uses schema default_page_size" do - complexity = reduce_result.first - assert_equal 1 + 1 + 1 + (2 * 1) + 1, complexity - end - end - end - - describe "calculation complexity for a multiplex" do - let(:query_string) {%| - query cheeses { - cheese(id: 1) { - id - flavor - source - } - } - |} - - - it "sums complexity for both queries" do - complexity = reduce_multiplex_result.first - assert_equal 8, complexity - end - - describe "abstract type" do - let(:query_string) {%| - query Edible { - allEdible { - origin - fatContent - } - } - |} - it "sums complexity for both queries" do - complexity = reduce_multiplex_result.first - assert_equal 6, complexity - end - end - end - - describe "custom complexities" do - class CustomComplexitySchema < GraphQL::Schema - module ComplexityInterface - include GraphQL::Schema::Interface - field :value, Int - end - - class SingleComplexity < GraphQL::Schema::Object - field :value, Int, complexity: 0.1 - field :complexity, SingleComplexity do - argument :int_value, Int, required: false - complexity(->(ctx, args, child_complexity) { args[:int_value] + child_complexity }) - end - implements ComplexityInterface - end - - class DoubleComplexity < GraphQL::Schema::Object - field :value, Int, complexity: 4 - implements ComplexityInterface - end - - class Query < GraphQL::Schema::Object - field :complexity, SingleComplexity do - argument :int_value, Int, required: false, prepare: ->(val, ctx) { - if ctx[:raise_prepare_error] - raise GraphQL::ExecutionError, "Boom" - else - val - end - } - complexity ->(ctx, args, child_complexity) { args[:int_value] + child_complexity } - end - - def complexity(int_value:) - { value: int_value } - end - - field :inner_complexity, ComplexityInterface do - argument :value, Int, required: false - end - end - - query(Query) - orphan_types(DoubleComplexity) - - module CustomIntrospection - class DynamicFields < GraphQL::Introspection::DynamicFields - field :__typename, String, complexity: 100 - end - - class EntryPoints < GraphQL::Introspection::EntryPoints - class CustomIntrospectionField < GraphQL::Schema::Field - def calculate_complexity(query:, nodes:, child_complexity:) - child_complexity + 0.6 - end - end - field_class CustomIntrospectionField - field :__schema, GraphQL::Schema::LateBoundType.new("__Schema") - end - end - - introspection(CustomIntrospection) - end - - let(:query) { GraphQL::Query.new(complexity_schema, query_string, context: query_context) } - let(:complexity_schema) { CustomComplexitySchema } - let(:query_string) {%| - { - a: complexity(intValue: 3) { value } - b: complexity(intValue: 6) { - value - complexity(intValue: 1) { - value - } - } - } - |} - - it "sums the complexity" do - complexity = reduce_result.first - # 10 from `complexity`, `0.3` from `value` - assert_equal 10.3, complexity - end - - describe "introspection" do - let(:query_string) { "{ __typename __schema { queryType } }"} - - it "does custom complexity for introspection" do - complexity = reduce_result.first - # 100 + 1 + 0.6 - assert_equal 101.6, complexity - end - end - - describe "same field on multiple types" do - let(:query_string) {%| - { - innerComplexity(intValue: 2) { - ... on SingleComplexity { value } - ... on DoubleComplexity { value } - } - } - |} - - it "picks them max for those fields" do - complexity = reduce_result.first - # 1 for innerComplexity + 4 for DoubleComplexity.value - assert_equal 5, complexity - end - end - - describe "when prepare raises an error" do - let(:query_string) { "{ complexity(intValue: 3) { value } }"} - let(:query_context) { { raise_prepare_error: true } } - - it "handles it nicely" do - result = query.result - assert_equal ["Boom"], result["errors"].map { |e| e["message"] } - complexity = reduce_result.first - assert_equal 0.1, complexity - end - end - end - - describe "custom complexities by complexity_for(...)" do - class CustomComplexityByMethodSchema < GraphQL::Schema - module ComplexityInterface - include GraphQL::Schema::Interface - field :value, Int - end - - class SingleComplexity < GraphQL::Schema::Object - field :value, Int, complexity: 0.1 - field :complexity, SingleComplexity do - argument :int_value, Int, required: false - - def complexity_for(query:, child_complexity:, lookahead:) - lookahead.arguments[:int_value] + child_complexity - end - end - implements ComplexityInterface - end - - class ComplexityFourField < GraphQL::Schema::Field - def complexity_for(query:, lookahead:, child_complexity:) - 4 - end - end - - class DoubleComplexity < GraphQL::Schema::Object - field_class ComplexityFourField - field :value, Int - implements ComplexityInterface - end - - class Thing < GraphQL::Schema::Object - field :name, String - end - - class CustomThingConnection < GraphQL::Types::Relay::BaseConnection - edge_type Thing.edge_type - field :something_special, String, complexity: 3 - end - - class Query < GraphQL::Schema::Object - field :complexity, SingleComplexity do - argument :int_value, Int, required: false - def complexity_for(query:, child_complexity:, lookahead:) - lookahead.arguments[:int_value] + child_complexity - end - end - - field :inner_complexity, ComplexityInterface do - argument :value, Int, required: false - end - - field :things, Thing.connection_type, max_page_size: 100 do - argument :count, Int, validates: { numericality: { less_than: 50 } } - end - - def things(count:) - count.times.map {|t| {name: t.to_s}} - end - - class ThingsCustom < GraphQL::Schema::Resolver - type CustomThingConnection, null: false - complexity 100 - - def resolve - 5.times { |t| { name: "Thing #{t}" } } - end - end - - field :things_custom, resolver: ThingsCustom - end - - query(Query) - orphan_types(DoubleComplexity) - end - - let(:query) { GraphQL::Query.new(complexity_schema, query_string) } - let(:complexity_schema) { CustomComplexityByMethodSchema } - let(:query_string) {%| - { - a: complexity(intValue: 3) { value } - b: complexity(intValue: 6) { - value - complexity(intValue: 1) { - value - } - } - } - |} - - it "sums the complexity" do - complexity = reduce_result.first - # 10 from `complexity`, `0.3` from `value` - assert_equal 10.3, complexity - end - - describe "same field on multiple types" do - let(:query_string) {%| - { - innerComplexity(value: 2) { - ... on SingleComplexity { value } - ... on DoubleComplexity { value } - } - } - |} - - it "picks them max for those fields" do - complexity = reduce_result.first - # 1 for innerComplexity + 4 for DoubleComplexity.value - assert_equal 5, complexity - end - end - - describe "when the query fails validation" do - let(:query_string) {%| - { - things(count: 200, first: 5) { - nodes { name } - } - } - |} - it "handles the error" do - res = GraphQL::Query.new(complexity_schema, query_string).result - assert_equal ["count must be less than 50"], res["errors"].map { |e| e["message"] } - assert_equal [], reduce_result, "It doesn't finish calculation" - end - end - - describe "when connection fields have custom complexity" do - let(:query_string) { "{ thingsCustom(first: 2) { somethingSpecial nodes { name } } }"} - - it "uses the custom configured value" do - complexity = reduce_result.first - assert_equal 106, complexity - end - end - end - - describe "field_complexity hook" do - class CustomComplexityAnalyzer < GraphQL::Analysis::QueryComplexity - def initialize(query) - super - @field_complexities_by_query = {} - end - - def result - super - @field_complexities_by_query[@query] - end - - private - - def field_complexity(scoped_type_complexity, max_complexity:, child_complexity:) - @field_complexities_by_query[scoped_type_complexity.query] ||= {} - @field_complexities_by_query[scoped_type_complexity.query][scoped_type_complexity.response_path] = { - max_complexity: max_complexity, - child_complexity: child_complexity, - } - end - end - - let(:reduce_result) { GraphQL::Analysis.analyze_query(query, [CustomComplexityAnalyzer]) } - - let(:query_string) {%| - { - cheese { - id - } - - cheese { - id - flavor - } - } - |} - it "gets called for each field with complexity data" do - field_complexities = reduce_result.first - - assert_equal({ - ['cheese', 'id'] => { max_complexity: 1, child_complexity: nil }, - ['cheese', 'flavor'] => { max_complexity: 1, child_complexity: nil }, - ['cheese'] => { max_complexity: 3, child_complexity: 2 }, - }, field_complexities) - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/analysis/query_depth_spec.rb b/vendor/gems/graphql/spec/graphql/analysis/query_depth_spec.rb deleted file mode 100644 index bb840c32502..00000000000 --- a/vendor/gems/graphql/spec/graphql/analysis/query_depth_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Analysis::QueryDepth do - let(:result) { GraphQL::Analysis.analyze_query(query, [GraphQL::Analysis::QueryDepth]) } - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables) } - let(:variables) { {} } - - describe "multiple operations" do - let(:query_string) {%| - query Cheese1 { - cheese1: cheese(id: 1) { - id - flavor - } - } - - query Cheese2 { - cheese(id: 2) { - similarCheese(source: SHEEP) { - ... on Cheese { - similarCheese(source: SHEEP) { - id - } - } - } - } - } - |} - - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, operation_name: "Cheese1") } - - it "analyzes the selected operation only" do - depth = result.first - assert_equal 2, depth - end - end - - describe "simple queries" do - let(:query_string) {%| - query cheeses($isIncluded: Boolean = true){ - # depth of 2 - cheese1: cheese(id: 1) { - id - flavor - } - - # depth of 4 - cheese2: cheese(id: 2) @include(if: $isIncluded) { - similarCheese(source: SHEEP) { - ... on Cheese { - similarCheese(source: SHEEP) { - id - } - } - } - } - } - |} - - it "finds the max depth" do - depth = result.first - assert_equal 4, depth - end - - describe "with directives" do - let(:variables) { { "isIncluded" => false } } - - it "doesn't count skipped fields" do - assert_equal 2, result.first - end - end - end - - describe "query with fragments" do - let(:query_string) {%| - { - # depth of 2 - cheese1: cheese(id: 1) { - id - flavor - } - - # depth of 4 - cheese2: cheese(id: 2) { - ... cheeseFields1 - } - } - - fragment cheeseFields1 on Cheese { - similarCheese(source: COW) { - id - ... cheeseFields2 - } - } - - fragment cheeseFields2 on Cheese { - similarCheese(source: SHEEP) { - id - } - } - |} - - it "finds the max depth" do - assert_equal 4, result.first - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/analysis_spec.rb b/vendor/gems/graphql/spec/graphql/analysis_spec.rb deleted file mode 100644 index da0b83a5b38..00000000000 --- a/vendor/gems/graphql/spec/graphql/analysis_spec.rb +++ /dev/null @@ -1,612 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Analysis do - class AstTypeCollector < GraphQL::Analysis::Analyzer - def initialize(query) - super - @types = [] - end - - def on_enter_operation_definition(node, parent, visitor) - @types << visitor.type_definition - end - - def on_enter_field(memo, node, visitor) - @types << visitor.field_definition.type.unwrap - end - - def result - @types - end - end - - class AstNodeCounter < GraphQL::Analysis::Analyzer - def initialize(query) - super - @nodes = Hash.new { |h,k| h[k] = 0 } - end - - def on_enter_abstract_node(node, parent, _visitor) - @nodes[node.class] += 1 - end - - alias :on_enter_operation_definition :on_enter_abstract_node - alias :on_enter_field :on_enter_abstract_node - alias :on_enter_argument :on_enter_abstract_node - - def result - @nodes - end - end - - class AstConditionalAnalyzer < GraphQL::Analysis::Analyzer - def initialize(query) - super - @i_have_been_called = false - end - - def analyze? - !!query.context[:analyze] - end - - def on_operation_definition(node, parent, visitor) - @i_have_been_called = true - end - - def result - @i_have_been_called - end - end - - class AstPrecomputedAnalyzer < GraphQL::Analysis::Analyzer - def initialize(query) - super - @i_have_been_visited = false - end - - def visit? - query.context[:precomputed_result].nil? - end - - def on_enter_field(node, parent, visitor) - @i_have_been_visited = true - end - - def result - return query.context[:precomputed_result], @i_have_been_visited - end - end - - class AstErrorAnalyzer < GraphQL::Analysis::Analyzer - def result - GraphQL::AnalysisError.new("An Error!") - end - end - - class AstPreviousField < GraphQL::Analysis::Analyzer - def on_enter_field(node, parent, visitor) - @previous_field = visitor.previous_field_definition - end - - def result - @previous_field - end - end - - class AstArguments < GraphQL::Analysis::Analyzer - def on_enter_argument(node, parent, visitor) - @argument = visitor.argument_definition - @previous_argument = visitor.previous_argument_definition - end - - def result - [@argument, @previous_argument] - end - end - - class AstSkipInclude < GraphQL::Analysis::Analyzer - def initialize(query) - super - @included = [] - end - - def on_enter_field(node, parent, visitor) - @included << "enter #{node.name}" unless visitor.skipping? - end - - def on_leave_field(node, parent, visitor) - @included << "leave #{node.name}" unless visitor.skipping? - end - - def on_enter_inline_fragment(node, parent, visitor) - @included << "enter ...on #{node.type.name}" unless visitor.skipping? - end - - def on_leave_inline_fragment(node, parent, visitor) - @included << "leave ...on #{node.type.name}" unless visitor.skipping? - end - - def on_enter_fragment_spread(node, parent, visitor) - @included << "enter ...#{node.name}" unless visitor.skipping? - end - - def on_leave_fragment_spread(node, parent, visitor) - @included << "leave ...#{node.name}" unless visitor.skipping? - end - - def result - @included - end - end - - describe "skip and include behaviors" do - let(:reduce_result) { GraphQL::Analysis.analyze_query(query, [AstSkipInclude]) } - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string) } - let(:query_string) {%|{}|} - - describe "for fields" do - let(:query_string) {%| - { - cheese { - flavor - origin @skip(if: true) - source @include(if: false) - } - cheese @skip(if: true) { flavor } - cheese @include(if: false) { flavor } - } - |} - - it "tracks inclusions" do - expected = [ - "enter cheese", - "enter flavor", - "leave flavor", - "leave cheese", - ] - assert_equal expected, reduce_result.first - end - end - - describe "for inline fragments" do - let(:query_string) {%| - { - cheese { - ...on Cheese @skip(if: true) { origin } - ...on Cheese { flavor } - ...on Cheese @include(if: false) { source } - } - } - |} - - it "tracks inclusions" do - expected = [ - "enter cheese", - "enter ...on Cheese", - "enter flavor", - "leave flavor", - "leave ...on Cheese", - "leave cheese", - ] - assert_equal expected, reduce_result.first - end - end - - describe "for fragment spreads" do - let(:query_string) {%| - { - cheese { - ...Original @skip(if: true) - ...Flavorful - ...Sourced @include(if: false) - } - } - fragment Flavorful on Cheese { flavor } - fragment Original on Cheese { origin } - fragment Sourced on Cheese { source } - |} - - it "tracks inclusions" do - expected = [ - "enter cheese", - "enter ...Flavorful", - "enter flavor", - "leave flavor", - "leave ...Flavorful", - "leave cheese", - ] - assert_equal expected, reduce_result.first - end - end - end - - describe "using the AST analysis engine" do - let(:schema) do - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Query' - - field :foobar, Integer, null: false - - def foobar - 1337 - end - end - - Class.new(GraphQL::Schema) do - query query_type - query_analyzer AstErrorAnalyzer - end - end - - let(:query_string) {%| - query { - foobar - } - |} - - let(:query) { GraphQL::Query.new(schema, query_string, variables: {}) } - - it "runs the AST analyzers correctly" do - res = query.result - refute res.key?("data") - assert_equal ["An Error!"], res["errors"].map { |e| e["message"] } - end - end - - describe ".analyze_query" do - let(:analyzers) { [AstTypeCollector, AstNodeCounter] } - let(:reduce_result) { GraphQL::Analysis.analyze_query(query, analyzers) } - let(:variables) { {} } - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables) } - let(:query_string) {%| - { - cheese(id: 1) { - id - flavor - } - } - |} - - describe "without a valid operation" do - let(:query_string) {%| - # A comment - # is an invalid operation - # Should break - |} - - it "bails early when there is no selected operation to be executed" do - assert_equal 2, reduce_result.size - end - end - - describe "conditional analysis" do - let(:analyzers) { [AstTypeCollector, AstConditionalAnalyzer] } - - describe "when analyze? returns false" do - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, context: { analyze: false }) } - - it "does not run the analyzer" do - # Only type_collector ran - assert_equal 1, reduce_result.size - end - end - - describe "when analyze? returns true" do - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, context: { analyze: true }) } - - it "it runs the analyzer" do - # Both analyzers ran - assert_equal 2, reduce_result.size - end - end - - describe "Visitor#previous_field_definition" do - let(:analyzers) { [AstPreviousField] } - let(:query) { GraphQL::Query.new(Dummy::Schema, "{ __schema { types { name } } }") } - - it "it runs the analyzer" do - prev_field = reduce_result.first - assert_equal "__Schema.types", prev_field.path - end - end - - describe "Visitor#argument_definition" do - let(:analyzers) { [AstArguments] } - let(:query) do - GraphQL::Query.new( - Dummy::Schema, - '{ searchDairy(product: [{ source: "SHEEP" }]) { ... on Cheese { id } } }' - ) - end - - it "it runs the analyzer" do - argument, prev_argument = reduce_result.first - assert_equal "DairyProductInput.source", argument.path - assert_equal "Query.searchDairy.product", prev_argument.path - end - end - end - - describe "precomputed analysis" do - let(:analyzers) { [AstPrecomputedAnalyzer] } - - describe "when visit? returns true" do - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, context: {}) } - - it "runs the analyzer with visitation" do - assert_equal [nil, true], reduce_result.first - end - end - - describe "when visit? returns false" do - let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, context: { precomputed_result: 23 }) } - - it "runs the analyzer without visitation" do - assert_equal [23, false], reduce_result.first - end - end - end - - it "calls the defined analyzers" do - collected_types, node_counts = reduce_result - expected_visited_types = [ - Dummy::DairyAppQuery, - Dummy::Cheese, - GraphQL::Types::Int, - GraphQL::Types::String - ] - assert_equal expected_visited_types, collected_types - - expected_node_counts = { - GraphQL::Language::Nodes::OperationDefinition => 1, - GraphQL::Language::Nodes::Field => 3, - GraphQL::Language::Nodes::Argument => 1 - } - - assert_equal expected_node_counts, node_counts - end - - class FinishedSchema < GraphQL::Schema - class FinishedAnalyzer < GraphQL::Analysis::Analyzer - def on_enter_field(node, parent, visitor) - if query.context[:force_prepare] - visitor.arguments_for(node, visitor.field_definition) - end - end - - def result - query.context[:analysis_finished] = true - end - end - - class Query < GraphQL::Schema::Object - field :f1, Int do - argument :arg, String, prepare: ->(val, ctx) { - ctx[:analysis_finished] ? val.to_i : raise("Prepared too soon!") - } - end - def f1(arg:) - arg - end - end - - query(Query) - - query_analyzer(FinishedAnalyzer) - end - - it "doesn't call prepare hooks by default" do - res = FinishedSchema.execute("{ f1(arg: \"5\") }") - assert_equal 5, res["data"]["f1"] - err = assert_raises RuntimeError do - FinishedSchema.execute("{ f1(arg: \"5\") }", context: { force_prepare: true }) - end - assert_equal "Prepared too soon!", err.message - end - - describe "tracing" do - let(:query_string) { "{ t: __typename }"} - - it "emits traces" do - traces = TestTracing.with_trace do - ctx = { tracers: [TestTracing] } - Dummy::Schema.execute(query_string, context: ctx) - end - - # The query_trace is on the list _first_ because it finished first - if USING_C_PARSER - _lex, _parse, _validate, query_trace, multiplex_trace, *_rest = traces - else - _parse, _validate, query_trace, multiplex_trace, *_rest = traces - end - - assert_equal "analyze_multiplex", multiplex_trace[:key] - assert_instance_of GraphQL::Execution::Multiplex, multiplex_trace[:multiplex] - - assert_equal "analyze_query", query_trace[:key] - assert_instance_of GraphQL::Query, query_trace[:query] - end - end - - class AstConnectionCounter < GraphQL::Analysis::Analyzer - def initialize(query) - super - @fields = 0 - @connections = 0 - end - - def on_enter_field(node, parent, visitor) - if visitor.field_definition.connection? - @connections += 1 - else - @fields += 1 - end - end - - def result - { - fields: @fields, - connections: @connections - } - end - end - - describe "when processing fields" do - let(:analyzers) { [AstConnectionCounter] } - let(:reduce_result) { GraphQL::Analysis.analyze_query(query, analyzers) } - let(:query) { GraphQL::Query.new(StarWars::Schema, query_string, variables: variables) } - let(:query_string) {%| - query getBases { - empire { - basesByName(first: 30) { edges { cursor } } - bases(first: 30) { edges { cursor } } - } - } - |} - - it "knows which fields are connections" do - connection_counts = reduce_result.first - expected_connection_counts = { - :fields => 5, - :connections => 2 - } - assert_equal expected_connection_counts, connection_counts - end - end - end - - describe "Detecting all-introspection queries" do - class AllIntrospectionSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :int, Int - end - query(Query) - end - - class AllIntrospectionAnalyzer < GraphQL::Analysis::Analyzer - def initialize(query) - @is_introspection = true - super - end - - def on_enter_field(node, parent, visitor) - @is_introspection &= (visitor.field_definition.introspection? || ((owner = visitor.field_definition.owner) && owner.introspection?)) - end - - def result - @is_introspection - end - end - - def is_introspection?(query_str) - query = GraphQL::Query.new(AllIntrospectionSchema, query_str) - result = GraphQL::Analysis.analyze_query(query, [AllIntrospectionAnalyzer]) - result.first - end - - it "returns true for queries containing only introspection types and fields" do - assert is_introspection?("{ __typename }") - refute is_introspection?("{ int }") - assert is_introspection?(GraphQL::Introspection::INTROSPECTION_QUERY) - assert is_introspection?("{ __type(name: \"Something\") { fields { name } } }") - refute is_introspection?("{ int __type(name: \"Thing\") { name } }") - end - end - - describe "when there's a hidden field" do - class HiddenAnalyzedFieldSchema < GraphQL::Schema - use GraphQL::Schema::Warden if ADD_WARDEN - class DoNothingAnalyzer < GraphQL::Analysis::Analyzer - def on_enter_field(node, parent, visitor) - @result ||= [] - @result << [node.name, visitor.field_definition.class] - super - end - - attr_reader :result - end - class BaseField < GraphQL::Schema::Field - def initialize(*args, visible: true, **kwargs, &block) - @visible = visible - super(*args, **kwargs, &block) - end - - def visible?(context) - return @visible - end - end - - class BaseObject < GraphQL::Schema::Object - field_class BaseField - end - - class Article < BaseObject - field :title, String, null: false - end - - class Query < BaseObject - field :article, String, visible: false do |f| - f.argument(:id, Integer) - end - - def article(id:) - { title: "hello world" } - end - end - - query Query - end - - it "uses nil for the field definition" do - gql = <<~GQL - { - article(id: 1) { - title - } - } - GQL - - query = GraphQL::Query.new(HiddenAnalyzedFieldSchema, gql) - result = GraphQL::Analysis.analyze_query(query, [HiddenAnalyzedFieldSchema::DoNothingAnalyzer]) - assert_equal [[["article", NilClass], ["title", NilClass]]], result - end - end - - - describe ".validate_timeout" do - class AnalysisTimeoutSchema < GraphQL::Schema - class SlowAnalyzer < GraphQL::Analysis::Analyzer - def on_enter_field(node, parent, visitor) - sleep 0.1 - super - end - - def result - nil - end - end - - class Query < GraphQL::Schema::Object - field :f1, Int - - def f1 - context[:int] ||= 0 - context[:int] += 1 - end - end - - query(Query) - query_analyzer(SlowAnalyzer) - validate_timeout 0.5 - end - - it "covers analysis too" do - res = AnalysisTimeoutSchema.execute("{ f1: f1 f2: f1 }") - assert_equal({ "f1" => 1, "f2" => 2}, res["data"]) - - res2 = AnalysisTimeoutSchema.execute("{ f1: f1, f2: f1, f3: f1, f4: f1, f5: f1, f6: f1}") - assert_equal ["Timeout on validation of query"], res2["errors"].map { |e| e["message"]} - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/authorization_spec.rb b/vendor/gems/graphql/spec/graphql/authorization_spec.rb deleted file mode 100644 index b49af2aa44c..00000000000 --- a/vendor/gems/graphql/spec/graphql/authorization_spec.rb +++ /dev/null @@ -1,1009 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe "GraphQL::Authorization" do - module AuthTest - class Box - attr_reader :value - def initialize(value:) - @value = value - end - end - - class BaseArgument < GraphQL::Schema::Argument - def visible?(context) - super && (context[:hide] ? @name != "hidden" : true) - end - - def authorized?(parent_object, value, context) - super && parent_object != :hide2 - end - end - - class BaseInputObjectArgument < BaseArgument - def authorized?(parent_object, value, context) - super && parent_object != :hide3 - end - end - - class BaseInputObject < GraphQL::Schema::InputObject - argument_class BaseInputObjectArgument - end - - class BaseField < GraphQL::Schema::Field - argument_class BaseArgument - def visible?(context) - super && (context[:hide] ? @name != "hidden" : true) - end - - def authorized?(object, args, context) - if object == :raise - raise GraphQL::UnauthorizedFieldError.new("raised authorized field error", object: object) - end - return Box.new(value: context[:lazy_field_authorized]) if context.key?(:lazy_field_authorized) - - super && object != :hide && object != :replace - end - end - - class BaseObject < GraphQL::Schema::Object - field_class BaseField - end - - module BaseInterface - include GraphQL::Schema::Interface - end - - class BaseEnumValue < GraphQL::Schema::EnumValue - def initialize(*args, role: nil, **kwargs) - @role = role - super(*args, **kwargs) - end - - def visible?(context) - super && (context[:hide] ? @role != :hidden : true) - end - - def authorized?(context) - super && (context[:authorized] ? true : @role != :unauthorized) - end - end - - class BaseEnum < GraphQL::Schema::Enum - enum_value_class(BaseEnumValue) - end - - module HiddenInterface - include BaseInterface - - definition_methods do - def visible?(ctx) - super && !ctx[:hide] - end - - def resolve_type(obj, ctx) - HiddenObject - end - end - end - - module HiddenDefaultInterface - if GraphQL::Schema.use_visibility_profile? - include HiddenInterface - else - # Warden will detect no possible types - include BaseInterface - end - - def self.resolve_type(obj, ctx) - HiddenObject - end - end - - class HiddenObject < BaseObject - implements HiddenInterface - implements HiddenDefaultInterface - def self.visible?(ctx) - super && !ctx[:hide] - end - - field :some_field, String - end - - class RelayObject < BaseObject - def self.visible?(ctx) - super && !ctx[:hidden_relay] - end - - def self.authorized?(_val, ctx) - super && !ctx[:unauthorized_relay] - end - - field :some_field, String - end - - class UnauthorizedObject < BaseObject - def self.authorized?(value, context) - if context[:raise] - raise GraphQL::UnauthorizedError.new("raised authorized object error", object: value.object) - end - super && !context[:hide] - end - - field :value, String, null: false, method: :itself - end - - class UnauthorizedBox < BaseObject - # Hide `"a"` - def self.authorized?(value, context) - super && value != "a" - end - - field :value, String, null: false, method: :itself - end - - module UnauthorizedInterface - include BaseInterface - - def self.resolve_type(obj, ctx) - if obj.is_a?(String) - UnauthorizedCheckBox - else - raise "Unexpected value: #{obj.inspect}" - end - end - end - - class UnauthorizedCheckBox < BaseObject - implements UnauthorizedInterface - # This authorized check returns a lazy object, it should be synced by the runtime. - def self.authorized?(value, context) - if !value.is_a?(String) - raise "Unexpected box value: #{value.inspect}" - end - is_authed = super && value != "a" - # Make it many levels nested just to make sure we support nested lazy objects - Box.new(value: Box.new(value: Box.new(value: Box.new(value: is_authed)))) - end - - field :value, String, null: false, method: :itself - end - - class IntegerObject < BaseObject - def self.authorized?(obj, ctx) - if !obj.is_a?(Integer) - raise "Unexpected IntegerObject: #{obj}" - end - is_allowed = !(ctx[:unauthorized_relay] || obj == ctx[:exclude_integer]) - Box.new(value: Box.new(value: is_allowed)) - end - field :value, Integer, null: false, method: :itself - end - - class IntegerObjectEdge < GraphQL::Types::Relay::BaseEdge - node_type(IntegerObject) - end - - class IntegerObjectConnection < GraphQL::Types::Relay::BaseConnection - edge_type(IntegerObjectEdge) - end - - # This object responds with `replaced => false`, - # but if its replacement value is used, it gives `replaced => true` - class Replaceable - def replacement - { replaced: true } - end - - def replaced - false - end - end - - class ReplacedObject < BaseObject - def self.authorized?(obj, ctx) - super && !ctx[:replace_me] - end - - field :replaced, Boolean, null: false - end - - class LandscapeFeature < BaseEnum - value "MOUNTAIN" - value "STREAM", role: :unauthorized - value "FIELD" - value "TAR_PIT", role: :hidden - end - - class Query < BaseObject - def self.authorized?(obj, ctx) - !ctx[:query_unauthorized] - end - - field :hidden, Integer, null: false - field :unauthorized, Integer, method: :itself - field :int2, Integer do - argument :int, Integer, required: false - argument :hidden, Integer, required: false - argument :unauthorized, Integer, required: false - end - - def int2(**args) - args[:unauthorized] || 1 - end - - field :landscape_feature, LandscapeFeature, null: false do - argument :string, String, required: false - argument :enum, LandscapeFeature, required: false - end - - def landscape_feature(string: nil, enum: nil) - string || enum - end - - field :landscape_features, [LandscapeFeature], null: false do - argument :strings, [String], required: false - argument :enums, [LandscapeFeature], required: false - end - - def landscape_features(strings: [], enums: []) - strings + enums - end - - def empty_array; []; end - field :hidden_object, HiddenObject, null: false, resolver_method: :itself - field :hidden_interface, HiddenInterface, null: false, resolver_method: :itself - field :hidden_default_interface, HiddenDefaultInterface, null: false, resolver_method: :itself - field :hidden_connection, RelayObject.connection_type, null: :false, resolver_method: :empty_array - field :hidden_edge, RelayObject.edge_type, null: :false, resolver_method: :edge_object - - field :unauthorized_object, UnauthorizedObject, resolver_method: :itself - field :unauthorized_connection, RelayObject.connection_type, null: false, resolver_method: :array_with_item - field :unauthorized_edge, RelayObject.edge_type, null: false, resolver_method: :edge_object - - def edge_object - OpenStruct.new(node: 100) - end - - def array_with_item - [1] - end - - field :unauthorized_lazy_box, UnauthorizedBox do - argument :value, String - end - def unauthorized_lazy_box(value:) - # Make it extra nested, just for good measure. - Box.new(value: Box.new(value: value)) - end - field :unauthorized_list_items, [UnauthorizedObject] - def unauthorized_list_items - [self, self] - end - - field :unauthorized_lazy_check_box, UnauthorizedCheckBox, resolver_method: :unauthorized_lazy_box do - argument :value, String - end - - field :unauthorized_interface, UnauthorizedInterface, resolver_method: :unauthorized_lazy_box do - argument :value, String - end - - field :unauthorized_lazy_list_interface, [UnauthorizedInterface, null: true] - - def unauthorized_lazy_list_interface - ["z", Box.new(value: Box.new(value: "z2")), "a", Box.new(value: "a")] - end - - field :integers, IntegerObjectConnection, null: false - - def integers - [1,2,3] - end - - field :lazy_integers, IntegerObjectConnection, null: false - - def lazy_integers - Box.new(value: Box.new(value: [1,2,3])) - end - - field :replaced_object, ReplacedObject, null: false - def replaced_object - Replaceable.new - end - end - - class DoHiddenStuff < GraphQL::Schema::RelayClassicMutation - def self.visible?(ctx) - super && (ctx[:hidden_mutation] ? false : true) - end - end - - class DoHiddenStuff2 < GraphQL::Schema::Mutation - def self.visible?(ctx) - super && !ctx[:hidden_mutation] - end - - field :some_return_field, String - end - - class DoUnauthorizedStuff < GraphQL::Schema::RelayClassicMutation - def self.authorized?(obj, ctx) - super && (ctx[:unauthorized_mutation] ? false : true) - end - end - - class Mutation < BaseObject - field :do_hidden_stuff, mutation: DoHiddenStuff - field :do_hidden_stuff2, mutation: DoHiddenStuff2 - field :do_unauthorized_stuff, mutation: DoUnauthorizedStuff - end - - class Nothing < GraphQL::Schema::Directive - locations(FIELD) - def self.visible?(ctx) - !!ctx[:show_nothing_directive] - end - end - - class Schema < GraphQL::Schema - query(Query) - mutation(Mutation) - directive(Nothing) - use GraphQL::Schema::Warden if ADD_WARDEN - lazy_resolve(Box, :value) - - def self.unauthorized_object(err) - if err.object.respond_to?(:replacement) - err.object.replacement - elsif err.object == :replace - 33 - elsif err.object == :raise_from_object - raise GraphQL::ExecutionError, err.message - else - raise GraphQL::ExecutionError, "Unauthorized #{err.type.graphql_name}: #{err.object.inspect}" - end - end - end - - class SchemaWithFieldHook < GraphQL::Schema - query(Query) - use GraphQL::Schema::Warden if ADD_WARDEN - lazy_resolve(Box, :value) - - def self.unauthorized_field(err) - if err.object == :replace - 42 - elsif err.object == :raise - raise GraphQL::ExecutionError, "#{err.message} in field #{err.field.graphql_name}" - else - raise GraphQL::ExecutionError, "Unauthorized field #{err.field.graphql_name} on #{err.type.graphql_name}: #{err.object}" - end - end - end - end - - def auth_execute(*args, **kwargs) - AuthTest::Schema.execute(*args, **kwargs) - end - - describe "applying the visible? method" do - it "works in queries" do - res = auth_execute(" { int int2 } ", context: { hide: true }) - assert_equal 1, res["errors"].size - end - - it "applies return type visibility to fields" do - error_queries = { - "hiddenObject" => "{ hiddenObject { __typename } }", - "hiddenInterface" => "{ hiddenInterface { __typename } }", - "hiddenDefaultInterface" => "{ hiddenDefaultInterface { __typename } }", - } - - error_queries.each do |name, q| - hidden_res = auth_execute(q, context: { hide: true}) - assert_equal ["Field '#{name}' doesn't exist on type 'Query'#{name == "hiddenDefaultInterface" ? "" : " (Did you mean `hiddenConnection`?)"}"], hidden_res["errors"].map { |e| e["message"] } - - visible_res = auth_execute(q) - # Both fields exist; the interface resolves to the object type, though - assert_equal "HiddenObject", visible_res["data"][name]["__typename"] - end - end - - it "uses the mutation for derived fields, inputs and outputs" do - query = "mutation { doHiddenStuff(input: {}) { __typename } }" - res = auth_execute(query, context: { hidden_mutation: true }) - assert_equal ["Field 'doHiddenStuff' doesn't exist on type 'Mutation'"], res["errors"].map { |e| e["message"] } - - # `#resolve` isn't implemented, so this errors out: - assert_raises GraphQL::RequiredImplementationMissingError do - auth_execute(query) - end - - introspection_q = <<-GRAPHQL - { - t1: __type(name: "DoHiddenStuffInput") { name } - t2: __type(name: "DoHiddenStuffPayload") { name } - } - GRAPHQL - hidden_introspection_res = auth_execute(introspection_q, context: { hidden_mutation: true }) - assert_nil hidden_introspection_res["data"]["t1"] - assert_nil hidden_introspection_res["data"]["t2"] - - visible_introspection_res = auth_execute(introspection_q) - assert_equal "DoHiddenStuffInput", visible_introspection_res["data"]["t1"]["name"] - assert_equal "DoHiddenStuffPayload", visible_introspection_res["data"]["t2"]["name"] - end - - it "works with Schema::Mutation" do - query = "mutation { doHiddenStuff2 { __typename } }" - res = auth_execute(query, context: { hidden_mutation: true }) - assert_equal ["Field 'doHiddenStuff2' doesn't exist on type 'Mutation'"], res["errors"].map { |e| e["message"] } - - # `#resolve` isn't implemented, so this errors out: - assert_raises GraphQL::RequiredImplementationMissingError do - auth_execute(query) - end - end - - it "uses the base type for edges and connections" do - query = <<-GRAPHQL - { - hiddenConnection { __typename } - hiddenEdge { __typename } - } - GRAPHQL - - hidden_res = auth_execute(query, context: { hidden_relay: true }) - assert_equal 2, hidden_res["errors"].size - - visible_res = auth_execute(query) - assert_equal "RelayObjectConnection", visible_res["data"]["hiddenConnection"]["__typename"] - assert_equal "RelayObjectEdge", visible_res["data"]["hiddenEdge"]["__typename"] - end - - it "treats hidden enum values as non-existent, even in lists" do - hidden_res_1 = auth_execute <<-GRAPHQL, context: { hide: true } - { - landscapeFeature(enum: TAR_PIT) - } - GRAPHQL - - assert_equal ["Argument 'enum' on Field 'landscapeFeature' has an invalid value (TAR_PIT). Expected type 'LandscapeFeature'."], hidden_res_1["errors"].map { |e| e["message"] } - - hidden_res_2 = auth_execute <<-GRAPHQL, context: { hide: true } - { - landscapeFeatures(enums: [STREAM, TAR_PIT]) - } - GRAPHQL - - assert_equal ["Argument 'enums' on Field 'landscapeFeatures' has an invalid value ([STREAM, TAR_PIT]). Expected type '[LandscapeFeature!]'."], hidden_res_2["errors"].map { |e| e["message"] } - - success_res = auth_execute <<-GRAPHQL, context: { hide: false, authorized: true } - { - landscapeFeature(enum: TAR_PIT) - landscapeFeatures(enums: [STREAM, TAR_PIT]) - } - GRAPHQL - - assert_equal "TAR_PIT", success_res["data"]["landscapeFeature"] - assert_equal ["STREAM", "TAR_PIT"], success_res["data"]["landscapeFeatures"] - end - - it "refuses to resolve to hidden enum values" do - expected_class = AuthTest::LandscapeFeature::UnresolvedValueError - assert_raises(expected_class) do - auth_execute <<-GRAPHQL, context: { hide: true } - { - landscapeFeature(string: "TAR_PIT") - } - GRAPHQL - end - - assert_raises(expected_class) do - auth_execute <<-GRAPHQL, context: { hide: true } - { - landscapeFeatures(strings: ["STREAM", "TAR_PIT"]) - } - GRAPHQL - end - end - - it "rejects incoming unauthorized enum values" do - res = auth_execute <<-GRAPHQL, context: { } - { - landscapeFeature(enum: STREAM) - } - GRAPHQL - - assert_equal ["Unauthorized LandscapeFeature: \"STREAM\""], res["errors"].map { |e| e["message"] } - end - - it "rejects outgoing unauthorized enum values" do - err = assert_raises(AuthTest::LandscapeFeature::UnresolvedValueError) do - auth_execute <<-GRAPHQL, context: { } - { - landscapeFeature(string: "STREAM") - } - GRAPHQL - end - - assert_equal "`Query.landscapeFeature` returned `\"STREAM\"` at `landscapeFeature`, but this value was unauthorized. Update the field or resolver to return a different value in this case (or return `nil`).", err.message - end - - it "works in introspection" do - res = auth_execute <<-GRAPHQL, context: { hide: true, hidden_mutation: true } - { - query: __type(name: "Query") { - fields { - name - args { name } - } - } - - hiddenObject: __type(name: "HiddenObject") { name } - hiddenInterface: __type(name: "HiddenInterface") { name } - landscapeFeatures: __type(name: "LandscapeFeature") { enumValues { name } } - } - GRAPHQL - query_field_names = res["data"]["query"]["fields"].map { |f| f["name"] } - refute_includes query_field_names, "int" - int2_arg_names = res["data"]["query"]["fields"].find { |f| f["name"] == "int2" }["args"].map { |a| a["name"] } - assert_equal ["int", "unauthorized"], int2_arg_names - - assert_nil res["data"]["hiddenObject"] - assert_nil res["data"]["hiddenInterface"] - - visible_landscape_features = res["data"]["landscapeFeatures"]["enumValues"].map { |v| v["name"] } - assert_equal ["MOUNTAIN", "STREAM", "FIELD"], visible_landscape_features - end - - it "works when printing the SDL" do - full_sdl_lines = AuthTest::Schema.to_definition.split("\n") - restricted_sdl_lines = AuthTest::Schema.to_definition(context: { hide: true, hidden_mutation: true, hidden_relay: true }).split("\n") - expected_hidden_lines = [ - "Autogenerated return type of DoHiddenStuff2.", - "type DoHiddenStuff2Payload {", - "Autogenerated input type of DoHiddenStuff", - "input DoHiddenStuffInput {", - "Autogenerated return type of DoHiddenStuff.", - "type DoHiddenStuffPayload {", - "interface HiddenDefaultInterface", - "interface HiddenInterface", - "type HiddenObject implements HiddenDefaultInterface & HiddenInterface {", - " doHiddenStuff(", - " Parameters for DoHiddenStuff", - " input: DoHiddenStuffInput!", - " ): DoHiddenStuffPayload", - " doHiddenStuff2: DoHiddenStuff2Payload", - " hidden: Int!", - " hiddenConnection(", - " hiddenDefaultInterface: HiddenDefaultInterface!", - " hiddenEdge: RelayObjectEdge", - " hiddenInterface: HiddenInterface!", - " hiddenObject: HiddenObject!", - " int2(hidden: Int, int: Int, unauthorized: Int): Int" - ] - assert_equal expected_hidden_lines, full_sdl_lines.select { |l| l.include?("Hidden") || l.include?("hidden") } - assert_equal [], restricted_sdl_lines.select { |l| l.include?("Hidden") || l.include?("hidden") } - end - - it "works with directives" do - query_str = "{ __typename @nothing }" - visible_response = auth_execute(query_str, context: { show_nothing_directive: true }) - assert_equal "Query", visible_response["data"]["__typename"] - hidden_response = auth_execute(query_str) - assert_equal ["Directive @nothing is not defined"], hidden_response["errors"].map { |e| e["message"] } - end - end - - describe "applying the authorized? method" do - it "halts on unauthorized objects, replacing the object with nil" do - query = "{ unauthorizedObject { __typename } }" - hidden_response = auth_execute(query, context: { hide: true }) - assert_nil hidden_response["data"].fetch("unauthorizedObject") - visible_response = auth_execute(query, context: {}) - assert_equal({ "__typename" => "UnauthorizedObject" }, visible_response["data"]["unauthorizedObject"]) - end - - it "halts on unauthorized mutations" do - query = "mutation { doUnauthorizedStuff(input: {}) { __typename } }" - res = auth_execute(query, context: { unauthorized_mutation: true }) - assert_nil res["data"].fetch("doUnauthorizedStuff") - assert_raises GraphQL::RequiredImplementationMissingError do - auth_execute(query) - end - end - - describe "field level authorization" do - describe "unauthorized field" do - describe "with an unauthorized field hook configured" do - describe "when the hook returns a value" do - it "replaces the response with the return value of the unauthorized field hook" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :replace) - assert_equal 42, response["data"].fetch("unauthorized") - end - end - - describe "when the field hook raises an error" do - it "returns nil" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :hide) - assert_nil response["data"].fetch("unauthorized") - end - - it "adds the error to the errors key" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :hide) - assert_equal ["Unauthorized field unauthorized on Query: hide"], response["errors"].map { |e| e["message"] } - end - end - - - describe "when the field authorization resolves lazily" do - it "returns value if authorized" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: 34, context: { lazy_field_authorized: true }) - assert_equal 34, response["data"].fetch("unauthorized") - end - - it "returns nil if not authorized" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: 34, context: { lazy_field_authorized: false }) - assert_nil response["data"].fetch("unauthorized") - assert_equal ["Unauthorized field unauthorized on Query: 34"], response["errors"].map { |e| e["message"] } - end - end - - describe "when the field authorization raises an UnauthorizedFieldError" do - it "receives the raised error" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: :raise) - assert_equal ["raised authorized field error in field unauthorized"], response["errors"].map { |e| e["message"] } - end - end - end - - describe "with an unauthorized field hook not configured" do - describe "When the object hook replaces the field" do - it "delegates to the unauthorized object hook, which replaces the object" do - query = "{ unauthorized }" - response = AuthTest::Schema.execute(query, root_value: :replace) - assert_equal 33, response["data"].fetch("unauthorized") - end - end - describe "When the object hook raises an error" do - it "returns nil" do - query = "{ unauthorized }" - response = AuthTest::Schema.execute(query, root_value: :hide) - assert_nil response["data"].fetch("unauthorized") - end - - it "adds the error to the errors key" do - query = "{ unauthorized }" - response = AuthTest::Schema.execute(query, root_value: :hide) - assert_equal ["Unauthorized Query: :hide"], response["errors"].map { |e| e["message"] } - end - end - end - end - - describe "authorized field" do - it "returns the field data" do - query = "{ unauthorized }" - response = AuthTest::SchemaWithFieldHook.execute(query, root_value: 1) - assert_equal 1, response["data"].fetch("unauthorized") - end - end - end - - it "halts on unauthorized fields, using the parent object" do - query = "{ unauthorized }" - hidden_response = auth_execute(query, root_value: :hide) - assert_nil hidden_response["data"].fetch("unauthorized") - visible_response = auth_execute(query, root_value: 1) - assert_equal 1, visible_response["data"]["unauthorized"] - end - - it "halts on unauthorized arguments, using the parent object" do - query = "{ int2(unauthorized: 5) }" - hidden_response = auth_execute(query, root_value: :hide2) - assert_nil hidden_response["data"].fetch("int2") - visible_response = auth_execute(query) - assert_equal 5, visible_response["data"]["int2"] - end - - it "works with edges and connections" do - query = <<-GRAPHQL - { - unauthorizedConnection { - __typename - edges { - __typename - node { - __typename - } - } - nodes { - __typename - } - } - unauthorizedEdge { - __typename - node { - __typename - } - } - } - GRAPHQL - - unauthorized_res = auth_execute(query, context: { unauthorized_relay: true }) - conn = unauthorized_res["data"].fetch("unauthorizedConnection") - assert_equal "RelayObjectConnection", conn.fetch("__typename") - # This is tricky: the previous behavior was to replace the _whole_ - # list with `nil`. This was due to an implementation detail: - # The list field's return value (an array of integers) was wrapped - # _before_ returning, and during this wrapping, a cascading error - # caused the entire field to be nilled out. - # - # In the interpreter, each list item is contained and the error doesn't propagate - # up to the whole list. - # - # Originally, I thought that this was a _feature_ that obscured list entries. - # But really, look at the test below: you don't get this "feature" if - # you use `edges { node }`, so it can't be relied on in any way. - # - # All that to say, in the interpreter, `nodes` and `edges { node }` behave - # the same. - # - # TODO revisit the docs for this. - failed_nodes_value = [nil] - assert_equal failed_nodes_value, conn.fetch("nodes") - assert_equal [{"node" => nil, "__typename" => "RelayObjectEdge"}], conn.fetch("edges") - - edge = unauthorized_res["data"].fetch("unauthorizedEdge") - assert_nil edge.fetch("node") - assert_equal "RelayObjectEdge", edge["__typename"] - - unauthorized_object_paths = [ - ["unauthorizedConnection", "edges", 0, "node"], - ["unauthorizedConnection", "nodes", 0], - ["unauthorizedEdge", "node"] - ] - - assert_equal unauthorized_object_paths, unauthorized_res["errors"].map { |e| e["path"] } - - authorized_res = auth_execute(query) - conn = authorized_res["data"].fetch("unauthorizedConnection") - assert_equal "RelayObjectConnection", conn.fetch("__typename") - assert_equal [{"__typename"=>"RelayObject"}], conn.fetch("nodes") - assert_equal [{"node" => {"__typename" => "RelayObject"}, "__typename" => "RelayObjectEdge"}], conn.fetch("edges") - - edge = authorized_res["data"].fetch("unauthorizedEdge") - assert_equal "RelayObject", edge.fetch("node").fetch("__typename") - assert_equal "RelayObjectEdge", edge["__typename"] - end - - it "authorizes _after_ resolving lazy objects" do - query = <<-GRAPHQL - { - a: unauthorizedLazyBox(value: "a") { value } - b: unauthorizedLazyBox(value: "b") { value } - } - GRAPHQL - - unauthorized_res = auth_execute(query) - assert_nil unauthorized_res["data"].fetch("a") - assert_equal "b", unauthorized_res["data"]["b"]["value"] - end - - it "authorizes items in a list" do - query = <<-GRAPHQL - { - unauthorizedListItems { __typename } - } - GRAPHQL - - unauthorized_res = auth_execute(query, context: { hide: true }) - - assert_nil unauthorized_res["data"]["unauthorizedListItems"] - authorized_res = auth_execute(query, context: { hide: false }) - assert_equal 2, authorized_res["data"]["unauthorizedListItems"].size - end - - it "syncs lazy objects from authorized? checks" do - query = <<-GRAPHQL - { - a: unauthorizedLazyCheckBox(value: "a") { value } - b: unauthorizedLazyCheckBox(value: "b") { value } - } - GRAPHQL - - unauthorized_res = auth_execute(query) - assert_nil unauthorized_res["data"].fetch("a") - assert_equal "b", unauthorized_res["data"]["b"]["value"] - # Also, the custom handler was called: - assert_equal ["Unauthorized UnauthorizedCheckBox: \"a\""], unauthorized_res["errors"].map { |e| e["message"] } - end - - it "Works for lazy connections" do - query = <<-GRAPHQL - { - lazyIntegers { edges { node { value } } } - } - GRAPHQL - res = auth_execute(query) - assert_equal [1,2,3], res["data"]["lazyIntegers"]["edges"].map { |e| e["node"]["value"] } - end - - it "Works for eager connections" do - query = <<-GRAPHQL - { - integers { edges { node { value } } } - } - GRAPHQL - res = auth_execute(query) - assert_equal [1,2,3], res["data"]["integers"]["edges"].map { |e| e["node"]["value"] } - end - - it "filters out individual nodes by value" do - query = <<-GRAPHQL - { - integers { edges { node { value } } } - } - GRAPHQL - res = auth_execute(query, context: { exclude_integer: 1 }) - assert_equal [nil,2,3], res["data"]["integers"]["edges"].map { |e| e["node"] && e["node"]["value"] } - assert_equal ["Unauthorized IntegerObject: 1"], res["errors"].map { |e| e["message"] } - end - - it "works with lazy values / interfaces" do - query = <<-GRAPHQL - query($value: String!){ - unauthorizedInterface(value: $value) { - ... on UnauthorizedCheckBox { - value - } - } - } - GRAPHQL - - res = auth_execute(query, variables: { value: "a"}) - assert_nil res["data"]["unauthorizedInterface"] - - res2 = auth_execute(query, variables: { value: "b"}) - assert_equal "b", res2["data"]["unauthorizedInterface"]["value"] - end - - it "works with lazy values / lists of interfaces" do - query = <<-GRAPHQL - { - unauthorizedLazyListInterface { - ... on UnauthorizedCheckBox { - value - } - } - } - GRAPHQL - - res = auth_execute(query) - # An error from two, values from the others - assert_equal ["Unauthorized UnauthorizedCheckBox: \"a\"", "Unauthorized UnauthorizedCheckBox: \"a\""], res["errors"].map { |e| e["message"] } - assert_equal [{"value" => "z"}, {"value" => "z2"}, nil, nil], res["data"]["unauthorizedLazyListInterface"] - end - - describe "with an unauthorized field hook configured" do - it "replaces objects from the unauthorized_object hook" do - query = "{ replacedObject { replaced } }" - res = auth_execute(query, context: { replace_me: true }) - assert_equal true, res["data"]["replacedObject"]["replaced"] - - res = auth_execute(query, context: { replace_me: false }) - assert_equal false, res["data"]["replacedObject"]["replaced"] - end - - it "works when the query hook returns false and there's no root object" do - query = "{ __typename }" - res = auth_execute(query) - assert_equal "Query", res["data"]["__typename"] - - unauth_res = auth_execute(query, context: { query_unauthorized: true }) - assert_nil unauth_res["data"] - assert_equal [{"message"=>"Unauthorized Query: nil"}], unauth_res["errors"] - end - - describe "when the object authorization raises an UnauthorizedFieldError" do - it "receives the raised error" do - query = "{ unauthorizedObject { value } }" - response = auth_execute(query, context: { raise: true }, root_value: :raise_from_object) - assert_equal ["raised authorized object error"], response["errors"].map { |e| e["message"] } - end - end - end - end - - describe "returning false" do - class FalseSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - def self.authorized?(obj, ctx) - false - end - - field :int, Integer, null: false - - def int - 1 - end - end - query(Query) - end - - it "works out-of-the-box" do - res = FalseSchema.execute("{ int }") - assert_nil res.fetch("data") - refute res.key?("errors") - end - end - - describe "overriding authorized_new" do - class AuthorizedNewOverrideSchema < GraphQL::Schema - module LogTrace - def trace(key, data) - if ((q = data[:query]) && (c = q.context)) - c[:log] << key - end - yield - end - ["parse", "lex", "validate", - "analyze_query", "analyze_multiplex", - "execute_query", "execute_multiplex", - "execute_field", "execute_field_lazy", - "authorized", "authorized_lazy", - "resolve_type", "resolve_type_lazy", - "execute_query_lazy"].each do |method_name| - define_method(method_name) do |**data, &block| - trace(method_name, data, &block) - end - end - end - - module CustomIntrospection - class DynamicFields < GraphQL::Introspection::DynamicFields - def self.authorized_new(obj, ctx) - new(obj, ctx) - end - end - end - - class Query < GraphQL::Schema::Object - def self.authorized_new(obj, ctx) - new(obj, ctx) - end - field :int, Integer, null: false - def int; 1; end - end - - query(Query) - introspection(CustomIntrospection) - trace_with(LogTrace) - end - - it "avoids calls to Object.authorized?" do - log = [] - res = AuthorizedNewOverrideSchema.execute("{ __typename int }", context: { log: log }) - assert_equal "Query", res["data"]["__typename"] - assert_equal 1, res["data"]["int"] - expected_log = [ - "validate", - "analyze_query", - "execute_query", - "execute_field", - "execute_field", - "execute_query_lazy" - ] - - assert_equal expected_log, log - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/autoload_spec.rb b/vendor/gems/graphql/spec/graphql/autoload_spec.rb deleted file mode 100644 index 702c209544d..00000000000 --- a/vendor/gems/graphql/spec/graphql/autoload_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" -require "open3" - -describe GraphQL::Autoload do - module LazyModule - extend GraphQL::Autoload - autoload(:LazyClass, "fixtures/lazy_module/lazy_class") - end - - module EagerModule - extend GraphQL::Autoload - autoload(:EagerClass, "fixtures/eager_module/eager_class") - autoload(:OtherEagerClass, "fixtures/eager_module/other_eager_class") - autoload(:NestedEagerModule, "fixtures/eager_module/nested_eager_module") - - def self.eager_load! - super - - NestedEagerModule.eager_load! - end - end - - describe "#autoload" do - it "sets autoload" do - assert LazyModule.const_defined?(:LazyClass) - assert_equal("fixtures/lazy_module/lazy_class", LazyModule.autoload?(:LazyClass)) - LazyModule::LazyClass - assert_nil(LazyModule.autoload?(:LazyClass)) - end - end - - describe "#eager_load!" do - it "eagerly loads autoload entries" do - assert EagerModule.autoload?(:EagerClass) - assert EagerModule.autoload?(:OtherEagerClass) - assert EagerModule.autoload?(:NestedEagerModule) - - EagerModule.eager_load! - - assert_nil(EagerModule.autoload?(:EagerClass)) - assert_nil(EagerModule.autoload?(:OtherEagerClass)) - assert_nil(EagerModule.autoload?(:NestedEagerModule)) - assert_nil(EagerModule::NestedEagerModule.autoload?(:NestedEagerClass)) - assert EagerModule::NestedEagerModule::NestedEagerClass - end - end - - describe "loading nested files in the repo" do - it "can load them individually" do - files_to_load = Dir.glob("lib/**/tracing/*.rb") - assert_equal 28, files_to_load.size, "It found all the expected files" - files_to_load.each do |file| - require_path = file.sub("lib/", "").sub(".rb", "") - stderr_and_stdout, _status = Open3.capture2e("ruby -Ilib -e 'require \"#{require_path}\"'") - assert_equal "", stderr_and_stdout, "It loads #{require_path.inspect} in isolation" - stderr_and_stdout, _status = Open3.capture2e("ruby -Ilib -e 'require \"graphql\"; require \"#{require_path}\"'") - assert_equal "", stderr_and_stdout, "It loads #{require_path.inspect} after loading graphql" - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/backtrace_spec.rb b/vendor/gems/graphql/spec/graphql/backtrace_spec.rb deleted file mode 100644 index 253e03c303d..00000000000 --- a/vendor/gems/graphql/spec/graphql/backtrace_spec.rb +++ /dev/null @@ -1,326 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Backtrace do - class LazyError - def raise_err - raise "Lazy Boom" - end - end - - class ErrorAnalyzer < GraphQL::Analysis::Analyzer - def on_enter_operation_definition(node, parent_node, visitor) - if node.name == "raiseError" - raise GraphQL::AnalysisError, "this should not be wrapped by a backtrace, but instead, returned to the client" - end - end - - def result - end - end - - class NilInspectObject - # Oops, this is evil, but it happens and we should handle it. - def inspect; nil; end - end - - module ErrorTrace - def initialize(required_arg:, **_rest) - super(**_rest) - end - - def execute_multiplex(multiplex:) - super - raise "Instrumentation Boom" - end - end - - let(:resolvers) { - { - "Query" => { - "field1" => Proc.new { :something }, - "field2" => Proc.new { :something }, - "nilInspect" => Proc.new { NilInspectObject.new }, - "nestedList" => Proc.new { [ { thing: { name: "abc" } }, { thing: { name: :boom } } ] }, - }, - "Thing" => { - "name" => Proc.new { |obj| obj[:name] == :boom ? raise("Boom!") : obj[:name] }, - "listField" => Proc.new { :not_a_list }, - "raiseField" => Proc.new { |o, a| raise("This is broken: #{a[:message]}") }, - "executionError" => Proc.new { raise GraphQL::ExecutionError, "Client-facing error" } - }, - "ThingWrapper" => { - "thing" => Proc.new { |obj| obj[:thing] }, - }, - "OtherThing" => { - "strField" => Proc.new { LazyError.new }, - }, - } - } - let(:schema) { - defn = <<-GRAPHQL - type Query { - field1: Thing - field2: OtherThing - nilInspect: Thing - nestedList: [ThingWrapper] - } - - type Thing { - name: String - listField: [OtherThing] - raiseField(message: String!): Int - executionError: Int - } - - type ThingWrapper { - thing: Thing - } - - type OtherThing { - strField: String - } - GRAPHQL - schema_class = GraphQL::Schema.from_definition(defn, default_resolve: resolvers) - schema_class.class_exec { - lazy_resolve(LazyError, :raise_err) - query_analyzer(ErrorAnalyzer) - } - schema_class - } - - let(:backtrace_schema) { - Class.new(schema) do - use GraphQL::Backtrace - end - } - - describe "GraphQL backtrace helpers" do - it "raises a TracedError when enabled" do - assert_raises(GraphQL::Backtrace::TracedError) { - backtrace_schema.execute("query BrokenList { field1 { listField { strField } } }") - } - - assert_raises(GraphQL::Execution::Interpreter::ListResultFailedError) { - schema.execute("query BrokenList { field1 { listField { strField } } }") - } - end - - it "works for objects inside lists" do - assert_raises(GraphQL::Backtrace::TracedError) do - backtrace_schema.execute("{ nestedList { thing { name } } }") - end - end - - it "doesn't wrap GraphQL::ExecutionError" do - assert_equal ["Client-facing error"], backtrace_schema.execute("{ field1 { executionError } }")["errors"].map { |e| e["message"] } - end - - it "annotates crashes from user code" do - err = assert_raises(GraphQL::Backtrace::TracedError) { - backtrace_schema.execute <<-GRAPHQL, root_value: "Root" - query($msg: String = \"Boom\") { - field1 { - boomError: raiseField(message: $msg) - } - } - GRAPHQL - } - - # The original error info is present - assert_instance_of RuntimeError, err.cause - b = err.cause.backtrace - assert_backtrace_includes(b, file: "backtrace_spec.rb", method: "block") - assert_backtrace_includes(b, file: "field.rb", method: "resolve") - assert_backtrace_includes(b, file: "runtime.rb", method: "evaluate_selections") - assert_backtrace_includes(b, file: "interpreter.rb", method: "run_all") - - # GraphQL backtrace is present - expected_graphql_backtrace = [ - "3:13: Thing.raiseField as boomError", - "2:11: Query.field1", - "1:9: query", - ] - assert_equal expected_graphql_backtrace, err.graphql_backtrace - - hash_inspect = { message: "Boom" }.inspect - # The message includes the GraphQL context - rendered_table = [ - 'Loc | Field | Object | ' + "Arguments".ljust(hash_inspect.size) + ' | Result', - '3:13 | Thing.raiseField as boomError | :something | ' + hash_inspect + ' | #', - '2:11 | Query.field1 | "Root" | ' + "{}".ljust(hash_inspect.size) + ' | {}', - '1:9 | query | "Root" | ' + {"msg" => "Boom"}.inspect.ljust(hash_inspect.size) + ' | {field1: {...}}', - ].join("\n") - - assert_includes err.message, "\n" + rendered_table - # The message includes the original error message - assert_includes err.message, "This is broken: Boom" - assert_includes err.message, "spec/graphql/backtrace_spec.rb:49", "It includes the original backtrace" - assert_includes err.message, "more lines" - end - - it "annotates errors from Query#result" do - query_str = "query StrField { field2 { strField } __typename }" - context = { backtrace: true } - query = GraphQL::Query.new(schema, query_str, context: context) - err = assert_raises(GraphQL::Backtrace::TracedError) { - query.result - } - assert_instance_of RuntimeError, err.cause - end - - it "annotates errors inside lazy resolution" do - # Test context-based flag - err = assert_raises(GraphQL::Backtrace::TracedError) { - schema.execute("query StrField { field2 { strField } __typename }", context: { backtrace: true }) - } - assert_instance_of RuntimeError, err.cause - b = err.cause.backtrace - assert_backtrace_includes(b, file: "backtrace_spec.rb", method: "raise_err") - assert_backtrace_includes(b, file: "schema.rb", method: "sync_lazy") - assert_backtrace_includes(b, file: "interpreter.rb", method: "run_all") - - expected_graphql_backtrace = [ - "1:27: OtherThing.strField", - "1:18: Query.field2", - "1:1: query StrField", - ] - - assert_equal(expected_graphql_backtrace, err.graphql_backtrace) - - rendered_table = [ - 'Loc | Field | Object | Arguments | Result', - '1:27 | OtherThing.strField | :something | {} | #', - '1:18 | Query.field2 | nil | {} | {strField: (unresolved)}', - '1:1 | query StrField | nil | {} | {field2: {...}, __typename: "Query"}', - ].join("\n") - assert_includes err.message, rendered_table - end - - it "returns analysis errors to the client" do - res = backtrace_schema.execute("query raiseError { __typename }") - assert_equal "this should not be wrapped by a backtrace, but instead, returned to the client", res["errors"].first["message"] - end - - it "always stringifies the #inspect response" do - # test the schema plugin - err = assert_raises(GraphQL::Backtrace::TracedError) { - backtrace_schema.execute("query { nilInspect { raiseField(message: \"pop!\") } }") - } - - hash_inspect = {message: "pop!"}.inspect # `=>` on Ruby < 3.4 - rendered_table = [ - 'Loc | Field | Object | ' + "Arguments".ljust(hash_inspect.size) + ' | Result', - '1:22 | Thing.raiseField | | ' + hash_inspect + ' | #', - '1:9 | Query.nilInspect | nil | ' + "{}".ljust(hash_inspect.size) + ' | {}', - '1:1 | query | nil | ' + "{}".ljust(hash_inspect.size) + ' | {nilInspect: {...}}', - '', - '' - ].join("\n") - - table = err.message.split("GraphQL Backtrace:\n").last - assert_equal rendered_table, table - end - - it "raises original exception instead of a TracedError when error does not occur during resolving" do - instrumentation_schema = Class.new(schema) do - trace_with(ErrorTrace, required_arg: true) - end - - assert_raises(RuntimeError) { - instrumentation_schema.execute(GraphQL::Introspection::INTROSPECTION_QUERY, context: { backtrace: true }) - } - end - end - - # This will get brittle when execution code moves between files - # but I'm not sure how to be sure that the backtrace contains the right stuff! - def assert_backtrace_includes(backtrace, file:, method:) - includes_tag = if RUBY_VERSION < "3.4" - backtrace.any? { |s| s.include?(file) && s.include?("`" + method) } - elsif method == "block" - backtrace.any? { |s| s.include?(file) && s.include?("'block") } - else - backtrace.any? { |s| s.include?(file) && s.include?("#{method}'") } - end - assert includes_tag, "Backtrace should include #{file} inside method #{method}\n\n#{backtrace.join("\n")}" - end - - it "works with stand-alone validation" do - res = backtrace_schema.validate("{ __typename }") - assert_equal [], res - end - - it "works with stand-alone analysis" do - example_analyzer = Class.new(GraphQL::Analysis::Analyzer) do - def result - :finished - end - end - query = GraphQL::Query.new(backtrace_schema, "{ __typename }") - result = GraphQL::Analysis.analyze_query(query, [example_analyzer]) - assert_equal [:finished], result - end - - it "works with multiplex analysis" do - example_analyzer = Class.new(GraphQL::Analysis::Analyzer) do - def result - :finished - end - end - query = GraphQL::Query.new(backtrace_schema, "{ __typename }") - multiplex = GraphQL::Execution::Multiplex.new( - schema: schema, - queries: [query], - context: {}, - max_complexity: nil, - ) - result = GraphQL::Analysis.analyze_multiplex(multiplex, [example_analyzer]) - assert_equal [:finished], result - end - - it "works with multiplex queries" do - res = backtrace_schema.multiplex([ - { query: 'query { __typename }' }, - { query: 'query { __typename }' }, - ]) - - expected_res = [ - {"data" => { "__typename" => "Query" }}, - {"data" => { "__typename" => "Query" }}, - ] - - assert_equal expected_res, res - end - - it "includes other trace modules when backtrace is active" do - custom_trace = Module.new - schema = Class.new(GraphQL::Schema) do - trace_with(custom_trace) - end - query = GraphQL::Query.new(schema, "{ __typename }", context: { backtrace: true }) - assert_includes query.current_trace.class.ancestors, custom_trace - end - - describe "When validators are used" do - class ValidatorBacktraceSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :greeting, String do - argument :name, String, validates: { length: { minimum: 5 }} - end - - def greeting(name:) - "Hello, #{name}!" - end - end - - query(Query) - use GraphQL::Backtrace - end - - it "works properly" do - assert_equal "Hello, Albert!", ValidatorBacktraceSchema.execute("{ greeting(name: \"Albert\") }")["data"]["greeting"] - assert_equal ["name is too short (minimum is 5)"], ValidatorBacktraceSchema.execute("{ greeting(name: \"Tim\") }")["errors"].map { |e| e["message"] } - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/cop/default_null_true_spec.rb b/vendor/gems/graphql/spec/graphql/cop/default_null_true_spec.rb deleted file mode 100644 index 7f5cdf280a9..00000000000 --- a/vendor/gems/graphql/spec/graphql/cop/default_null_true_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -describe "GraphQL::Cop::DefaultNullTrue" do - include RubocopTestHelpers - - it "finds and autocorrects `null: true` field configurations" do - result = run_rubocop_on("spec/fixtures/cop/null_true.rb") - assert_equal 3, rubocop_errors(result) - - assert_includes result, <<-RUBY - field :name, String, null: true - ^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - null: true, - ^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - field :described, [String, null: true], null: true, description: "Something" - ^^^^^^^^^^ - RUBY - - assert_rubocop_autocorrects_all("spec/fixtures/cop/null_true.rb") - end -end diff --git a/vendor/gems/graphql/spec/graphql/cop/default_required_true_spec.rb b/vendor/gems/graphql/spec/graphql/cop/default_required_true_spec.rb deleted file mode 100644 index e72367c2a85..00000000000 --- a/vendor/gems/graphql/spec/graphql/cop/default_required_true_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -describe "GraphQL::Cop::DefaultRequiredTrue" do - include RubocopTestHelpers - - it "finds and autocorrects `required: true` argument configurations" do - result = run_rubocop_on("spec/fixtures/cop/required_true.rb") - assert_equal 4, rubocop_errors(result) - - assert_includes result, <<-RUBY - argument :id_1, ID, required: true - ^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - required: true, - ^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - argument :id_3, ID, other_config: { something: false, required: true }, required: true, description: \"Something\" - ^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - f.argument(:id_1, ID, required: true) - ^^^^^^^^^^^^^^ - RUBY - - assert_rubocop_autocorrects_all("spec/fixtures/cop/required_true.rb") - end -end diff --git a/vendor/gems/graphql/spec/graphql/cop/field_type_in_block_spec.rb b/vendor/gems/graphql/spec/graphql/cop/field_type_in_block_spec.rb deleted file mode 100644 index f0e2fc22233..00000000000 --- a/vendor/gems/graphql/spec/graphql/cop/field_type_in_block_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -describe "GraphQL::Cop::FieldTypeInBlock" do - include RubocopTestHelpers - - it "finds and autocorrects field corrections with inline types" do - result = run_rubocop_on("spec/fixtures/cop/field_type.rb") - assert_equal 3, rubocop_errors(result) - - assert_includes result, <<-RUBY - field :current_account, Types::Account, null: false, description: "The account of the current viewer" - ^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - field :find_account, Types::Account do - ^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - field(:all_accounts, [Types::Account, null: false]) { - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - RUBY - - assert_rubocop_autocorrects_all("spec/fixtures/cop/field_type.rb") - end - - it "works on small classes" do - result = run_rubocop_on("spec/fixtures/cop/small_field_type.rb") - assert_equal 1, rubocop_errors(result) - end - - it "works with array types" do - result = run_rubocop_on("spec/fixtures/cop/field_type_array.rb") - assert_equal 1, rubocop_errors(result) - - assert_includes result, <<-RUBY - field :bar, [Thing], null: false do - ^^^^^^^ - RUBY - - assert_rubocop_autocorrects_all("spec/fixtures/cop/field_type_array.rb") - end - - it "Works with interfaces" do - result = run_rubocop_on("spec/fixtures/cop/field_type_interface.rb") - assert_equal 1, rubocop_errors(result) - - assert_includes result, <<-RUBY - field :thing, Thing - ^^^^^ - RUBY - - assert_rubocop_autocorrects_all("spec/fixtures/cop/field_type_interface.rb") - end -end diff --git a/vendor/gems/graphql/spec/graphql/cop/root_types_in_block_spec.rb b/vendor/gems/graphql/spec/graphql/cop/root_types_in_block_spec.rb deleted file mode 100644 index b27d6ea71f4..00000000000 --- a/vendor/gems/graphql/spec/graphql/cop/root_types_in_block_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -describe "GraphQL::Cop::RootTypesInBlock" do - include RubocopTestHelpers - - it "finds and autocorrects field corrections with inline types" do - result = run_rubocop_on("spec/fixtures/cop/root_types.rb") - assert_equal 3, rubocop_errors(result) - - assert_includes result, <<-RUBY - query Types::Query - ^^^^^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - mutation Types::Mutation - ^^^^^^^^^^^^^^^^^^^^^^^^ - RUBY - - assert_includes result, <<-RUBY - subscription Types::Subscription - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - RUBY - - assert_rubocop_autocorrects_all("spec/fixtures/cop/root_types.rb") - end -end diff --git a/vendor/gems/graphql/spec/graphql/current_spec.rb b/vendor/gems/graphql/spec/graphql/current_spec.rb deleted file mode 100644 index b4af514c544..00000000000 --- a/vendor/gems/graphql/spec/graphql/current_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Current do - describe "when no query is running" do - it "returns nil for things" do - assert_nil GraphQL::Current.operation_name - assert_nil GraphQL::Current.field - assert_nil GraphQL::Current.dataloader_source_class - end - end - - describe "in queries" do - class CurrentSchema < GraphQL::Schema - class ThingSource < GraphQL::Dataloader::Source - def initialize(context) - @context = context - end - - def fetch(names) - @context[:current_operation_name] << GraphQL::Current.operation_name - @context[:current_source] << GraphQL::Current.dataloader_source_class - names - end - end - class Thing < GraphQL::Schema::Object - field :name, String - - def name - context[:current_field] << GraphQL::Current.field.path - context.dataloader.with(ThingSource, context).load("thing") - end - end - class Query < GraphQL::Schema::Object - field :thing, Thing - - def thing - context[:current_field] << GraphQL::Current.field.path - :thing - end - end - - query(Query) - use GraphQL::Dataloader - end - - it "returns execution information" do - ctx = { - current_field: [], - current_source: [], - current_operation_name: [] - } - - res = CurrentSchema.execute("query GetThingName { thing { name } }", context: ctx) - assert_equal "thing", res["data"]["thing"]["name"] - - assert_equal ["GetThingName"], ctx[:current_operation_name] - assert_equal [CurrentSchema::ThingSource], ctx[:current_source] - assert_equal ["Query.thing", "Thing.name"], ctx[:current_field] - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/dataloader/active_record_association_source_spec.rb b/vendor/gems/graphql/spec/graphql/dataloader/active_record_association_source_spec.rb deleted file mode 100644 index c32610886f3..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader/active_record_association_source_spec.rb +++ /dev/null @@ -1,109 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Dataloader::ActiveRecordAssociationSource do - if testing_rails? - it_dataloads "queries for associated records when the association isn't already loaded" do |d| - my_first_car = ::Album.find(2) - homey = ::Album.find(4) - log = with_active_record_log(colorize: false) do - vulfpeck, chon = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :band).load_all([my_first_car, homey]) - assert_equal "Vulfpeck", vulfpeck.name - assert_equal "Chon", chon.name - end - - assert_includes log, '[["id", 1], ["id", 3]]' - - toms_story = ::Album.find(3) - log = with_active_record_log(colorize: false) do - vulfpeck, chon, toms_story_band = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :band).load_all([my_first_car, homey, toms_story]) - assert_equal "Vulfpeck", vulfpeck.name - assert_equal "Chon", chon.name - assert_equal "Tom's Story", toms_story_band.name - end - - assert_includes log, '[["id", 2]]' - end - - it_dataloads "doesn't load records that are already cached by ActiveRecordSource" do |d| - d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load_all([1,2,3]) - - my_first_car = ::Album.find(2) - homey = ::Album.find(4) - toms_story = ::Album.find(3) - - log = with_active_record_log(colorize: false) do - vulfpeck, chon, toms_story_band = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :band).load_all([my_first_car, homey, toms_story]) - assert_equal "Vulfpeck", vulfpeck.name - assert_equal "Chon", chon.name - assert_equal "Tom's Story", toms_story_band.name - end - - assert_equal "", log - end - - it_dataloads "warms the cache for ActiveRecordSource" do |d| - my_first_car = ::Album.find(2) - homey = ::Album.find(4) - toms_story = ::Album.find(3) - d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :band).load_all([my_first_car, homey, toms_story]) - - log = with_active_record_log(colorize: false) do - d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load_all([1,2,3]) - end - - assert_equal "", log - end - - it_dataloads "doesn't warm the cache when a scope is given" do |d| - my_first_car = ::Album.find(2) - homey = ::Album.find(4) - summerteeth = ::Album.find(6) - results = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :band, ::Band.country).load_all([my_first_car, homey, summerteeth]) - assert_equal [nil, nil, ::Band.find(4)], results - - log = with_active_record_log(colorize: false) do - d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load_all([1,2,4]) - end - - assert_includes log, "SELECT \"bands\".* FROM \"bands\" WHERE \"bands\".\"id\" IN (?, ?, ?) [[\"id\", 1], [\"id\", 2], [\"id\", 4]]" - end - - it_dataloads "doesn't pause when the association is already loaded" do |d| - source = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :band) - assert_equal 0, source.results.size - assert_equal 0, source.pending.size - - my_first_car = ::Album.find(2) - vulfpeck = my_first_car.band - - vulfpeck2 = source.load(my_first_car) - - assert_equal vulfpeck, vulfpeck2 - - assert_equal 0, source.results.size - assert_equal 0, source.pending.size - - my_first_car.reload - vulfpeck3 = source.load(my_first_car) - assert_equal vulfpeck, vulfpeck3 - - assert_equal 1, source.results.size - assert_equal 0, source.pending.size - end - - it_dataloads "raises an error with a non-existent association" do |d| - my_first_car = ::Album.find(2) - source = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :tour_bus) - assert_raises ActiveRecord::AssociationNotFoundError do - source.load(my_first_car) - end - end - - it_dataloads "works with polymorphic associations" do |d| - wilco = ::Band.find(4) - vulfpeck = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :thing).load(wilco) - assert_equal ::Band.find(1), vulfpeck - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/dataloader/active_record_source_spec.rb b/vendor/gems/graphql/spec/graphql/dataloader/active_record_source_spec.rb deleted file mode 100644 index 5154afbd7a7..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader/active_record_source_spec.rb +++ /dev/null @@ -1,109 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Dataloader::ActiveRecordSource do - if testing_rails? - describe "finding by ID" do - it_dataloads "loads once, then returns from a cache when available" do |d| - log = with_active_record_log(colorize: false) do - r1 = d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load(1) - assert_equal "Vulfpeck", r1.name - end - - assert_includes log, 'SELECT "bands".* FROM "bands" WHERE "bands"."id" = ? [["id", 1]]' - - log = with_active_record_log(colorize: false) do - r1 = d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load(1) - assert_equal "Vulfpeck", r1.name - end - - assert_equal "", log - - log = with_active_record_log(colorize: false) do - records = d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load_all([1, 99, 2, 3]) - assert_equal ["Vulfpeck", nil, "Tom's Story", "Chon"], records.map { |r| r&.name } - end - - assert_includes log, '[["id", 99], ["id", 2], ["id", 3]]' - end - - it_dataloads "casts load values to the column type" do |d| - log = with_active_record_log(colorize: false) do - r1 = d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load("1") - assert_equal "Vulfpeck", r1.name - end - - assert_includes log, 'SELECT "bands".* FROM "bands" WHERE "bands"."id" = ? [["id", 1]]' - - log = with_active_record_log(colorize: false) do - d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load(1) - end - - assert_equal "", log - - log = with_active_record_log(colorize: false) do - d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load("1") - end - - assert_equal "", log - end - end - - describe "finding by other columns" do - it_dataloads "uses the alternative primary key" do |d| - log = with_active_record_log(colorize: false) do - r1 = d.with(GraphQL::Dataloader::ActiveRecordSource, AlternativeBand).load("Vulfpeck") - assert_equal "Vulfpeck", r1.name - if Rails::VERSION::STRING > "8" - assert_equal 1, r1["id"] - else - assert_equal 1, r1._read_attribute("id") - end - end - - assert_includes log, 'SELECT "bands".* FROM "bands" WHERE "bands"."name" = ? [["name", "Vulfpeck"]]' - end - - it_dataloads "uses specified find_by columns" do |d| - log = with_active_record_log(colorize: false) do - r1 = d.with(GraphQL::Dataloader::ActiveRecordSource, Band, find_by: :name).load("Chon") - assert_equal "Chon", r1.name - assert_equal 3, r1.id - end - - assert_includes log, 'SELECT "bands".* FROM "bands" WHERE "bands"."name" = ? [["name", "Chon"]]' - end - end - - describe "warming the cache" do - it_dataloads "can receive passed-in objects with a class" do |d| - d.with(GraphQL::Dataloader::ActiveRecordSource, Band).merge({ 100 => Band.find(3) }) - log = with_active_record_log(colorize: false) do - band3 = d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load(100) - assert_equal "Chon", band3.name - assert_equal 3, band3.id - end - - assert_equal "", log - end - - it_dataloads "can infer class of passed-in objects" do |d| - d.merge_records([Band.find(3), Album.find(4)]) - log = with_active_record_log(colorize: false) do - band3 = d.with(GraphQL::Dataloader::ActiveRecordSource, Band).load(3) - assert_equal "Chon", band3.name - - album4 = d.with(GraphQL::Dataloader::ActiveRecordSource, Album).load(4) - assert_equal "Homey", album4.name - end - assert_equal "", log - end - end - - describe "in queries" do - it "loads records with dataload_record" - - it "accepts custom find-by with dataload_record" - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/dataloader/async_dataloader_spec.rb b/vendor/gems/graphql/spec/graphql/dataloader/async_dataloader_spec.rb deleted file mode 100644 index c180c38cdb6..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader/async_dataloader_spec.rb +++ /dev/null @@ -1,358 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" -if RUBY_VERSION >= "3.1.1" - require "async" - describe GraphQL::Dataloader::AsyncDataloader do - class AsyncSchema < GraphQL::Schema - class SleepSource < GraphQL::Dataloader::Source - def initialize(tag = nil) - @tag = tag - end - - def fetch(keys) - max_sleep = keys.max - # t1 = Time.now - # puts "----- SleepSource => #{max_sleep} (from: #{keys})" - sleep(max_sleep) - # puts "----- SleepSource done #{max_sleep} after #{Time.now - t1}" - keys.map { |_k| max_sleep } - end - end - - class WaitForSource < GraphQL::Dataloader::Source - def initialize(tag) - @tag = tag - end - - def fetch(waits) - max_wait = waits.max - # puts "[#{Time.now.to_f}] Waiting #{max_wait} for #{@tag}" - `sleep #{max_wait}` - # puts "[#{Time.now.to_f}] Finished for #{@tag}" - waits.map { |_w| @tag } - end - end - - class KeyWaitForSource < GraphQL::Dataloader::Source - class << self - attr_accessor :fetches - def reset - @fetches = [] - end - end - - def initialize(wait) - @wait = wait - end - - def fetch(keys) - self.class.fetches << keys - sleep(@wait) - keys - end - end - - class FiberLocalContextSource < GraphQL::Dataloader::Source - def fetch(keys) - keys.map { |key| Thread.current[key] } - end - end - - class Sleeper < GraphQL::Schema::Object - field :sleeper, Sleeper, null: false, resolver_method: :sleep do - argument :duration, Float - end - - def sleep(duration:) - context[:key_i] ||= 0 - new_key = context[:key_i] += 1 - dataloader.with(SleepSource, new_key).load(duration) - duration - end - - field :duration, Float, null: false - def duration; object; end - end - - class Waiter < GraphQL::Schema::Object - field :wait_for, Waiter, null: false do - argument :tag, String - argument :wait, Float - end - - def wait_for(tag:, wait:) - dataloader.with(WaitForSource, tag).load(wait) - end - - field :tag, String, null: false - def tag - object - end - end - - class Query < GraphQL::Schema::Object - field :sleep, Float, null: false do - argument :duration, Float - end - - field :sleeper, Sleeper, null: false, resolver_method: :sleep do - argument :duration, Float - end - - def sleep(duration:) - context[:key_i] ||= 0 - new_key = context[:key_i] += 1 - dataloader.with(SleepSource, new_key).load(duration) - duration - end - - field :wait_for, Waiter, null: false do - argument :tag, String - argument :wait, Float - end - - def wait_for(tag:, wait:) - dataloader.with(WaitForSource, tag).load(wait) - end - - class ListWaiter < GraphQL::Schema::Object - field :waiter, Waiter - - def waiter - dataloader.with(KeyWaitForSource, object[:wait]).load(object[:tag]) - end - end - - field :list_waiters, [ListWaiter] do - argument :wait, Float - argument :tags, [String] - end - - def list_waiters(wait:, tags:) - Kernel.sleep(0.1) - tags.map { |t| { tag: t, wait: wait }} - end - - field :fiber_local_context, String do - argument :key, String - end - def fiber_local_context(key:) - dataloader.with(FiberLocalContextSource).load(key) - end - end - - query(Query) - use GraphQL::Dataloader::AsyncDataloader - end - - module AsyncDataloaderAssertions - def self.included(child_class) - child_class.class_eval do - it "works with sources" do - dataloader = GraphQL::Dataloader::AsyncDataloader.new - r1 = dataloader.with(AsyncSchema::SleepSource, :s1).request(0.1) - r2 = dataloader.with(AsyncSchema::SleepSource, :s2).request(0.2) - r3 = dataloader.with(AsyncSchema::SleepSource, :s3).request(0.3) - - v1 = nil - dataloader.append_job { - v1 = r1.load - } - started_at = Time.now - dataloader.run - ended_at = Time.now - assert_equal 0.1, v1 - started_at_2 = Time.now - # These should take no time at all since they're already resolved - v2 = r2.load - v3 = r3.load - ended_at_2 = Time.now - - assert_equal 0.2, v2 - assert_equal 0.3, v3 - assert_in_delta 0.0, started_at_2 - ended_at_2, 0.06, "Already-loaded values returned instantly" - - assert_in_delta 0.3, ended_at - started_at, 0.06, "IO ran in parallel" - end - - it "works with GraphQL" do - started_at = Time.now - res = @schema.execute("{ s1: sleep(duration: 0.1) s2: sleep(duration: 0.2) s3: sleep(duration: 0.3) }") - ended_at = Time.now - assert_equal({"s1"=>0.1, "s2"=>0.2, "s3"=>0.3}, res["data"]) - assert_in_delta 0.3, ended_at - started_at, 0.06, "IO ran in parallel" - end - - it "runs fields by depth" do - query_str = <<-GRAPHQL - { - s1: sleeper(duration: 0.1) { - sleeper(duration: 0.1) { - sleeper(duration: 0.1) { - duration - } - } - } - s2: sleeper(duration: 0.2) { - sleeper(duration: 0.1) { - duration - } - } - s3: sleeper(duration: 0.3) { - duration - } - } - GRAPHQL - started_at = Time.now - res = @schema.execute(query_str) - ended_at = Time.now - - expected_data = { - "s1" => { "sleeper" => { "sleeper" => { "duration" => 0.1 } } }, - "s2" => { "sleeper" => { "duration" => 0.1 } }, - "s3" => { "duration" => 0.3 } - } - assert_equal expected_data, res["data"] - assert_in_delta 0.5, ended_at - started_at, 0.06, "Each depth ran in parallel" - end - - it "runs dataloaders in parallel across branches" do - query_str = <<-GRAPHQL - { - w1: waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "b", wait: 0.2) { - waitFor(tag: "c", wait: 0.2) { - tag - } - } - } - # After the first, these are returned eagerly from cache - w2: waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "a", wait: 0.2) { - tag - } - } - } - w3: waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "b", wait: 0.2) { - waitFor(tag: "d", wait: 0.2) { - tag - } - } - } - w4: waitFor(tag: "e", wait: 0.6) { - tag - } - } - GRAPHQL - started_at = Time.now - res = @schema.execute(query_str) - ended_at = Time.now - - expected_data = { - "w1" => { "waitFor" => { "waitFor" => { "tag" => "c" } } }, - "w2" => { "waitFor" => { "waitFor" => { "tag" => "a" } } }, - "w3" => { "waitFor" => { "waitFor" => { "tag" => "d" } } }, - "w4" => { "tag" => "e" } - } - assert_equal expected_data, res["data"] - # We've basically got two options here: - # - Put all jobs in the same queue (fields and sources), but then you don't get predictable batching. - # - Work one-layer-at-a-time, but then layers can get stuck behind one another. That's what's implemented here. - # assert_in_delta 1.0, ended_at - started_at, 0.06, "Sources were executed in parallel" - end - - it "groups across list items" do - query_str = <<-GRAPHQL - { - listWaiters(wait: 0.2, tags: ["a", "b", "c"]) { - waiter { - tag - } - } - } - GRAPHQL - - t1 = Time.now - result = @schema.execute(query_str) - t2 = Time.now - assert_equal ["a", "b", "c"], result["data"]["listWaiters"].map { |lw| lw["waiter"]["tag"]} - # The field itself waits 0.1 - assert_in_delta 0.3, t2 - t1, 0.06, "Wait was parallel" - assert_equal [["a", "b", "c"]], AsyncSchema::KeyWaitForSource.fetches, "All keys were fetched at once" - end - - it 'copies fiber-local variables over to sources' do - key = 'arbitrary_context' - value = 'test' - Thread.current[key] = value - query_str = <<-GRAPHQL - { - fiberLocalContext(key: "#{key}") - } - GRAPHQL - - result = @schema.execute(query_str) - assert_equal value, result['data']['fiberLocalContext'] - end - end - end - end - - describe "with async" do - before do - @schema = AsyncSchema - AsyncSchema::KeyWaitForSource.reset - end - include AsyncDataloaderAssertions - end - - describe "with perfetto trace turned on" do - class TraceAsyncSchema < AsyncSchema - trace_with GraphQL::Tracing::PerfettoTrace - use GraphQL::Dataloader::AsyncDataloader - end - - before do - @schema = TraceAsyncSchema - AsyncSchema::KeyWaitForSource.reset - end - - include AsyncDataloaderAssertions - include PerfettoSnapshot - - it "produces a trace" do - query_str = <<-GRAPHQL - { - s1: sleeper(duration: 0.1) { - sleeper(duration: 0.1) { - sleeper(duration: 0.1) { - duration - } - } - } - s2: sleeper(duration: 0.2) { - sleeper(duration: 0.1) { - duration - } - } - s3: sleeper(duration: 0.3) { - duration - } - } - GRAPHQL - res = @schema.execute(query_str) - if ENV["DUMP_PERFETTO"] - res.context.query.current_trace.write(file: "perfetto.dump") - end - - json = res.context.query.current_trace.write(file: nil, debug_json: true) - data = JSON.parse(json) - - - check_snapshot(data, "example.json") - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/dataloader/nonblocking_dataloader_spec.rb b/vendor/gems/graphql/spec/graphql/dataloader/nonblocking_dataloader_spec.rb deleted file mode 100644 index 1badce24550..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader/nonblocking_dataloader_spec.rb +++ /dev/null @@ -1,261 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -if Fiber.respond_to?(:scheduler) # Ruby 3+ - describe "GraphQL::Dataloader::NonblockingDataloader" do - class NonblockingSchema < GraphQL::Schema - class SleepSource < GraphQL::Dataloader::Source - def fetch(keys) - max_sleep = keys.max - # t1 = Time.now - # puts "----- SleepSource => #{max_sleep} " - sleep(max_sleep) - # puts "----- SleepSource done #{max_sleep} after #{Time.now - t1}" - keys.map { |_k| max_sleep } - end - end - - class WaitForSource < GraphQL::Dataloader::Source - def initialize(tag) - @tag = tag - end - - def fetch(waits) - max_wait = waits.max - # puts "[#{Time.now.to_f}] Waiting #{max_wait} for #{@tag}" - `sleep #{max_wait}` - # puts "[#{Time.now.to_f}] Finished for #{@tag}" - waits.map { |_w| @tag } - end - end - - class Sleeper < GraphQL::Schema::Object - field :sleeper, Sleeper, null: false, resolver_method: :sleep do - argument :duration, Float - end - - def sleep(duration:) - `sleep #{duration}` - duration - end - - field :duration, Float, null: false - def duration; object; end - end - - class Waiter < GraphQL::Schema::Object - field :wait_for, Waiter, null: false do - argument :tag, String - argument :wait, Float - end - - def wait_for(tag:, wait:) - dataloader.with(WaitForSource, tag).load(wait) - end - - field :tag, String, null: false - def tag - object - end - end - - class Query < GraphQL::Schema::Object - field :sleep, Float, null: false do - argument :duration, Float - end - - field :sleeper, Sleeper, null: false, resolver_method: :sleep do - argument :duration, Float - end - - def sleep(duration:) - `sleep #{duration}` - duration - end - - field :wait_for, Waiter, null: false do - argument :tag, String - argument :wait, Float - end - - def wait_for(tag:, wait:) - dataloader.with(WaitForSource, tag).load(wait) - end - end - - query(Query) - use GraphQL::Dataloader, nonblocking: true - end - - def with_scheduler - Fiber.set_scheduler(scheduler_class.new) - yield - ensure - Fiber.set_scheduler(nil) - end - - module NonblockingDataloaderAssertions - def self.included(child_class) - child_class.class_eval do - - it "runs IO in parallel by default" do - dataloader = GraphQL::Dataloader.new(nonblocking: true) - results = {} - dataloader.append_job { sleep(0.1); results[:a] = 1 } - dataloader.append_job { sleep(0.2); results[:b] = 2 } - dataloader.append_job { sleep(0.3); results[:c] = 3 } - - assert_equal({}, results, "Nothing ran yet") - started_at = Time.now - with_scheduler { dataloader.run } - ended_at = Time.now - - assert_equal({ a: 1, b: 2, c: 3 }, results, "All the jobs ran") - assert_in_delta 0.3, ended_at - started_at, 0.06, "IO ran in parallel" - end - - it "works with sources" do - dataloader = GraphQL::Dataloader.new(nonblocking: true) - r1 = dataloader.with(NonblockingSchema::SleepSource).request(0.1) - r2 = dataloader.with(NonblockingSchema::SleepSource).request(0.2) - r3 = dataloader.with(NonblockingSchema::SleepSource).request(0.3) - - v1 = nil - dataloader.append_job { - v1 = r1.load - } - started_at = Time.now - with_scheduler { dataloader.run } - ended_at = Time.now - assert_equal 0.3, v1 - started_at_2 = Time.now - # These should take no time at all since they're already resolved - v2 = r2.load - v3 = r3.load - ended_at_2 = Time.now - - assert_equal 0.3, v2 - assert_equal 0.3, v3 - assert_in_delta 0.0, started_at_2 - ended_at_2, 0.06, "Already-loaded values returned instantly" - - assert_in_delta 0.3, ended_at - started_at, 0.06, "IO ran in parallel" - end - - it "works with GraphQL" do - started_at = Time.now - res = with_scheduler { - NonblockingSchema.execute("{ s1: sleep(duration: 0.1) s2: sleep(duration: 0.2) s3: sleep(duration: 0.3) }") - } - ended_at = Time.now - assert_equal({"s1"=>0.1, "s2"=>0.2, "s3"=>0.3}, res["data"]) - # assert_in_delta 0.3, ended_at - started_at, 0.06, "IO ran in parallel" - end - - it "nested fields don't wait for slower higher-level fields" do - query_str = <<-GRAPHQL - { - s1: sleeper(duration: 0.1) { - sleeper(duration: 0.1) { - sleeper(duration: 0.1) { - duration - } - } - } - s2: sleeper(duration: 0.2) { - sleeper(duration: 0.1) { - duration - } - } - s3: sleeper(duration: 0.3) { - duration - } - } - GRAPHQL - started_at = Time.now - res = with_scheduler { - NonblockingSchema.execute(query_str) - } - ended_at = Time.now - - expected_data = { - "s1" => { "sleeper" => { "sleeper" => { "duration" => 0.1 } } }, - "s2" => { "sleeper" => { "duration" => 0.1 } }, - "s3" => { "duration" => 0.3 } - } - assert_equal expected_data, res["data"] - # assert_in_delta 0.3, ended_at - started_at, 0.06, "Fields ran without any waiting" - end - - it "runs dataloaders in parallel across branches" do - query_str = <<-GRAPHQL - { - w1: waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "b", wait: 0.2) { - waitFor(tag: "c", wait: 0.2) { - tag - } - } - } - # After the first, these are returned eagerly from cache - w2: waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "a", wait: 0.2) { - tag - } - } - } - w3: waitFor(tag: "a", wait: 0.2) { - waitFor(tag: "b", wait: 0.2) { - waitFor(tag: "d", wait: 0.2) { - tag - } - } - } - w4: waitFor(tag: "e", wait: 0.6) { - tag - } - } - GRAPHQL - started_at = Time.now - res = with_scheduler do - NonblockingSchema.execute(query_str) - end - ended_at = Time.now - - expected_data = { - "w1" => { "waitFor" => { "waitFor" => { "tag" => "c" } } }, - "w2" => { "waitFor" => { "waitFor" => { "tag" => "a" } } }, - "w3" => { "waitFor" => { "waitFor" => { "tag" => "d" } } }, - "w4" => { "tag" => "e" } - } - assert_equal expected_data, res["data"] - # We've basically got two options here: - # - Put all jobs in the same queue (fields and sources), but then you don't get predictable batching. - # - Work one-layer-at-a-time, but then layers can get stuck behind one another. That's what's implemented here. - assert_in_delta 1.0, ended_at - started_at, 0.5, "Sources were executed in parallel" - end - end - end - end - - - describe "With the toy scheduler from Ruby's tests" do - let(:scheduler_class) { ::DummyScheduler } - include NonblockingDataloaderAssertions - end - - if RUBY_ENGINE == "ruby" && !ENV["GITHUB_ACTIONS"] - describe "With libev_scheduler" do - require "libev_scheduler" - let(:scheduler_class) { Libev::Scheduler } - include NonblockingDataloaderAssertions - end - - describe "with evt" do - require "evt" - let(:scheduler_class) { Evt::Scheduler } - include NonblockingDataloaderAssertions - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/dataloader/snapshots/example.json b/vendor/gems/graphql/spec/graphql/dataloader/snapshots/example.json deleted file mode 100644 index 2bdcede08b4..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader/snapshots/example.json +++ /dev/null @@ -1,2709 +0,0 @@ -{ - "packet": [ - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "previousPacketDropped": true, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Main Thread", - "childOrdering": "CHRONOLOGICAL" - }, - "firstPacketOnSequence": true - }, - { - "trustedPacketSequenceId": 101010101010, - "internedData": { - "eventCategories": [ - { - "iid": "10101010101010", - "name": "Dataloader" - }, - { - "iid": "10101010101010", - "name": "Field Execution" - }, - { - "iid": "10101010101010", - "name": "ActiveSupport::Notifications" - }, - { - "iid": "10101010101010", - "name": "Authorized" - }, - { - "iid": "10101010101010", - "name": "Resolve Type" - } - ], - "debugAnnotationNames": [ - { - "iid": "10101010101010", - "name": "object" - }, - { - "iid": "10101010101010", - "name": "arguments" - }, - { - "iid": "10101010101010", - "name": "result" - }, - { - "iid": "10101010101010", - "name": "fetch keys" - } - ], - "debugAnnotationStringValues": [ - { - "iid": "10101010101010", - "str": "KG5pbCk=\n" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Main Fiber", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Allocated Objects", - "parentUuid": "10101010101010", - "counter": {} - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Active Fibers", - "parentUuid": "10101010101010", - "counter": {} - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Resolved Fields", - "parentUuid": "10101010101010", - "counter": {} - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Parse", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "valid?" - }, - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "validate?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Validate\n", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "internedData": { - "debugAnnotationNames": [ - { - "iid": "10101010101010", - "name": "validate?" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "stringValue": "query {\n s1: sleeper(duration: 0.1) {\n sleeper(duration: 0.1) {\n sleeper(duration: 0.1) {\n duration\n }\n }\n }\n s2: sleeper(duration: 0.2) {\n sleeper(duration: 0.1) {\n duration\n }\n }\n s3: sleeper(duration: 0.3) {\n duration\n }\n}", - "name": "query_string" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "Multiplex" - }, - "internedData": { - "debugAnnotationNames": [ - { - "iid": "10101010101010", - "name": "valid?" - }, - { - "iid": "10101010101010", - "name": "query_string" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "name": "analyzers" - }, - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "analyzers_count" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Analysis\n", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "internedData": { - "debugAnnotationNames": [ - { - "iid": "10101010101010", - "name": "analyzers_count" - }, - { - "iid": "10101010101010", - "name": "analyzers" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Dataloader Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Execution Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Exec Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Query" - }, - "internedData": { - "eventNames": [ - { - "iid": "10101010101010", - "name": "Authorize: Query" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s1", - "flowIds": [ - "10101010101010" - ] - }, - "internedData": { - "debugAnnotationNames": [ - { - "iid": "10101010101010", - "name": "authorized?" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Yield" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Execution Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Exec Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s2", - "flowIds": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Yield" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Execution Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Exec Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s3", - "flowIds": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Yield" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Source Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Source Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "arrayValues": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "flowIds": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "internedData": { - "eventNames": [ - { - "iid": "10101010101010", - "name": "AsyncSchema::SleepSource" - } - ], - "debugAnnotationNames": [ - { - "iid": "10101010101010" - }, - { - "iid": "10101010101010", - "name": "@tag" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Source Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Source Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "arrayValues": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "flowIds": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Source Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Source Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "arrayValues": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "flowIds": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Resume" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "dictEntries": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "stringValueIid": "10101010101010", - "name": "object", - "stringValue": null - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s1", - "flowIds": [ - "10101010101010" - ] - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "internedData": { - "debugAnnotationNames": [ - { - "iid": "10101010101010", - "name": "duration\n" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Sleeper" - }, - "internedData": { - "eventNames": [ - { - "iid": "10101010101010", - "name": "Authorize: Sleeper" - } - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s1.sleeper", - "flowIds": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Yield" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Resume" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "dictEntries": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "stringValueIid": "10101010101010", - "name": "object", - "stringValue": null - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s2", - "flowIds": [ - "10101010101010" - ] - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Sleeper" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s2.sleeper", - "flowIds": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Yield" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Resume" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "dictEntries": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "stringValueIid": "10101010101010", - "name": "object", - "stringValue": null - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s3", - "flowIds": [ - "10101010101010" - ] - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Sleeper" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "object" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "s3.duration", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Source Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Source Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "arrayValues": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "flowIds": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Source Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Source Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "arrayValues": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "flowIds": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Resume" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "dictEntries": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "object" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s1.sleeper", - "flowIds": [ - "10101010101010" - ] - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Sleeper" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s1.sleeper.sleeper", - "flowIds": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Yield" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Resume" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "dictEntries": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "object" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s2.sleeper", - "flowIds": [ - "10101010101010" - ] - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Sleeper" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "object" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "s2.sleeper.duration", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Source Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Source Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "intValue": "10101010101010", - "name": "@tag" - }, - { - "nameIid": "10101010101010", - "arrayValues": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "fetch keys" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "flowIds": [ - "10101010101010" - ], - "name": "AsyncSchema::SleepSource" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "name": "Fiber Resume" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "dictEntries": [ - { - "nameIid": "10101010101010", - "doubleValue": 101010101010 - } - ], - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "object" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "name": "s1.sleeper.sleeper", - "flowIds": [ - "10101010101010" - ] - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Authorized" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "boolValue": true, - "name": "authorized?" - } - ], - "type": "TYPE_SLICE_BEGIN", - "nameIid": "10101010101010", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ], - "name": "Authorize: Sleeper" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Field Execution" - ], - "categoryIids": [ - "10101010101010" - ], - "debugAnnotations": [ - { - "nameIid": "10101010101010", - "name": "arguments" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "object" - }, - { - "nameIid": "10101010101010", - "doubleValue": 101010101010, - "name": "result" - } - ], - "type": "TYPE_SLICE_BEGIN", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "s1.sleeper.sleeper.duration", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Dataloader Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010", - "10101010101010" - ], - "name": "Create Execution Fiber", - "extraCounterTrackUuids": [ - "10101010101010", - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "trustedPacketSequenceId": 101010101010, - "sequenceFlags": 101010101010, - "trackDescriptor": { - "uuid": "10101010101010", - "name": "Exec Fiber #1010", - "parentUuid": "10101010101010", - "childOrdering": "CHRONOLOGICAL" - } - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "categories": [ - "Dataloader" - ], - "categoryIids": [ - "10101010101010" - ], - "type": "TYPE_INSTANT", - "trackUuid": "10101010101010", - "extraCounterValues": [ - "10101010101010" - ], - "name": "Fiber Exit", - "extraCounterTrackUuids": [ - "10101010101010" - ] - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_COUNTER", - "trackUuid": "10101010101010", - "counterValue": "10101010101010" - }, - "sequenceFlags": 101010101010 - }, - { - "timestamp": "10101010101010", - "trustedPacketSequenceId": 101010101010, - "trackEvent": { - "type": "TYPE_SLICE_END", - "trackUuid": "10101010101010" - }, - "sequenceFlags": 101010101010 - } - ] -} \ No newline at end of file diff --git a/vendor/gems/graphql/spec/graphql/dataloader/source_spec.rb b/vendor/gems/graphql/spec/graphql/dataloader/source_spec.rb deleted file mode 100644 index 1006e18fffe..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader/source_spec.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Dataloader::Source do - class FailsToLoadSource < GraphQL::Dataloader::Source - def fetch(keys) - dataloader.with(FailsToLoadSource).load_all(keys) - end - end - - it "raises an error when it tries too many times to sync" do - dl = GraphQL::Dataloader.new - dl.append_job { dl.with(FailsToLoadSource).load(1) } - err = assert_raises RuntimeError do - dl.run - end - expected_message = "FailsToLoadSource#sync tried 1000 times to load pending keys ([1]), but they still weren't loaded. There is likely a circular dependency." - assert_equal expected_message, err.message - - dl = GraphQL::Dataloader.new(fiber_limit: 10000) - dl.append_job { dl.with(FailsToLoadSource).load(1) } - err = assert_raises RuntimeError do - dl.run - end - expected_message = "FailsToLoadSource#sync tried 1000 times to load pending keys ([1]), but they still weren't loaded. There is likely a circular dependency or `fiber_limit: 10000` is set too low." - assert_equal expected_message, err.message - end - - it "is pending when waiting for false and nil" do - dl = GraphQL::Dataloader.new - dl.with(FailsToLoadSource).request(nil) - - source_cache = dl.instance_variable_get(:@source_cache) - source_cache_for_source = source_cache[FailsToLoadSource] - - # The value of this changed in Ruby 3.3.3, see https://bugs.ruby-lang.org/issues/20180 - # In previous versions, it was `[{}]`, but now it's `[]` - empty_batch_key = [*[], **{}] - source_inst = source_cache_for_source[empty_batch_key] - assert_instance_of FailsToLoadSource, source_inst, "The cache includes a pending source (#{source_cache_for_source.inspect})" - assert source_inst.pending? - end - - class CustomKeySource < GraphQL::Dataloader::Source - def result_key_for(record) - record[:id] - end - - def fetch(records) - records.map { |r| r[:value] * 10 } - end - end - - it "uses a custom key when configured" do - values = nil - - GraphQL::Dataloader.with_dataloading do |dl| - first_req = dl.with(CustomKeySource).request({ id: 1, value: 10 }) - second_rec = dl.with(CustomKeySource).request({ id: 2, value: 20 }) - third_rec = dl.with(CustomKeySource).request({id: 1, value: 30 }) - - values = [ - first_req.load, - second_rec.load, - third_rec.load - ] - end - - # There wasn't a `300` because the third requested value was de-duped to the first one. - assert_equal [100, 200, 100], values - end - - class NoDataloaderSchema < GraphQL::Schema - class ThingSource < GraphQL::Dataloader::Source - def fetch(ids) - ids.map { |id| { name: "Thing-#{id}" } } - end - end - - class Thing < GraphQL::Schema::Object - field :name, String - end - - class Query < GraphQL::Schema::Object - field :thing, Thing do - argument :id, ID - end - - def thing(id:) - context.dataloader.with(ThingSource).load(id) - end - end - query(Query) - end - - it "raises an error when used without a dataloader" do - err = assert_raises GraphQL::Error do - NoDataloaderSchema.execute("{ thing(id: 1) { name } }") - end - - expected_message = "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources." - assert_equal expected_message, err.message - end -end diff --git a/vendor/gems/graphql/spec/graphql/dataloader_spec.rb b/vendor/gems/graphql/spec/graphql/dataloader_spec.rb deleted file mode 100644 index 5684e4d820a..00000000000 --- a/vendor/gems/graphql/spec/graphql/dataloader_spec.rb +++ /dev/null @@ -1,1710 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" -require "fiber" - -if defined?(Console) && defined?(Async) - Console.logger.disable(Async::Task) -end - -describe GraphQL::Dataloader do - class BatchedCallsCounter - def initialize - @count = 0 - end - - def increment - @count += 1 - end - - attr_reader :count - end - - class FiberSchema < GraphQL::Schema - module Database - extend self - DATA = {} - [ - { id: "1", name: "Wheat", type: "Grain" }, - { id: "2", name: "Corn", type: "Grain" }, - { id: "3", name: "Butter", type: "Dairy" }, - { id: "4", name: "Baking Soda", type: "LeaveningAgent" }, - { id: "5", name: "Cornbread", type: "Recipe", ingredient_ids: ["1", "2", "3", "4"] }, - { id: "6", name: "Grits", type: "Recipe", ingredient_ids: ["2", "3", "7"] }, - { id: "7", name: "Cheese", type: "Dairy" }, - ].each { |d| DATA[d[:id]] = d } - - def log - @log ||= [] - end - - def mget(ids) - log << [:mget, ids.sort] - ids.map { |id| DATA[id] } - end - - def find_by(attribute, values) - log << [:find_by, attribute, values.sort] - values.map { |v| DATA.each_value.find { |dv| dv[attribute] == v } } - end - end - - class DataObject < GraphQL::Dataloader::Source - def initialize(column = :id) - @column = column - end - - def fetch(keys) - if @column == :id - Database.mget(keys) - else - Database.find_by(@column, keys) - end - end - end - - class ToString < GraphQL::Dataloader::Source - def fetch(keys) - keys.map(&:to_s) - end - end - - class NestedDataObject < GraphQL::Dataloader::Source - def fetch(ids) - @dataloader.with(DataObject).load_all(ids) - end - end - - class SlowDataObject < GraphQL::Dataloader::Source - def initialize(batch_key) - # This is just so that I can force different instances in test - @batch_key = batch_key - end - - def fetch(keys) - t = Thread.new { - sleep 0.5 - Database.mget(keys) - } - dataloader.yield - t.value - end - end - - class CustomBatchKeySource < GraphQL::Dataloader::Source - def initialize(batch_key) - @batch_key = batch_key - end - - def self.batch_key_for(batch_key) - Database.log << [:batch_key_for, batch_key] - # Ignore it altogether - :all_the_same - end - - def fetch(keys) - Database.mget(keys) - end - end - - class KeywordArgumentSource < GraphQL::Dataloader::Source - def initialize(column:) - @column = column - end - - def fetch(keys) - if @column == :id - Database.mget(keys) - else - Database.find_by(@column, keys) - end - end - end - - class AuthorizedSource < GraphQL::Dataloader::Source - def initialize(counter) - @counter = counter - end - - def fetch(recipes) - @counter&.increment - recipes.map { true } - end - end - - class ErrorSource < GraphQL::Dataloader::Source - def fetch(ids) - raise GraphQL::Error, "Source error on: #{ids.inspect}" - end - end - - module Ingredient - include GraphQL::Schema::Interface - field :name, String, null: false - field :id, ID, null: false - - field :name_by_scoped_context, String - - def name_by_scoped_context - context[:ingredient_name] - end - end - - class Grain < GraphQL::Schema::Object - implements Ingredient - end - - class LeaveningAgent < GraphQL::Schema::Object - implements Ingredient - end - - class Dairy < GraphQL::Schema::Object - implements Ingredient - end - - class Recipe < GraphQL::Schema::Object - def self.authorized?(obj, ctx) - ctx.dataloader.with(AuthorizedSource, ctx[:batched_calls_counter]).load(obj) - end - - field :name, String, null: false - field :ingredients, [Ingredient], null: false - - def ingredients - ingredients = dataloader.with(DataObject).load_all(object[:ingredient_ids]) - ingredients - end - - field :slow_ingredients, [Ingredient], null: false - - def slow_ingredients - # Use `object[:id]` here to force two different instances of the loader in the test - dataloader.with(SlowDataObject, object[:id]).load_all(object[:ingredient_ids]) - end - end - - class Query < GraphQL::Schema::Object - field :recipes, [Recipe], null: false - - def recipes - Database.mget(["5", "6"]) - end - - field :ingredient, Ingredient do - argument :id, ID - end - - def ingredient(id:) - dataloader.with(DataObject).load(id) - end - - field :ingredient_by_name, Ingredient do - argument :name, String - end - - def ingredient_by_name(name:) - ing = dataloader.with(DataObject, :name).load(name) - context.scoped_set!(:ingredient_name, "Scoped:#{name}") - ing - end - - field :nested_ingredient, Ingredient do - argument :id, ID - end - - def nested_ingredient(id:) - dataloader.with(NestedDataObject).load(id) - end - - field :slow_recipe, Recipe do - argument :id, ID - end - - def slow_recipe(id:) - dataloader.with(SlowDataObject, id).load(id) - end - - field :recipe, Recipe do - argument :id, ID, loads: Recipe, as: :recipe - end - - def recipe(recipe:) - recipe - end - - field :recipe_by_id_using_load, Recipe do - argument :id, ID, required: false - end - - def recipe_by_id_using_load(id:) - dataloader.with(DataObject).load(id) - end - - field :recipes_by_id_using_load_all, [Recipe] do - argument :ids, [ID, null: true] - end - - def recipes_by_id_using_load_all(ids:) - dataloader.with(DataObject).load_all(ids) - end - - field :recipes_by_id, [Recipe] do - argument :ids, [ID], loads: Recipe, as: :recipes - end - - def recipes_by_id(recipes:) - recipes - end - - field :key_ingredient, Ingredient do - argument :id, ID - end - - def key_ingredient(id:) - dataloader.with(KeywordArgumentSource, column: :id).load(id) - end - - class RecipeIngredientInput < GraphQL::Schema::InputObject - argument :id, ID - argument :ingredient_number, Int - end - - field :recipe_ingredient, Ingredient do - argument :recipe, RecipeIngredientInput - end - - def recipe_ingredient(recipe:) - recipe_object = dataloader.with(DataObject).load(recipe[:id]) - ingredient_idx = recipe[:ingredient_number] - 1 - ingredient_id = recipe_object[:ingredient_ids][ingredient_idx] - dataloader.with(DataObject).load(ingredient_id) - end - - field :common_ingredients, [Ingredient] do - argument :recipe_1_id, ID - argument :recipe_2_id, ID - end - - def common_ingredients(recipe_1_id:, recipe_2_id:) - req1 = dataloader.with(DataObject).request(recipe_1_id) - req2 = dataloader.with(DataObject).request(recipe_2_id) - recipe1 = req1.load - recipe2 = req2.load - common_ids = recipe1[:ingredient_ids] & recipe2[:ingredient_ids] - dataloader.with(DataObject).load_all(common_ids) - end - - field :common_ingredients_with_load, [Ingredient], null: false do - argument :recipe_1_id, ID, loads: Recipe - argument :recipe_2_id, ID, loads: Recipe - end - - def common_ingredients_with_load(recipe_1:, recipe_2:) - common_ids = recipe_1[:ingredient_ids] & recipe_2[:ingredient_ids] - dataloader.with(DataObject).load_all(common_ids) - end - - field :common_ingredients_from_input_object, [Ingredient], null: false do - class CommonIngredientsInput < GraphQL::Schema::InputObject - argument :recipe_1_id, ID, loads: Recipe - argument :recipe_2_id, ID, loads: Recipe - end - argument :input, CommonIngredientsInput - end - - def common_ingredients_from_input_object(input:) - recipe_1 = input[:recipe_1] - recipe_2 = input[:recipe_2] - common_ids = recipe_1[:ingredient_ids] & recipe_2[:ingredient_ids] - dataloader.with(DataObject).load_all(common_ids) - end - - field :ingredient_with_custom_batch_key, Ingredient do - argument :id, ID - argument :batch_key, String - end - - def ingredient_with_custom_batch_key(id:, batch_key:) - dataloader.with(CustomBatchKeySource, batch_key).load(id) - end - - field :recursive_ingredient_name, String do - argument :id, ID - end - - def recursive_ingredient_name(id:) - res = context.schema.execute("{ ingredient(id: #{id}) { name } }") - res["data"]["ingredient"]["name"] - end - - field :test_error, String do - argument :source, Boolean, required: false, default_value: false - end - - def test_error(source:) - if source - dataloader.with(ErrorSource).load(1) - else - raise GraphQL::Error, "Field error" - end - end - - class LookaheadInput < GraphQL::Schema::InputObject - argument :id, ID - argument :batch_key, String - end - - field :lookahead_ingredient, Ingredient, extras: [:lookahead] do - argument :input, LookaheadInput - end - - - def lookahead_ingredient(input:, lookahead:) - lookahead.arguments # forces a dataloader.run_isolated call - dataloader.with(CustomBatchKeySource, input[:batch_key]).load(input[:id]) - end - end - - query(Query) - - class Mutation1 < GraphQL::Schema::Mutation - argument :argument_1, String, prepare: ->(val, ctx) { - raise FieldTestError - } - field :value, String - def resolve(argument_1:) - { value: argument_1 } - end - end - - class Mutation2 < GraphQL::Schema::Mutation - argument :argument_2, String, prepare: ->(val, ctx) { - raise FieldTestError - } - field :value, String - def resolve(argument_2:) - { value: argument_2 } - end - end - - class Mutation3 < GraphQL::Schema::Mutation - argument :label, String - type String - - def resolve(label:) - log = context[:mutation_log] ||= [] - log << "begin #{label}" - dataloader.with(DataObject).load(1) - log << "end #{label}" - label - end - end - - class GetCache < GraphQL::Schema::Mutation - type String - def resolve - dataloader.with(ToString).load(1) - end - end - - class Mutation < GraphQL::Schema::Object - field :mutation_1, mutation: Mutation1 - field :mutation_2, mutation: Mutation2 - field :mutation_3, mutation: Mutation3 - field :set_cache, String do - argument :input, String - end - - def set_cache(input:) - dataloader.with(ToString).merge({ 1 => input }) - input - end - - field :get_cache, mutation: GetCache - end - - mutation(Mutation) - - def self.object_from_id(id, ctx) - ctx.dataloader.with(DataObject).load(id) - end - - def self.resolve_type(type, obj, ctx) - get_type(obj[:type]) - end - - orphan_types(Grain, Dairy, Recipe, LeaveningAgent) - use GraphQL::Dataloader - - class FieldTestError < StandardError; end - - rescue_from(FieldTestError) do |err, obj, args, ctx, field| - errs = ctx[:errors] ||= [] - errs << "FieldTestError @ #{ctx[:current_path]}, #{field.path} / #{ctx[:current_field].path}" - nil - end - end - - class UsageAnalyzer < GraphQL::Analysis::Analyzer - def initialize(query) - @query = query - @fields = Set.new - end - - def on_enter_field(node, parent, visitor) - args = @query.arguments_for(node, visitor.field_definition) - # This bug has been around for a while, - # see https://github.com/rmosolgo/graphql-ruby/issues/3321 - if args.is_a?(GraphQL::Execution::Lazy) - args = args.value - end - @fields << [node.name, args.keys] - end - - def result - @fields - end - end - - def database_log - FiberSchema::Database.log - end - - before do - database_log.clear - end - - ALL_FIBERS = [] - - - class PartsSchema < GraphQL::Schema - class FieldSource < GraphQL::Dataloader::Source - DATA = [ - {"id" => 1, "name" => "a"}, - {"id" => 2, "name" => "b"}, - {"id" => 3, "name" => "c"}, - {"id" => 4, "name" => "d"}, - ] - def fetch(fields) - @previously_fetched ||= Set.new - fields.each do |f| - if !@previously_fetched.add?(f) - raise "Duplicate fetch for #{f.inspect}" - end - end - Array.new(fields.size, DATA) - end - end - - class StringFilter < GraphQL::Schema::InputObject - argument :equal_to_any_of, [String] - end - - class ComponentFilter < GraphQL::Schema::InputObject - argument :name, StringFilter - end - - class FetchObjects < GraphQL::Schema::Resolver - argument :filter, ComponentFilter, required: false - def resolve(**_kwargs) - context.dataloader.with(FieldSource).load("#{field.path}/#{object&.fetch("id")}") - end - end - - class Component < GraphQL::Schema::Object - field :name, String - end - - class Part < GraphQL::Schema::Object - field :components, [Component], resolver: FetchObjects - end - - class Manufacturer < GraphQL::Schema::Object - field :parts, [Part], resolver: FetchObjects - end - - class Query < GraphQL::Schema::Object - field :manufacturers, [Manufacturer], resolver: FetchObjects - end - - query(Query) - use GraphQL::Dataloader - end - - module DataloaderAssertions - module FiberCounting - class << self - attr_accessor :starting_fiber_count, :last_spawn_fiber_count, :last_max_fiber_count - - def current_fiber_count - count_active_fibers - starting_fiber_count - end - - def count_active_fibers - GC.start - ObjectSpace.each_object(Fiber).count - end - end - - def initialize(*args, **kwargs, &block) - super - FiberCounting.starting_fiber_count = FiberCounting.count_active_fibers - FiberCounting.last_max_fiber_count = 0 - FiberCounting.last_spawn_fiber_count = 0 - end - - def spawn_fiber - result = super - update_fiber_counts - result - end - - def spawn_source_task(parent_task, condition, trace) - result = super - if result - update_fiber_counts - end - result - end - - private - - def update_fiber_counts - FiberCounting.last_spawn_fiber_count += 1 - current_count = FiberCounting.current_fiber_count - if current_count > FiberCounting.last_max_fiber_count - FiberCounting.last_max_fiber_count = current_count - end - end - end - - def self.included(child_class) - child_class.class_eval do - let(:schema) { make_schema_from(FiberSchema) } - let(:parts_schema) { make_schema_from(PartsSchema) } - - it "Works with request(...)" do - res = schema.execute <<-GRAPHQL - { - commonIngredients(recipe1Id: 5, recipe2Id: 6) { - name - } - } - GRAPHQL - - expected_data = { - "data" => { - "commonIngredients" => [ - { "name" => "Corn" }, - { "name" => "Butter" }, - ] - } - } - assert_equal expected_data, res - assert_equal [[:mget, ["5", "6"]], [:mget, ["2", "3"]]], database_log - end - - it "runs mutations sequentially" do - res = schema.execute <<-GRAPHQL - mutation { - first: mutation3(label: "first") - second: mutation3(label: "second") - } - GRAPHQL - - assert_equal({ "first" => "first", "second" => "second" }, res["data"]) - assert_equal ["begin first", "end first", "begin second", "end second"], res.context[:mutation_log] - end - - it "clears the cache between mutations" do - res = schema.execute <<-GRAPHQL - mutation { - setCache(input: "Salad") - getCache - } - GRAPHQL - - assert_equal({"setCache" => "Salad", "getCache" => "1"}, res["data"]) - end - - it "batch-loads" do - res = schema.execute <<-GRAPHQL - { - i1: ingredient(id: 1) { id name } - i2: ingredient(id: 2) { name } - r1: recipe(id: 5) { - # This loads Ingredients 3 and 4 - ingredients { name } - } - # This loads Ingredient 7 - ri1: recipeIngredient(recipe: { id: 6, ingredientNumber: 3 }) { - name - } - } - GRAPHQL - - expected_data = { - "i1" => { "id" => "1", "name" => "Wheat" }, - "i2" => { "name" => "Corn" }, - "r1" => { - "ingredients" => [ - { "name" => "Wheat" }, - { "name" => "Corn" }, - { "name" => "Butter" }, - { "name" => "Baking Soda" }, - ], - }, - "ri1" => { - "name" => "Cheese", - }, - } - assert_equal(expected_data, res["data"]) - - expected_log = [ - [:mget, [ - "1", "2", # The first 2 ingredients - "5", # The first recipe - "6", # recipeIngredient recipeId - ]], - [:mget, [ - "7", # recipeIngredient ingredient_id - ]], - [:mget, [ - "3", "4", # The two unfetched ingredients the first recipe - ]], - ] - assert_equal expected_log, database_log - end - - it "caches and batch-loads across a multiplex" do - context = {} - result = schema.multiplex([ - { query: "{ i1: ingredient(id: 1) { name } i2: ingredient(id: 2) { name } }", }, - { query: "{ i2: ingredient(id: 2) { name } r1: recipe(id: 5) { ingredients { name } } }", }, - { query: "{ i1: ingredient(id: 1) { name } ri1: recipeIngredient(recipe: { id: 5, ingredientNumber: 2 }) { name } }", }, - ], context: context) - - expected_result = [ - {"data"=>{"i1"=>{"name"=>"Wheat"}, "i2"=>{"name"=>"Corn"}}}, - {"data"=>{"i2"=>{"name"=>"Corn"}, "r1"=>{"ingredients"=>[{"name"=>"Wheat"}, {"name"=>"Corn"}, {"name"=>"Butter"}, {"name"=>"Baking Soda"}]}}}, - {"data"=>{"i1"=>{"name"=>"Wheat"}, "ri1"=>{"name"=>"Corn"}}}, - ] - assert_equal expected_result, result - expected_log = [ - [:mget, ["1", "2", "5"]], - [:mget, ["3", "4"]], - ] - assert_equal expected_log, database_log - end - - it "works with calls within sources" do - res = schema.execute <<-GRAPHQL - { - i1: nestedIngredient(id: 1) { name } - i2: nestedIngredient(id: 2) { name } - } - GRAPHQL - - expected_data = { "i1" => { "name" => "Wheat" }, "i2" => { "name" => "Corn" } } - assert_equal expected_data, res["data"] - assert_equal [[:mget, ["1", "2"]]], database_log - end - - it "works with batch parameters" do - res = schema.execute <<-GRAPHQL - { - i1: ingredientByName(name: "Butter") { id } - i2: ingredientByName(name: "Corn") { id } - i3: ingredientByName(name: "Gummi Bears") { id } - } - GRAPHQL - - expected_data = { - "i1" => { "id" => "3" }, - "i2" => { "id" => "2" }, - "i3" => nil, - } - assert_equal expected_data, res["data"] - assert_equal [[:find_by, :name, ["Butter", "Corn", "Gummi Bears"]]], database_log - end - - it "works with manual parallelism" do - start = Time.now.to_f - schema.execute <<-GRAPHQL - { - i1: slowRecipe(id: 5) { slowIngredients { name } } - i2: slowRecipe(id: 6) { slowIngredients { name } } - } - GRAPHQL - finish = Time.now.to_f - - # For some reason Async adds some overhead to this manual parallelism. - # But who cares, you wouldn't use Thread#join in that case - delta = schema.dataloader_class == GraphQL::Dataloader ? 0.1 : 0.5 - # Each load slept for 0.5 second, so sequentially, this would have been 2s sequentially - assert_in_delta 1, finish - start, delta, "Load threads are executed in parallel" - expected_log = [ - # These were separated because of different recipe IDs: - [:mget, ["5"]], - [:mget, ["6"]], - # These were cached separately because of different recipe IDs: - [:mget, ["2", "3", "7"]], - [:mget, ["1", "2", "3", "4"]], - ] - # Sort them because threads may have returned in slightly different order - assert_equal expected_log.sort, database_log.sort - end - - it "Works with multiple-field selections and __typename" do - query_str = <<-GRAPHQL - { - ingredient(id: 1) { - __typename - name - } - } - GRAPHQL - - res = schema.execute(query_str) - expected_data = { - "ingredient" => { - "__typename" => "Grain", - "name" => "Wheat", - } - } - assert_equal expected_data, res["data"] - end - - it "Works when the parent field didn't yield" do - query_str = <<-GRAPHQL - { - recipes { - ingredients { - name - } - } - } - GRAPHQL - - res = schema.execute(query_str) - expected_data = { - "recipes" =>[ - { "ingredients" => [ - {"name"=>"Wheat"}, - {"name"=>"Corn"}, - {"name"=>"Butter"}, - {"name"=>"Baking Soda"} - ]}, - { "ingredients" => [ - {"name"=>"Corn"}, - {"name"=>"Butter"}, - {"name"=>"Cheese"} - ]}, - ] - } - assert_equal expected_data, res["data"] - - expected_log = [ - [:mget, ["5", "6"]], - [:mget, ["1", "2", "3", "4", "7"]], - ] - assert_equal expected_log, database_log - end - - it "loads arguments in batches, even with request" do - query_str = <<-GRAPHQL - { - commonIngredientsWithLoad(recipe1Id: 5, recipe2Id: 6) { - name - } - } - GRAPHQL - - res = schema.execute(query_str) - expected_data = { - "commonIngredientsWithLoad" => [ - {"name"=>"Corn"}, - {"name"=>"Butter"}, - ] - } - assert_equal expected_data, res["data"] - - expected_log = [ - [:mget, ["5", "6"]], - [:mget, ["2", "3"]], - ] - assert_equal expected_log, database_log - end - - it "works with sources that use keyword arguments in the initializer" do - query_str = <<-GRAPHQL - { - keyIngredient(id: 1) { - __typename - name - } - } - GRAPHQL - - res = schema.execute(query_str) - expected_data = { - "keyIngredient" => { - "__typename" => "Grain", - "name" => "Wheat", - } - } - assert_equal expected_data, res["data"] - end - - it "Works with analyzing arguments with `loads:`, even with .request" do - query_str = <<-GRAPHQL - { - commonIngredientsWithLoad(recipe1Id: 5, recipe2Id: 6) { - name - } - } - GRAPHQL - query = GraphQL::Query.new(schema, query_str) - results = GraphQL::Analysis.analyze_query(query, [UsageAnalyzer]) - expected_results = [ - ["commonIngredientsWithLoad", [:recipe_1, :recipe_2]], - ["name", []], - ] - normalized_results = results.first.to_a - normalized_results.each do |key, values| - values.sort! - end - assert_equal expected_results, results.first.to_a - end - - it "Works with input objects, load and request" do - query_str = <<-GRAPHQL - { - commonIngredientsFromInputObject(input: { recipe1Id: 5, recipe2Id: 6 }) { - name - } - } - GRAPHQL - res = schema.execute(query_str) - expected_data = { - "commonIngredientsFromInputObject" => [ - {"name"=>"Corn"}, - {"name"=>"Butter"}, - ] - } - assert_equal expected_data, res["data"] - - expected_log = [ - [:mget, ["5", "6"]], - [:mget, ["2", "3"]], - ] - assert_equal expected_log, database_log - end - - it "batches calls in .authorized?" do - query_str = "{ r1: recipe(id: 5) { name } r2: recipe(id: 6) { name } }" - context = { batched_calls_counter: BatchedCallsCounter.new } - schema.execute(query_str, context: context) - assert_equal 1, context[:batched_calls_counter].count - - query_str = "{ recipes { name } }" - context = { batched_calls_counter: BatchedCallsCounter.new } - schema.execute(query_str, context: context) - assert_equal 1, context[:batched_calls_counter].count - - query_str = "{ recipesById(ids: [5, 6]) { name } }" - context = { batched_calls_counter: BatchedCallsCounter.new } - schema.execute(query_str, context: context) - assert_equal 1, context[:batched_calls_counter].count - end - - it "works when passing nil into source" do - query_str = <<-GRAPHQL - query($id: ID) { - recipe: recipeByIdUsingLoad(id: $id) { - name - } - } - GRAPHQL - res = schema.execute(query_str, variables: { id: nil }) - expected_data = { "recipe" => nil } - assert_equal expected_data, res["data"] - - query_str = <<-GRAPHQL - query($ids: [ID]!) { - recipes: recipesByIdUsingLoadAll(ids: $ids) { - name - } - } - GRAPHQL - res = schema.execute(query_str, variables: { ids: [nil] }) - expected_data = { "recipes" => nil } - assert_equal expected_data, res["data"] - end - - it "Works with input objects using variables, load and request" do - query_str = <<-GRAPHQL - query($input: CommonIngredientsInput!) { - commonIngredientsFromInputObject(input: $input) { - name - } - } - GRAPHQL - res = schema.execute(query_str, variables: { input: { recipe1Id: 5, recipe2Id: 6 }}) - expected_data = { - "commonIngredientsFromInputObject" => [ - {"name"=>"Corn"}, - {"name"=>"Butter"}, - ] - } - assert_equal expected_data, res["data"] - - expected_log = [ - [:mget, ["5", "6"]], - [:mget, ["2", "3"]], - ] - assert_equal expected_log, database_log - end - - it "supports general usage" do - a = b = c = nil - - res = GraphQL::Dataloader.with_dataloading { |dataloader| - dataloader.append_job { - a = dataloader.with(FiberSchema::DataObject).load("1") - } - - dataloader.append_job { - b = dataloader.with(FiberSchema::DataObject).load("1") - } - - dataloader.append_job { - r1 = dataloader.with(FiberSchema::DataObject).request("2") - r2 = dataloader.with(FiberSchema::DataObject).request("3") - c = [ - r1.load, - r2.load - ] - } - - :finished - } - - assert_equal :finished, res - assert_equal [[:mget, ["1", "2", "3"]]], database_log - assert_equal "Wheat", a[:name] - assert_equal "Wheat", b[:name] - assert_equal ["Corn", "Butter"], c.map { |d| d[:name] } - end - - it "works with scoped context" do - query_str = <<-GRAPHQL - { - i1: ingredientByName(name: "Corn") { nameByScopedContext } - i2: ingredientByName(name: "Wheat") { nameByScopedContext } - i3: ingredientByName(name: "Butter") { nameByScopedContext } - } - GRAPHQL - - expected_data = { - "i1" => { "nameByScopedContext" => "Scoped:Corn" }, - "i2" => { "nameByScopedContext" => "Scoped:Wheat" }, - "i3" => { "nameByScopedContext" => "Scoped:Butter" }, - } - result = schema.execute(query_str) - assert_equal expected_data, result["data"] - end - - it "works when the schema calls itself" do - result = schema.execute("{ recursiveIngredientName(id: 1) }") - assert_equal "Wheat", result["data"]["recursiveIngredientName"] - end - - it "uses .batch_key_for in source classes" do - query_str = <<-GRAPHQL - { - i1: ingredientWithCustomBatchKey(id: 1, batchKey: "abc") { name } - i2: ingredientWithCustomBatchKey(id: 2, batchKey: "def") { name } - i3: ingredientWithCustomBatchKey(id: 3, batchKey: "ghi") { name } - } - GRAPHQL - - res = schema.execute(query_str) - expected_data = { "i1" => { "name" => "Wheat" }, "i2" => { "name" => "Corn" }, "i3" => { "name" => "Butter" } } - assert_equal expected_data, res["data"] - expected_log = [ - # Each batch key is given to the source class: - [:batch_key_for, "abc"], - [:batch_key_for, "def"], - [:batch_key_for, "ghi"], - # But since they return the same value, - # all keys are fetched in the same call: - [:mget, ["1", "2", "3"]] - ] - assert_equal expected_log, database_log - end - - it "uses cached values from .merge" do - query_str = "{ ingredient(id: 1) { id name } }" - assert_equal "Wheat", schema.execute(query_str)["data"]["ingredient"]["name"] - assert_equal [[:mget, ["1"]]], database_log - database_log.clear - - dataloader = schema.dataloader_class.new - data_source = dataloader.with(FiberSchema::DataObject) - data_source.merge({ "1" => { name: "Kamut", id: "1", type: "Grain" } }) - assert_equal "Kamut", data_source.load("1")[:name] - res = schema.execute(query_str, context: { dataloader: dataloader }) - assert_equal [], database_log - assert_equal "Kamut", res["data"]["ingredient"]["name"] - end - - it "raises errors from fields" do - err = assert_raises GraphQL::Error do - schema.execute("{ testError }") - end - - assert_equal "Field error", err.message - end - - it "raises errors from sources" do - err = assert_raises GraphQL::Error do - schema.execute("{ testError(source: true) }") - end - - assert_equal "Source error on: [1]", err.message - end - - it "works with very very large queries" do - query_str = "{".dup - fields = 1100 - fields.times do |i| - query_str << "\n field#{i}: lookaheadIngredient(input: { id: 1, batchKey: \"key-#{i}\"}) { name }" - end - query_str << "\n}" - GC.start - GC.disable - old_fibers = [] - ObjectSpace.each_object(Fiber) do |f| - old_fibers << f - end - res = schema.execute(query_str) - assert_equal fields, res["data"].keys.size - all_fibers = [] - ObjectSpace.each_object(Fiber) do |f| - all_fibers << f - end - new_fibers = all_fibers - old_fibers - if new_fibers.any?(&:alive?) - message = "Alive fibers:\n\n".dup - new_fibers.select(&:alive?).each do |f| - message << " - #{f.inspect}\n" - f.backtrace.each do |line| - message << " #{line}\n" - end - end - puts message - end - assert_equal [false], new_fibers.map(&:alive?).uniq - ensure - GC.enable - end - - it "doesn't perform duplicate source fetches" do - query = <<~QUERY - query { - manufacturers { - parts { - components(filter: {name: {equalToAnyOf: ["c1", "c2", "c3"]}}) { - name - } - } - } - } - QUERY - response = parts_schema.execute(query).to_h - assert_equal [4, 4, 4, 4], response["data"]["manufacturers"].map { |parts_obj| parts_obj["parts"].size } - end - - describe "fiber_limit" do - def assert_last_max_fiber_count(expected_last_max_fiber_count, message = nil) - if FiberCounting.last_max_fiber_count == (expected_last_max_fiber_count + 1) - # TODO why does this happen sometimes? - warn "AsyncDataloader had +1 last_max_fiber_count" - assert_equal (expected_last_max_fiber_count + 1), FiberCounting.last_max_fiber_count, message - else - assert_equal expected_last_max_fiber_count, FiberCounting.last_max_fiber_count, message - end - end - - it "respects a configured fiber_limit" do - query_str = <<-GRAPHQL - { - recipes { - ingredients { - name - } - } - nestedIngredient(id: 2) { - name - } - keyIngredient(id: 4) { - name - } - commonIngredientsWithLoad(recipe1Id: 5, recipe2Id: 6) { - name - } - } - GRAPHQL - - fiber_counting_dataloader_class = Class.new(schema.dataloader_class) - fiber_counting_dataloader_class.include(FiberCounting) - - res = schema.execute(query_str, context: { dataloader: fiber_counting_dataloader_class.new }) - assert_nil res.context.dataloader.fiber_limit - assert_equal 12, FiberCounting.last_spawn_fiber_count - assert_last_max_fiber_count(9, "No limit works as expected") - - res = schema.execute(query_str, context: { dataloader: fiber_counting_dataloader_class.new(fiber_limit: 4) }) - assert_equal 4, res.context.dataloader.fiber_limit - assert_equal 14, FiberCounting.last_spawn_fiber_count - assert_last_max_fiber_count(4, "Limit of 4 works as expected") - - res = schema.execute(query_str, context: { dataloader: fiber_counting_dataloader_class.new(fiber_limit: 6) }) - assert_equal 6, res.context.dataloader.fiber_limit - assert_equal 10, FiberCounting.last_spawn_fiber_count - assert_last_max_fiber_count(6, "Limit of 6 works as expected") - end - - it "accepts a default fiber_limit config" do - schema = Class.new(FiberSchema) do - use GraphQL::Dataloader, fiber_limit: 4 - end - query_str = <<-GRAPHQL - { - recipes { - ingredients { - name - } - } - nestedIngredient(id: 2) { - name - } - keyIngredient(id: 4) { - name - } - commonIngredientsWithLoad(recipe1Id: 5, recipe2Id: 6) { - name - } - } - GRAPHQL - res = schema.execute(query_str) - assert_equal 4, res.context.dataloader.fiber_limit - assert_nil res["errors"] - end - - it "requires at least three fibers" do - dl = GraphQL::Dataloader.new(fiber_limit: 2) - err = assert_raises ArgumentError do - dl.run - end - assert_equal "Dataloader fiber limit is too low (2), it must be at least 4", err.message - end - end - end - end - end - - def make_schema_from(schema) - schema - end - - include DataloaderAssertions - - if RUBY_VERSION >= "3.1.1" - require "async" - describe "AsyncDataloader" do - def make_schema_from(schema) - Class.new(schema) { - use GraphQL::Dataloader::AsyncDataloader - } - end - - include DataloaderAssertions - end - end - - if Fiber.respond_to?(:scheduler) - describe "nonblocking: true" do - def make_schema_from(schema) - Class.new(schema) do - use GraphQL::Dataloader, nonblocking: true - end - end - - before do - Fiber.set_scheduler(::DummyScheduler.new) - end - - after do - Fiber.set_scheduler(nil) - end - - include DataloaderAssertions - end - - if RUBY_ENGINE == "ruby" && !ENV["GITHUB_ACTIONS"] - describe "nonblocking: true with libev" do - require "libev_scheduler" - def make_schema_from(schema) - Class.new(schema) do - use GraphQL::Dataloader, nonblocking: true - end - end - - before do - Fiber.set_scheduler(Libev::Scheduler.new) - end - - after do - Fiber.set_scheduler(nil) - end - - include DataloaderAssertions - end - end - end - - describe "example from #3314" do - module Example - class FooType < GraphQL::Schema::Object - field :id, ID, null: false - end - - class FooSource < GraphQL::Dataloader::Source - def fetch(ids) - ids.map { |id| OpenStruct.new(id: id) } - end - end - - class QueryType < GraphQL::Schema::Object - field :foo, Example::FooType do - argument :foo_id, GraphQL::Types::ID, required: false, loads: Example::FooType - end - - def foo(foo: nil) - dataloader.with(Example::FooSource).load("load") - end - end - - class Schema < GraphQL::Schema - query Example::QueryType - use GraphQL::Dataloader - - def self.object_from_id(id, ctx) - ctx.dataloader.with(Example::FooSource).load(id) - end - - def self.resolve_type(type, obj, ctx) - type - end - end - end - - it "loads properly" do - result = Example::Schema.execute(<<-GRAPHQL) - { - fooWithLoad: foo(fooId: "Other") { - __typename - id - } - } - GRAPHQL - # This should not have a Lazy in it - expected_result = { - "data" => { - "fooWithLoad" => { "id" => "load", "__typename" => "Foo" }, - } - } - - assert_equal expected_result, result.to_h - end - end - - class FiberErrorSchema < GraphQL::Schema - class ErrorObject < GraphQL::Dataloader::Source - def fetch(_) - raise ArgumentError, "Nope" - end - end - - class Query < GraphQL::Schema::Object - field :load, String, null: false - field :load_all, String, null: false - field :request, String, null: false - field :request_all, String, null: false - - def load - dataloader.with(ErrorObject).load(123) - end - - def load_all - dataloader.with(ErrorObject).load_all([123]) - end - - def request - req = dataloader.with(ErrorObject).request(123) - req.load - end - - def request_all - req = dataloader.with(ErrorObject).request_all([123]) - req.load - end - end - - use GraphQL::Dataloader - query(Query) - - rescue_from(StandardError) do |err, obj, args, ctx, field| - ctx[:errors] << "#{err.message} (#{field.owner.name}.#{field.graphql_name}, #{obj.inspect}, #{args.inspect})" - nil - end - end - - it "Works with error handlers" do - context = { errors: [] } - - res = FiberErrorSchema.execute("{ load loadAll request requestAll }", context: context) - - expected_errors = [ - "Nope (FiberErrorSchema::Query.load, nil, {})", - "Nope (FiberErrorSchema::Query.loadAll, nil, {})", - "Nope (FiberErrorSchema::Query.request, nil, {})", - "Nope (FiberErrorSchema::Query.requestAll, nil, {})", - ] - - assert_nil(res["data"]) - assert_equal(expected_errors, context[:errors].sort) - end - - it "has proper context[:current_field]" do - res = FiberSchema.execute("mutation { mutation1(argument1: \"abc\") { __typename } mutation2(argument2: \"def\") { __typename } }") - assert_equal({"mutation1"=>{ "__typename" => "Mutation1Payload" }, "mutation2"=>{ "__typename" => "Mutation2Payload"} }, res["data"]) - expected_errors = [ - "FieldTestError @ [\"mutation1\"], Mutation.mutation1 / Mutation.mutation1", - "FieldTestError @ [\"mutation2\"], Mutation.mutation2 / Mutation.mutation2", - ] - assert_equal expected_errors, res.context[:errors] - end - - it "passes along throws" do - value = catch(:hello) do - dataloader = GraphQL::Dataloader.new - dataloader.append_job do - throw(:hello, :world) - end - dataloader.run - end - - assert :world, value - end - - class CanaryDataloader < GraphQL::Dataloader::NullDataloader - end - - it "uses context[:dataloader] when given" do - res = Class.new(GraphQL::Schema) do - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name "Query" - end - query(query_type) - end.execute("{ __typename }") - assert_instance_of GraphQL::Dataloader::NullDataloader, res.context.dataloader - res = FiberSchema.execute("{ __typename }") - assert_instance_of GraphQL::Dataloader, res.context.dataloader - refute res.context.dataloader.nonblocking? - res = FiberSchema.execute("{ __typename }", context: { dataloader: CanaryDataloader.new } ) - assert_instance_of CanaryDataloader, res.context.dataloader - - if Fiber.respond_to?(:scheduler) - Fiber.set_scheduler(::DummyScheduler.new) - res = FiberSchema.execute("{ __typename }", context: { dataloader: GraphQL::Dataloader.new(nonblocking: true) }) - assert res.context.dataloader.nonblocking? - - res = FiberSchema.multiplex([{ query: "{ __typename }" }], context: { dataloader: GraphQL::Dataloader.new(nonblocking: true) }) - assert res[0].context.dataloader.nonblocking? - Fiber.set_scheduler(nil) - end - end - - describe "#run_isolated" do - module RunIsolated - class CountSource < GraphQL::Dataloader::Source - def fetch(ids) - @count ||= 0 - @count += ids.size - ids.map { |_id| @count } - end - end - end - - it "uses its own queue" do - dl = GraphQL::Dataloader.new - result = {} - dl.append_job { result[:a] = 1 } - dl.append_job { result[:b] = 2 } - dl.append_job { result[:c] = 3 } - - dl.run_isolated { result[:d] = 4 } - - assert_equal({ d: 4 }, result) - - dl.run_isolated { - _r1 = dl.with(RunIsolated::CountSource).request(1) - _r2 = dl.with(RunIsolated::CountSource).request(2) - r3 = dl.with(RunIsolated::CountSource).request(3) - # This is going to `Fiber.yield` - result[:e] = r3.load - } - - assert_equal({ d: 4, e: 3 }, result) - dl.run - assert_equal({ a: 1, b: 2, c: 3, d: 4, e: 3 }, result) - end - - it "shares a cache" do - dl = GraphQL::Dataloader.new - result = {} - dl.run_isolated { - _r1 = dl.with(RunIsolated::CountSource).request(1) - _r2 = dl.with(RunIsolated::CountSource).request(2) - r3 = dl.with(RunIsolated::CountSource).request(3) - # Run all three of the above requests: - result[:a] = r3.load - } - - dl.append_job { - # This should return cached from above - result[:b] = dl.with(RunIsolated::CountSource).load(1) - } - dl.append_job { - # This one is run by itself - result[:c] = dl.with(RunIsolated::CountSource).load(4) - } - - assert_equal({ a: 3 }, result) - dl.run - assert_equal({ a: 3, b: 3, c: 4 }, result) - end - end - - describe "thread local variables" do - module ThreadVariable - class Type < GraphQL::Schema::Object - field :key, String, null: false - field :value, String, null: false - end - - class Source < GraphQL::Dataloader::Source - def fetch(keys) - keys.map { |key| OpenStruct.new(key: key, value: Thread.current[key.to_sym]) } - end - end - - class QueryType < GraphQL::Schema::Object - field :thread_var, ThreadVariable::Type do - argument :key, GraphQL::Types::String - end - - def thread_var(key:) - dataloader.with(ThreadVariable::Source).load(key) - end - end - - class Schema < GraphQL::Schema - query ThreadVariable::QueryType - use GraphQL::Dataloader - end - end - - it "sets the parent thread locals in the execution fiber" do - Thread.current[:test_thread_var] = 'foobarbaz' - - result = ThreadVariable::Schema.execute(<<-GRAPHQL) - { - threadVar(key: "test_thread_var") { - key - value - } - } - GRAPHQL - - expected_result = { - "data" => { - "threadVar" => { "key" => "test_thread_var", "value" => "foobarbaz" } - } - } - - assert_equal expected_result, result.to_h - end - end - - describe "thread-local variables with custom dataloader" do - module CustomThreadVariable - class Type < GraphQL::Schema::Object - field :key, String, null: false - field :value, String, null: false - end - - class CustomDataloader < GraphQL::Dataloader - def get_fiber_variables - { test_thread_var: "bazbarfoo" } - end - end - - class Source < GraphQL::Dataloader::Source - def fetch(keys) - keys.map { |key| OpenStruct.new(key: key, value: Thread.current[key.to_sym]) } - end - end - - class QueryType < GraphQL::Schema::Object - field :thread_var, CustomThreadVariable::Type do - argument :key, GraphQL::Types::String - end - - def thread_var(key:) - dataloader.with(CustomThreadVariable::Source).load(key) - end - end - - class Schema < GraphQL::Schema - query CustomThreadVariable::QueryType - use CustomDataloader - end - end - - it "sets the parent thread locals in the execution fiber" do - result = CustomThreadVariable::Schema.execute(<<-GRAPHQL) - { - threadVar(key: "test_thread_var") { - key - value - } - } - GRAPHQL - - expected_result = { - "data" => { - "threadVar" => { "key" => "test_thread_var", "value" => "bazbarfoo" } - } - } - - assert_equal expected_result, result.to_h - end - end - - describe "dataloader calls from inside sources" do - class NestedDataloaderCallsSchema < GraphQL::Schema - class Echo < GraphQL::Dataloader::Source - def fetch(keys) - keys - end - end - - class Nested < GraphQL::Dataloader::Source - def fetch(keys) - dataloader.with(Echo).load_all(keys) - end - end - - class Nested2 < GraphQL::Dataloader::Source - def fetch(keys) - dataloader.with(Nested).load_all(keys) - end - end - - class QueryType < GraphQL::Schema::Object - field :nested, String - field :nested2, String - - def nested - dataloader.with(Nested).load("nested") - end - - def nested2 - dataloader.with(Nested2).load("nested2") - end - end - - query QueryType - use GraphQL::Dataloader - end - end - - it "loads data from inside source methods" do - assert_equal({ "data" => { "nested" => "nested" } }, NestedDataloaderCallsSchema.execute("{ nested }")) - assert_equal({ "data" => { "nested2" => "nested2" } }, NestedDataloaderCallsSchema.execute("{ nested2 }")) - assert_equal({ "data" => { "nested" => "nested", "nested2" => "nested2" } }, NestedDataloaderCallsSchema.execute("{ nested nested2 }")) - end - - describe "with lazy authorization hooks" do - class LazyAuthHookSchema < GraphQL::Schema - class Source < ::GraphQL::Dataloader::Source - def fetch(ids) - return ids.map {|i| i * 2} - end - end - - class BarType < GraphQL::Schema::Object - field :id, Integer - - def id - object - end - - def self.authorized?(object, context) - -> { true } - end - end - - class FooType < GraphQL::Schema::Object - field :dataloader_value, BarType - - def self.authorized?(object, context) - -> { true } - end - - def dataloader_value - dataloader.with(Source).load(1) - end - end - - class QueryType < GraphQL::Schema::Object - field :foo, FooType - - def foo - {} - end - end - - use GraphQL::Dataloader - query QueryType - lazy_resolve Proc, :call - end - - it "resolves everything" do - dataloader_query = """ - query { - foo { - dataloaderValue { - id - } - } - } - """ - dataloader_result = LazyAuthHookSchema.execute(dataloader_query) - assert_equal 2, dataloader_result["data"]["foo"]["dataloaderValue"]["id"] - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/directive_spec.rb b/vendor/gems/graphql/spec/graphql/directive_spec.rb deleted file mode 100644 index 8f1b78e3ab2..00000000000 --- a/vendor/gems/graphql/spec/graphql/directive_spec.rb +++ /dev/null @@ -1,278 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe "GraphQL::Directive" do - let(:variables) { {"t" => true, "f" => false} } - let(:result) { Dummy::Schema.execute(query_string, variables: variables) } - describe "on fields" do - let(:query_string) { %|query directives($t: Boolean!, $f: Boolean!) { - cheese(id: 1) { - # plain fields: - skipFlavor: flavor @skip(if: true) - dontSkipFlavor: flavor @skip(if: false) - dontSkipDontIncludeFlavor: flavor @skip(if: false), @include(if: false) - skipAndInclude: flavor @skip(if: true), @include(if: true) - includeFlavor: flavor @include(if: $t) - dontIncludeFlavor: flavor @include(if: $f) - # fields in fragments - ... includeIdField - ... dontIncludeIdField - ... skipIdField - ... dontSkipIdField - } - } - fragment includeIdField on Cheese { includeId: id @include(if: true) } - fragment dontIncludeIdField on Cheese { dontIncludeId: id @include(if: false) } - fragment skipIdField on Cheese { skipId: id @skip(if: true) } - fragment dontSkipIdField on Cheese { dontSkipId: id @skip(if: false) } - | - } - - describe "child fields" do - let(:query_string) { <<-GRAPHQL - { - __type(name: """ - Cheese - """) { - fields { name } - fields @skip(if: true) { isDeprecated } - } - } - GRAPHQL - } - - it "skips child fields too" do - first_field = result["data"]["__type"]["fields"].first - assert first_field.key?("name") - assert !first_field.key?("isDeprecated") - end - end - - describe "when directive uses argument with default value" do - describe "with false" do - let(:query_string) { <<-GRAPHQL - query($f: Boolean = false) { - cheese(id: 1) { - dontIncludeFlavor: flavor @include(if: $f) - dontSkipFlavor: flavor @skip(if: $f) - } - } - GRAPHQL - } - - it "is not included" do - assert !result["data"]["cheese"].key?("dontIncludeFlavor") - end - - it "is not skipped" do - assert result["data"]["cheese"].key?("dontSkipFlavor") - end - end - - describe "with true" do - let(:query_string) { <<-GRAPHQL - query($t: Boolean = true) { - cheese(id: 1) { - includeFlavor: flavor @include(if: $t) - skipFlavor: flavor @skip(if: $t) - } - } - GRAPHQL - } - - it "is included" do - assert result["data"]["cheese"].key?("includeFlavor") - end - - it "is skipped" do - assert !result["data"]["cheese"].key?("skipFlavor") - end - end - end - - it "intercepts fields" do - expected = { "data" =>{ - "cheese" => { - "dontSkipFlavor" => "Brie", - "includeFlavor" => "Brie", - "includeId" => 1, - "dontSkipId" => 1, - }, - }} - assert_equal(expected, result) - end - end - describe "on fragments spreads and inline fragments" do - let(:query_string) { %|query directives { - cheese(id: 1) { - ... skipFlavorField @skip(if: true) - ... dontSkipFlavorField @skip(if: false) - ... includeFlavorField @include(if: true) - ... dontIncludeFlavorField @include(if: false) - - - ... on Cheese @skip(if: true) { skipInlineId: id } - ... on Cheese @skip(if: false) { dontSkipInlineId: id } - ... on Cheese @include(if: true) { includeInlineId: id } - ... on Cheese @include(if: false) { dontIncludeInlineId: id } - ... @skip(if: true) { skipNoType: id } - ... @skip(if: false) { dontSkipNoType: id } - } - } - fragment includeFlavorField on Cheese { includeFlavor: flavor } - fragment dontIncludeFlavorField on Cheese { dontIncludeFlavor: flavor } - fragment skipFlavorField on Cheese { skipFlavor: flavor } - fragment dontSkipFlavorField on Cheese { dontSkipFlavor: flavor } - |} - - it "intercepts fragment spreads" do - expected = { "data" => { - "cheese" => { - "dontSkipFlavor" => "Brie", - "includeFlavor" => "Brie", - "dontSkipInlineId" => 1, - "includeInlineId" => 1, - "dontSkipNoType" => 1, - }, - }} - assert_equal(expected, result) - end - end - describe "merging @skip and @include" do - let(:field_included?) { r = result["data"]["cheese"]; r.has_key?('flavor') && r.has_key?('withVariables') } - let(:skip?) { false } - let(:include?) { true } - let(:variables) { {"skip" => skip?, "include" => include?} } - let(:query_string) {" - query getCheese ($include: Boolean!, $skip: Boolean!) { - cheese(id: 1) { - flavor @include(if: #{include?}) @skip(if: #{skip?}), - withVariables: flavor @include(if: $include) @skip(if: $skip) - } - } - "} - # behavior as defined in - # https://github.com/facebook/graphql/blob/master/spec/Section%203%20--%20Type%20System.md#include - describe "when @skip=false and @include=true" do - let(:skip?) { false } - let(:include?) { true } - it "is included" do assert field_included? end - end - describe "when @skip=false and @include=false" do - let(:skip?) { false } - let(:include?) { false } - it "is not included" do assert !field_included? end - end - describe "when @skip=true and @include=true" do - let(:skip?) { true } - let(:include?) { true } - it "is not included" do assert !field_included? end - end - describe "when @skip=true and @include=false" do - let(:skip?) { true } - let(:include?) { false } - it "is not included" do assert !field_included? end - end - describe "when evaluating skip on query selection and fragment" do - describe "with @skip" do - let(:query_string) {" - query getCheese ($skip: Boolean!) { - cheese(id: 1) { - flavor, - withVariables: flavor, - ...F0 - } - } - fragment F0 on Cheese { - flavor @skip(if: #{skip?}) - withVariables: flavor @skip(if: $skip) - } - "} - describe "and @skip=false" do - let(:skip?) { false } - it "is included" do assert field_included? end - end - describe "and @skip=true" do - let(:skip?) { true } - it "is included" do assert field_included? end - end - end - end - describe "when evaluating conflicting @skip and @include on query selection and fragment" do - let(:query_string) {" - query getCheese ($include: Boolean!, $skip: Boolean!) { - cheese(id: 1) { - ... on Cheese @include(if: #{include?}) { - flavor - } - withVariables: flavor @include(if: $include), - ...F0 - } - } - fragment F0 on Cheese { - flavor @skip(if: #{skip?}), - withVariables: flavor @skip(if: $skip) - } - "} - describe "when @skip=false and @include=true" do - let(:skip?) { false } - let(:include?) { true } - it "is included" do assert field_included? end - end - describe "when @skip=false and @include=false" do - let(:skip?) { false } - let(:include?) { false } - it "is included" do assert field_included? end - end - describe "when @skip=true and @include=true" do - let(:skip?) { true } - let(:include?) { true } - it "is included" do assert field_included? end - end - describe "when @skip=true and @include=false" do - let(:skip?) { true } - let(:include?) { false } - it "is not included" do - assert !field_included? - end - end - end - - describe "when handling multiple fields at the same level" do - describe "when at least one occurrence would be included" do - let(:query_string) {" - query getCheese ($include: Boolean!, $skip: Boolean!) { - cheese(id: 1) { - ... on Cheese { - flavor - } - flavor @include(if: #{include?}), - flavor @skip(if: #{skip?}), - withVariables: flavor, - withVariables: flavor @include(if: $include), - withVariables: flavor @skip(if: $skip) - } - } - "} - let(:skip?) { true } - let(:include?) { false } - it "is included" do assert field_included? end - end - describe "when no occurrence would be included" do - let(:query_string) {" - query getCheese ($include: Boolean!, $skip: Boolean!) { - cheese(id: 1) { - flavor @include(if: #{include?}), - flavor @skip(if: #{skip?}), - withVariables: flavor @include(if: $include), - withVariables: flavor @skip(if: $skip) - } - } - "} - let(:skip?) { true } - let(:include?) { false } - it "is not included" do assert !field_included? end - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/errors_spec.rb b/vendor/gems/graphql/spec/graphql/execution/errors_spec.rb deleted file mode 100644 index 7433d1bbf6a..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/errors_spec.rb +++ /dev/null @@ -1,336 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe "GraphQL::Execution::Errors" do - class ParentErrorsTestSchema < GraphQL::Schema - class ErrorD < RuntimeError; end - - rescue_from(ErrorD) do |err, obj, args, ctx, field| - raise GraphQL::ExecutionError, "ErrorD on #{obj.inspect} at #{field ? "#{field.path}(#{args})" : "boot"}" - end - end - - class ErrorsTestSchema < ParentErrorsTestSchema - ErrorD = ParentErrorsTestSchema::ErrorD - class ErrorA < RuntimeError; end - class ErrorB < RuntimeError; end - - class ErrorC < RuntimeError - attr_reader :value - def initialize(value:) - @value = value - super - end - end - - class ErrorASubclass < ErrorA; end - class ErrorBChildClass < ErrorB; end - class ErrorBGrandchildClass < ErrorBChildClass; end - - rescue_from(ErrorA) do |err, obj, args, ctx, field| - ctx[:errors] << "#{err.message} (#{field.owner.name}.#{field.graphql_name}, #{obj.inspect}, #{args.inspect})" - nil - end - - rescue_from(ErrorBChildClass) do |*| - "Handled ErrorBChildClass" - end - - # Trying to assert that _specificity_ takes priority - # over sequence, but the stability of that assertion - # depends on the underlying implementation. - rescue_from(ErrorBGrandchildClass) do |*| - "Handled ErrorBGrandchildClass" - end - - rescue_from(ErrorB) do |*| - raise GraphQL::ExecutionError, "boom!" - end - - rescue_from(ErrorC) do |err, *| - err.value - end - - class ErrorList < Array - def each - raise ErrorB - end - end - - class Thing < GraphQL::Schema::Object - def self.authorized?(obj, ctx) - if ctx[:authorized] == false - raise ErrorD - end - true - end - - field :string, String, null: false - def string - "a string" - end - end - - class ValuesInput < GraphQL::Schema::InputObject - argument :value, Int, loads: Thing - - def self.object_from_id(type, value, ctx) - if value == 1 - :thing - else - raise ErrorD - end - end - end - - class PickyString < GraphQL::Schema::Scalar - def self.coerce_input(value, ctx) - if value == "picky" - value - else - raise ErrorB, "The string wasn't \"picky\"" - end - end - end - - class Query < GraphQL::Schema::Object - field :f1, Int do - argument :a1, Int, required: false - end - - def f1(a1: nil) - raise ErrorA, "f1 broke" - end - - field :f2, Int - def f2 - -> { raise ErrorA, "f2 broke" } - end - - field :f3, Int - - def f3 - raise ErrorB - end - - field :f4, Int, null: false - def f4 - raise ErrorC.new(value: 20) - end - - field :f5, Int - def f5 - raise ErrorASubclass, "raised subclass" - end - - field :f6, Int - def f6 - -> { raise ErrorB } - end - - field :f7, String - def f7 - raise ErrorBGrandchildClass - end - - field :f8, String do - argument :input, PickyString - end - - def f8(input:) - input - end - - field :f9, String do - argument :thing_id, ID, loads: Thing - end - - def f9(thing:) - thing[:id] - end - - field :thing, Thing - def thing - :thing - end - - field :input_field, Int do - argument :values, ValuesInput - end - - field :non_nullable_array, [String], null: false - def non_nullable_array - [nil] - end - - field :error_in_each, [Int] - - def error_in_each - ErrorList.new - end - end - - query(Query) - lazy_resolve(Proc, :call) - - def self.object_from_id(id, ctx) - if id == "boom" - raise ErrorB - end - - { thing: true, id: id } - end - - def self.resolve_type(type, obj, ctx) - Thing - end - end - - class ErrorsTestSchemaWithoutInterpreter < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :non_nullable_array, [String], null: false - def non_nullable_array - [nil] - end - end - - query(Query) - end - - describe "rescue_from handling" do - it "can replace values with `nil`" do - ctx = { errors: [] } - res = ErrorsTestSchema.execute "{ f1(a1: 1) }", context: ctx, root_value: :abc - assert_equal({ "data" => { "f1" => nil } }, res) - assert_equal ["f1 broke (ErrorsTestSchema::Query.f1, :abc, #{{a1: 1}.inspect})"], ctx[:errors] - end - - it "rescues errors from lazy code" do - ctx = { errors: [] } - res = ErrorsTestSchema.execute("{ f2 }", context: ctx) - assert_equal({ "data" => { "f2" => nil } }, res) - assert_equal ["f2 broke (ErrorsTestSchema::Query.f2, nil, {})"], ctx[:errors] - end - - it "picks the most specific handler and uses the return value from it" do - res = ErrorsTestSchema.execute("{ f7 }") - assert_equal({ "data" => { "f7" => "Handled ErrorBGrandchildClass" } }, res) - end - - it "rescues errors from lazy code with handlers that re-raise" do - res = ErrorsTestSchema.execute("{ f6 }") - expected_error = { - "message"=>"boom!", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["f6"] - } - assert_equal({ "data" => { "f6" => nil }, "errors" => [expected_error] }, res) - end - - it "can raise new errors" do - res = ErrorsTestSchema.execute("{ f3 }") - expected_error = { - "message"=>"boom!", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["f3"] - } - assert_equal({ "data" => { "f3" => nil }, "errors" => [expected_error] }, res) - end - - it "can replace values with non-nil" do - res = ErrorsTestSchema.execute("{ f4 }") - assert_equal({ "data" => { "f4" => 20 } }, res) - end - - it "rescues subclasses" do - context = { errors: [] } - res = ErrorsTestSchema.execute("{ f5 }", context: context) - assert_equal({ "data" => { "f5" => nil } }, res) - assert_equal ["raised subclass (ErrorsTestSchema::Query.f5, nil, {})"], context[:errors] - end - - describe "errors raised when coercing inputs" do - it "rescues them" do - res1 = ErrorsTestSchema.execute("{ f8(input: \"picky\") }") - assert_equal "picky", res1["data"]["f8"] - res2 = ErrorsTestSchema.execute("{ f8(input: \"blah\") }") - assert_equal ["errors"], res2.keys - assert_equal ["boom!"], res2["errors"].map { |e| e["message"] } - - res3 = ErrorsTestSchema.execute("query($v: PickyString!) { f8(input: $v) }", variables: { v: "blah" }) - assert_equal ["errors"], res3.keys - assert_equal ["Variable $v of type PickyString! was provided invalid value"], res3["errors"].map { |e| e["message"] } - assert_equal [["boom!"]], res3["errors"].map { |e| e["extensions"]["problems"].map { |pr| pr["explanation"] } } - end - end - - describe "errors raised when loading objects from ID" do - it "rescues them" do - res1 = ErrorsTestSchema.execute("{ f9(thingId: \"abc\") }") - assert_equal "abc", res1["data"]["f9"] - - res2 = ErrorsTestSchema.execute("{ f9(thingId: \"boom\") }") - assert_equal ["boom!"], res2["errors"].map { |e| e["message"] } - end - end - - describe "errors raised in authorized hooks" do - it "rescues them" do - context = { authorized: false } - res = ErrorsTestSchema.execute(" { thing { string } } ", context: context) - assert_equal ["ErrorD on nil at Query.thing({})"], res["errors"].map { |e| e["message"] } - end - end - - describe "errors raised in input_object loads" do - it "rescues them from literal values" do - context = { authorized: false } - res = ErrorsTestSchema.execute(" { inputField(values: { value: 2 }) } ", root_value: :root, context: context) - # It would be better to have the arguments here, but since this error was raised during _creation_ of keywords, - # so the runtime arguments aren't available now. - assert_equal ["ErrorD on :root at Query.inputField()"], res["errors"].map { |e| e["message"] } - end - - it "rescues them from variable values" do - context = { authorized: false } - res = ErrorsTestSchema.execute( - "query($values: ValuesInput!) { inputField(values: $values) } ", - variables: { values: { "value" => 2 } }, - context: context, - ) - - assert_equal ["ErrorD on nil at Query.inputField()"], res["errors"].map { |e| e["message"] } - end - end - - describe "errors raised in non_nullable_array loads" do - it "outputs the appropriate error message when using non-interpreter schema" do - res = ErrorsTestSchemaWithoutInterpreter.execute("{ nonNullableArray }") - expected_error = { - "message" => "Cannot return null for non-nullable field Query.nonNullableArray", - "path" => ["nonNullableArray", 0], - "locations" => [{ "line" => 1, "column" => 3 }] - } - assert_equal({ "data" => nil, "errors" => [expected_error] }, res) - end - - it "outputs the appropriate error message when using interpreter schema" do - res = ErrorsTestSchema.execute("{ nonNullableArray }") - expected_error = { - "message" => "Cannot return null for non-nullable field Query.nonNullableArray", - "path" => ["nonNullableArray", 0], - "locations" => [{ "line" => 1, "column" => 3 }] - } - assert_equal({ "data" => nil, "errors" => [expected_error] }, res) - end - end - - describe "when .each on a list type raises an error" do - it "rescues it properly" do - res = ErrorsTestSchema.execute("{ __typename errorInEach }") - expected_error = { "message" => "boom!", "locations"=>[{"line"=>1, "column"=>14}], "path"=>["errorInEach"] } - assert_equal({ "data" => { "__typename" => "Query", "errorInEach" => nil }, "errors" => [expected_error] }, res) - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/instrumentation_spec.rb b/vendor/gems/graphql/spec/graphql/execution/instrumentation_spec.rb deleted file mode 100644 index 1044d0da47e..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/instrumentation_spec.rb +++ /dev/null @@ -1,208 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Schema do - describe "instrumentation teardown bug" do - # This instrumenter records that it ran, - # or raises an error if instructed to do so - class InstrumenterError < StandardError - attr_reader :key - def initialize(key) - @key = key - super() - end - end - - module LogInstrumenter - def self.generate(context_key_sym) - hook_method = :"#{context_key_sym}_run_hook" - mod = Module.new - - mod.define_method(:execute_query) do |query:, &block| - public_send(hook_method, query, "begin") - result = nil - begin - result = super(query: query, &block) - ensure - public_send(hook_method, query, "end") - end - result - end - - mod.define_method(:execute_multiplex) do |multiplex:, &block| - public_send(hook_method, multiplex, "begin") - result = nil - begin - result = super(multiplex: multiplex, &block) - ensure - public_send(hook_method, multiplex, "end") - end - result - end - - mod.define_method(hook_method) do |unit_of_work, event_name| - log_key = :"#{context_key_sym}_did_#{event_name}" - error_key = :"#{context_key_sym}_should_raise_#{event_name}" - unit_of_work.context[log_key] = true - if unit_of_work.context[error_key] - raise InstrumenterError.new(log_key) - end - end - - mod - end - end - - module ExecutionErrorTrace - def execute_query(query:) - if query.context[:raise_execution_error] - raise GraphQL::ExecutionError, "Raised from trace execute_query" - end - super - end - end - - # This is how you might add queries from a persisted query backend - - module QueryStringTrace - def execute_multiplex(multiplex:) - multiplex.queries.each do |query| - if query.context[:extra_query_string] && query.query_string.nil? - query.query_string = query.context[:extra_query_string] - end - end - super - end - end - - let(:query_type) { - Class.new(GraphQL::Schema::Object) do - graphql_name "Query" - field :int, Integer do - argument :value, Integer, required: false - end - - def int(value:) - value - end - end - } - - let(:schema) { - spec = self - Class.new(GraphQL::Schema) do - query(spec.query_type) - trace_with(LogInstrumenter.generate(:second_instrumenter)) - trace_with(LogInstrumenter.generate(:first_instrumenter)) - trace_with(ExecutionErrorTrace) - trace_with(QueryStringTrace) - end - } - - describe "query instrumenters" do - it "before_query of the 2nd instrumenter does not run but after_query does" do - context = {second_instrumenter_should_raise_begin: true} - assert_raises InstrumenterError do - schema.execute(" { int(value: 2) } ", context: context) - end - assert context[:first_instrumenter_did_begin] - assert context[:first_instrumenter_did_end] - assert context[:second_instrumenter_did_begin] - refute context[:second_instrumenter_did_end] - end - - it "runs after_query even if a previous after_query raised an error" do - context = {second_instrumenter_should_raise_end: true} - err = assert_raises InstrumenterError do - schema.execute(" { int(value: 2) } ", context: context) - end - # The error came from the second instrumenter: - assert_equal :second_instrumenter_did_end, err.key - # But the first instrumenter still got a chance to teardown - assert context[:first_instrumenter_did_begin] - assert context[:first_instrumenter_did_end] - assert context[:second_instrumenter_did_begin] - assert context[:second_instrumenter_did_end] - end - - it "rescues execution errors from execute_query" do - context = {raise_execution_error: true} - res = schema.execute(" { int(value: 2) } ", context: context) - assert_equal "Raised from trace execute_query", res["errors"].first["message"] - refute res.key?("data"), "The query doesn't run" - end - - it "can assign a query string there" do - context = { extra_query_string: "{ __typename }"} - res = schema.execute(nil, context: context) - assert_equal "Query", res["data"]["__typename"] - end - end - - describe "within a multiplex" do - let(:multiplex_schema) { - Class.new(schema) { - trace_with(LogInstrumenter.generate(:second_instrumenter)) - trace_with(LogInstrumenter.generate(:first_instrumenter)) - } - } - - it "only runs after_multiplex if before_multiplex finished" do - multiplex_ctx = {second_instrumenter_should_raise_begin: true} - query_1_ctx = {} - query_2_ctx = {} - assert_raises InstrumenterError do - multiplex_schema.multiplex( - [ - {query: "{int(value: 1)}", context: query_1_ctx}, - {query: "{int(value: 2)}", context: query_2_ctx}, - ], - context: multiplex_ctx - ) - end - - assert multiplex_ctx[:first_instrumenter_did_begin] - assert multiplex_ctx[:first_instrumenter_did_end] - assert multiplex_ctx[:second_instrumenter_did_begin] - refute multiplex_ctx[:second_instrumenter_did_end] - # No query instrumentation was run at all - expected_ctx_size = GraphQL::Schema.use_visibility_profile? ? 1 : 0 - assert_equal expected_ctx_size, query_1_ctx.size - assert_equal expected_ctx_size, query_2_ctx.size - end - - it "does full and partial query runs" do - multiplex_ctx = {} - query_1_ctx = {} - query_2_ctx = {second_instrumenter_should_raise_begin: true} - assert_raises InstrumenterError do - multiplex_schema.multiplex( - [ - { query: " { int(value: 2) } ", context: query_1_ctx }, - { query: " { int(value: 2) } ", context: query_2_ctx }, - ], - context: multiplex_ctx - ) - end - - # multiplex got a full run - assert multiplex_ctx[:first_instrumenter_did_begin] - assert multiplex_ctx[:first_instrumenter_did_end] - assert multiplex_ctx[:second_instrumenter_did_begin] - assert multiplex_ctx[:second_instrumenter_did_end] - - # query 1 got a full run - assert query_1_ctx[:first_instrumenter_did_begin] - assert query_1_ctx[:first_instrumenter_did_end] - assert query_1_ctx[:second_instrumenter_did_begin] - assert query_1_ctx[:second_instrumenter_did_end] - - # query 2 got a partial run - assert query_2_ctx[:first_instrumenter_did_begin] - assert query_2_ctx[:first_instrumenter_did_end] - assert query_2_ctx[:second_instrumenter_did_begin] - refute query_2_ctx[:second_instrumenter_did_end] - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/interpreter/arguments_spec.rb b/vendor/gems/graphql/spec/graphql/execution/interpreter/arguments_spec.rb deleted file mode 100644 index 3552ab44232..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/interpreter/arguments_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe "GraphQL::Execution::Interpreter::Arguments" do - class InterpreterArgsTestSchema < GraphQL::Schema - class SearchParams < GraphQL::Schema::InputObject - argument :query, String, required: false - end - - class Query < GraphQL::Schema::Object - field :search, [String], null: false do - argument :params, SearchParams, required: false - argument :limit, Int - end - end - - query(Query) - end - - def arguments(query_str) - query = GraphQL::Query.new(InterpreterArgsTestSchema, query_str) - ast_node = query.document.definitions.first.selections.first - query_type = query.get_type("Query") - field = query.get_field(query_type, "search") - query.arguments_for(ast_node, field) - end - - it "provides .dig" do - query_str = <<-GRAPHQL - { - search(limit: 10, params: { query: "abcde" } ) - } - GRAPHQL - args = arguments(query_str) - assert_equal 10, args.dig(:limit) - assert_equal "abcde", args.dig(:params, :query) - assert_nil args.dig(:nothing) - assert_nil args.dig(:params, :nothing) - assert_nil args.dig(:nothing, :nothing, :nothing) - end - - it "is frozen, and so are its constituent hashes" do - query_str = <<-GRAPHQL - { - search(limit: 10, params: { query: "abcde" } ) - } - GRAPHQL - args = arguments(query_str) - - assert args.frozen? - assert args.argument_values.frozen? - assert args.keyword_arguments.frozen? - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/interpreter_spec.rb b/vendor/gems/graphql/spec/graphql/execution/interpreter_spec.rb deleted file mode 100644 index 656e558c11e..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/interpreter_spec.rb +++ /dev/null @@ -1,952 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" -require_relative "../subscriptions_spec" - -describe GraphQL::Execution::Interpreter do - module InterpreterTest - class Box - def initialize(value: nil, &block) - @value = value - @block = block - end - - def value - if @block - @value = @block.call - @block = nil - end - @value - end - end - - class Expansion < GraphQL::Schema::Object - field :sym, String, null: false - field :lazy_sym, String, null: false - field :name, String, null: false - field :cards, ["InterpreterTest::Card"], null: false - - def self.authorized?(expansion, ctx) - if expansion.sym == "NOPE" - false - else - true - end - end - - def cards - Query::CARDS.select { |c| c.expansion_sym == @object.sym } - end - - def lazy_sym - Box.new(value: object.sym) - end - - field :null_union_field_test, Integer, null: false - def null_union_field_test - 1 - end - - field :always_cached_value, Integer, null: false - def always_cached_value - raise "should never be called" - end - end - - class Card < GraphQL::Schema::Object - field :name, String, null: false - field :colors, "[InterpreterTest::Color]", null: false - field :expansion, Expansion, null: false - - def expansion - Query::EXPANSIONS.find { |e| e.sym == @object.expansion_sym } - end - - field :null_union_field_test, Integer - def null_union_field_test - nil - end - - field :parent_class_name, String, null: false, extras: [:parent] - - def parent_class_name(parent:) - parent.class.name - end - end - - class Color < GraphQL::Schema::Enum - value "WHITE" - value "BLUE" - value "BLACK" - value "RED" - value "GREEN" - end - - class Entity < GraphQL::Schema::Union - possible_types Card, Expansion - - def self.resolve_type(obj, ctx) - obj.sym ? Expansion : Card - end - end - - class FieldCounter < GraphQL::Schema::Object - implements GraphQL::Types::Relay::Node - - field :field_counter, FieldCounter, null: false - def field_counter; self.class.generate_tag(context); end - - field :calls, Integer, null: false do - argument :expected, Integer - end - - def calls(expected:) - c = context[:calls] += 1 - if c != expected - raise "Expected #{expected} calls but had #{c} so far" - else - c - end - end - - field :runtime_info, String, null: false do - argument :a, Integer, required: false - argument :b, Integer, required: false - end - - def runtime_info(a: nil, b: nil) - inspect_context - end - - field :lazy_runtime_info, String, null: false do - argument :a, Integer, required: false - argument :b, Integer, required: false - end - - def lazy_runtime_info(a: nil, b: nil) - Box.new { inspect_context } - end - - def self.generate_tag(context) - context[:field_counters_count] ||= 0 - current_count = context[:field_counters_count] += 1 - "field_counter_#{current_count}" - end - - private - - def inspect_context - "<#{interpreter_context_for(:current_object).object.inspect}> #{interpreter_context_for(:current_path)} -> #{interpreter_context_for(:current_field).path}(#{interpreter_context_for(:current_arguments).size})" - end - - def interpreter_context_for(key) - # Make sure it's set in query context and interpreter namespace. - base_ctx_value = context[key] - interpreter_ctx_value = context.namespace(:interpreter)[key] - if base_ctx_value != interpreter_ctx_value - raise "Context mismatch for #{key} -> #{base_ctx_value} / interpreter: #{interpreter_ctx_value}" - else - base_ctx_value - end - end - end - - class Query < GraphQL::Schema::Object - # Try a root-level authorized hook that returns a lazy value - def self.authorized?(obj, ctx) - Box.new(value: true) - end - - field :card, Card do - argument :name, String - end - - def card(name:) - Box.new(value: CARDS.find { |c| c.name == name }) - end - - field :expansion, Expansion do - argument :sym, String - end - - def expansion(sym:) - EXPANSIONS.find { |e| e.sym == sym } - end - - field :expansion_raw, Expansion, null: false - - def expansion_raw - raw_value(sym: "RAW", name: "Raw expansion", always_cached_value: 42) - end - - field :expansion_mixed, [Expansion], null: false - - def expansion_mixed - expansions + [expansion_raw] - end - - field :expansions, [Expansion], null: false - def expansions - EXPANSIONS - end - - class ExpansionData < OpenStruct - end - - CARDS = [ - OpenStruct.new(name: "Dark Confidant", colors: ["BLACK"], expansion_sym: "RAV"), - ] - - EXPANSIONS = [ - ExpansionData.new(name: "Ravnica, City of Guilds", sym: "RAV"), - # This data has an error, for testing null propagation - ExpansionData.new(name: nil, sym: "XYZ"), - # This is not allowed by .authorized?, - ExpansionData.new(name: nil, sym: "NOPE"), - ] - - field :find, [Entity], null: false do - argument :id, [ID] - end - - def find(id:) - id.map do |ent_id| - Query::EXPANSIONS.find { |e| e.sym == ent_id } || - Query::CARDS.find { |c| c.name == ent_id } - end - end - - field :find_many, [Entity, null: true], null: false do - argument :ids, [ID] - end - - def find_many(ids:) - find(id: ids).map { |e| Box.new(value: e) } - end - - field :field_counter, FieldCounter, null: false - def field_counter; FieldCounter.generate_tag(context) ; end - - include GraphQL::Types::Relay::HasNodeField - include GraphQL::Types::Relay::HasNodesField - - class NestedQueryResult < GraphQL::Schema::Object - field :result, String - field :current_path, [String] - end - - field :nested_query, NestedQueryResult do - argument :query, String - end - - def nested_query(query:) - result = context.schema.multiplex([{query: query}], context: { allow_pending_thread_state: true }).first - { - result: JSON.dump(result), - current_path: context[:current_path], - } - end - end - - class Counter < GraphQL::Schema::Object - field :value, Integer, null: false - field :lazy_value, Integer, null: false - - def lazy_value - Box.new { object.value } - end - - field :increment, Counter, null: false - - def increment - object.value += 1 - object - end - end - - class Mutation < GraphQL::Schema::Object - field :increment_counter, Counter, null: false - - def increment_counter - counter = context[:counter] - counter.value += 1 - counter - end - end - - class Schema < GraphQL::Schema - query(Query) - mutation(Mutation) - lazy_resolve(Box, :value) - - use GraphQL::Schema::AlwaysVisible - - def self.object_from_id(id, ctx) - OpenStruct.new(id: id) - end - - def self.id_from_object(obj, type, ctx) - obj.id - end - - def self.resolve_type(type, obj, ctx) - FieldCounter - end - - class EnsureArgsAreObject - def self.trace(event, data) - case event - when "execute_field", "execute_field_lazy" - args = data[:query].context[:current_arguments] - if !args.is_a?(GraphQL::Execution::Interpreter::Arguments) - raise "Expected arguments object, got #{args.class}: #{args.inspect}" - end - end - yield - end - end - tracer EnsureArgsAreObject - - module EnsureThreadCleanedUp - def execute_multiplex(multiplex:) - res = super - runtime_info = Fiber[:__graphql_runtime_info] - if !runtime_info.nil? && runtime_info != {} - if !multiplex.context[:allow_pending_thread_state] - # `nestedQuery` can allow this - raise "Query did not clean up runtime state, found: #{runtime_info.inspect}" - end - end - res - end - end - trace_with(EnsureThreadCleanedUp) - end - end - - it "runs a query" do - query_string = <<-GRAPHQL - query($expansion: String!, $id1: ID!, $id2: ID!){ - card(name: "Dark Confidant") { - colors - expansion { - ... { - name - } - cards { - name - } - } - } - expansion(sym: $expansion) { - ... ExpansionFields - } - find(id: [$id1, $id2]) { - __typename - ... on Card { - name - } - ... on Expansion { - sym - } - } - } - - fragment ExpansionFields on Expansion { - cards { - name - } - } - GRAPHQL - - vars = {expansion: "RAV", id1: "Dark Confidant", id2: "RAV"} - result = InterpreterTest::Schema.execute(query_string, variables: vars) - assert_equal ["BLACK"], result["data"]["card"]["colors"] - assert_equal "Ravnica, City of Guilds", result["data"]["card"]["expansion"]["name"] - assert_equal [{"name" => "Dark Confidant"}], result["data"]["card"]["expansion"]["cards"] - assert_equal [{"name" => "Dark Confidant"}], result["data"]["expansion"]["cards"] - expected_abstract_list = [ - {"__typename" => "Card", "name" => "Dark Confidant"}, - {"__typename" => "Expansion", "sym" => "RAV"}, - ] - assert_equal expected_abstract_list, result["data"]["find"] - assert_nil Fiber[:__graphql_runtime_info] - end - - it "runs a nested query and maintains proper state" do - query_str = "query($queryStr: String!) { nestedQuery(query: $queryStr) { result currentPath } }" - result = InterpreterTest::Schema.execute(query_str, variables: { queryStr: "{ __typename }" }) - assert_equal '{"data":{"__typename":"Query"}}', result["data"]["nestedQuery"]["result"] - assert_equal ["nestedQuery"], result["data"]["nestedQuery"]["currentPath"] - assert_nil Fiber[:__graphql_runtime_info] - end - - it "runs mutation roots atomically and sequentially" do - query_str = <<-GRAPHQL - mutation { - i1: incrementCounter { value lazyValue - i2: increment { value lazyValue } - i3: increment { value lazyValue } - } - i4: incrementCounter { value lazyValue } - i5: incrementCounter { value lazyValue } - } - GRAPHQL - - result = InterpreterTest::Schema.execute(query_str, context: { counter: OpenStruct.new(value: 0) }) - expected_data = { - "i1" => { - "value" => 1, - # All of these get `3` as lazy value. They're resolved together, - # since they aren't _root_ mutation fields. - "lazyValue" => 3, - "i2" => { "value" => 2, "lazyValue" => 3 }, - "i3" => { "value" => 3, "lazyValue" => 3 }, - }, - "i4" => { "value" => 4, "lazyValue" => 4}, - "i5" => { "value" => 5, "lazyValue" => 5}, - } - assert_equal expected_data, result["data"] - end - - it "runs skip and include" do - query_str = <<-GRAPHQL - query($truthy: Boolean!, $falsey: Boolean!){ - exp1: expansion(sym: "RAV") @skip(if: true) { name } - exp2: expansion(sym: "RAV") @skip(if: false) { name } - exp3: expansion(sym: "RAV") @include(if: true) { name } - exp4: expansion(sym: "RAV") @include(if: false) { name } - exp5: expansion(sym: "RAV") @include(if: $truthy) { name } - exp6: expansion(sym: "RAV") @include(if: $falsey) { name } - } - GRAPHQL - - vars = {truthy: true, falsey: false} - result = InterpreterTest::Schema.execute(query_str, variables: vars) - expected_data = { - "exp2" => {"name" => "Ravnica, City of Guilds"}, - "exp3" => {"name" => "Ravnica, City of Guilds"}, - "exp5" => {"name" => "Ravnica, City of Guilds"}, - } - assert_equal expected_data, result["data"] - assert_nil Fiber[:__graphql_runtime_info] - end - - describe "runtime info in context" do - it "is available" do - res = InterpreterTest::Schema.execute <<-GRAPHQL - { - fieldCounter { - runtimeInfo(a: 1, b: 2) - fieldCounter { - runtimeInfo - lazyRuntimeInfo(a: 1) - } - } - } - GRAPHQL - - assert_equal '<"field_counter_1"> ["fieldCounter", "runtimeInfo"] -> FieldCounter.runtimeInfo(2)', res["data"]["fieldCounter"]["runtimeInfo"] - # These are both `field_counter_2`, but one is lazy - assert_equal '<"field_counter_2"> ["fieldCounter", "fieldCounter", "runtimeInfo"] -> FieldCounter.runtimeInfo(0)', res["data"]["fieldCounter"]["fieldCounter"]["runtimeInfo"] - assert_equal '<"field_counter_2"> ["fieldCounter", "fieldCounter", "lazyRuntimeInfo"] -> FieldCounter.lazyRuntimeInfo(1)', res["data"]["fieldCounter"]["fieldCounter"]["lazyRuntimeInfo"] - end - end - - describe "null propagation" do - it "propagates nulls" do - query_str = <<-GRAPHQL - { - expansion(sym: "XYZ") { - name - sym - lazySym - } - } - GRAPHQL - - res = InterpreterTest::Schema.execute(query_str) - # Although the expansion was found, its name of `nil` - # propagated to here - assert_nil res["data"].fetch("expansion") - assert_equal ["Cannot return null for non-nullable field Expansion.name"], res["errors"].map { |e| e["message"] } - assert_nil Fiber[:__graphql_runtime_info] - end - - it "places errors ahead of data in the response" do - query_str = <<-GRAPHQL - { - expansion(sym: "XYZ") { - name - } - } - GRAPHQL - - res = InterpreterTest::Schema.execute(query_str) - assert_equal ["errors", "data"], res.keys - end - - it "propagates nulls in lists" do - query_str = <<-GRAPHQL - { - expansions { - name - sym - lazySym - } - } - GRAPHQL - - res = InterpreterTest::Schema.execute(query_str) - # A null in one of the list items removed the whole list - assert_nil(res["data"]) - end - - it "works with unions that fail .authorized?" do - res = InterpreterTest::Schema.execute <<-GRAPHQL - { - find(id: "NOPE") { - ... on Expansion { - sym - } - } - } - GRAPHQL - assert_equal ["Cannot return null for non-nullable field Query.find"], res["errors"].map { |e| e["message"] } - end - - it "works with lists of unions" do - res = InterpreterTest::Schema.execute <<-GRAPHQL - { - findMany(ids: ["RAV", "NOPE", "BOGUS"]) { - ... on Expansion { - sym - } - } - } - GRAPHQL - - assert_equal 3, res["data"]["findMany"].size - assert_equal "RAV", res["data"]["findMany"][0]["sym"] - assert_nil res["data"]["findMany"][1] - assert_nil res["data"]["findMany"][2] - assert_equal false, res.key?("errors") - - assert_equal Hash, res["data"].class - assert_equal Array, res["data"]["findMany"].class - end - - it "works with union lists that have members of different kinds, with different nullabilities" do - res = InterpreterTest::Schema.execute <<-GRAPHQL - { - findMany(ids: ["RAV", "Dark Confidant"]) { - ... on Expansion { - nullUnionFieldTest - } - ... on Card { - nullUnionFieldTest - } - } - } - GRAPHQL - - assert_equal [1, nil], res["data"]["findMany"].map { |f| f["nullUnionFieldTest"] } - end - end - - describe "duplicated fields" do - it "doesn't run them multiple times" do - query_str = <<-GRAPHQL - { - fieldCounter { - calls(expected: 1) - # This should not be called since it matches the above - calls(expected: 1) - fieldCounter { - calls(expected: 2) - } - ...ExtraFields - } - } - fragment ExtraFields on FieldCounter { - fieldCounter { - # This should not be called since it matches the inline field: - calls(expected: 2) - # This _should_ be called - c3: calls(expected: 3) - } - } - GRAPHQL - - # It will raise an error if it doesn't match the expectation - res = InterpreterTest::Schema.execute(query_str, context: { calls: 0 }) - assert_equal 3, res["data"]["fieldCounter"]["fieldCounter"]["c3"] - end - end - - describe "backwards compatibility" do - it "handles a legacy nodes field" do - res = InterpreterTest::Schema.execute('{ node(id: "abc") { id } }') - assert_equal "abc", res["data"]["node"]["id"] - - res = InterpreterTest::Schema.execute('{ nodes(ids: ["abc", "xyz"]) { id } }') - assert_equal ["abc", "xyz"], res["data"]["nodes"].map { |n| n["id"] } - end - end - - describe "returning raw values" do - it "returns raw value" do - query_str = <<-GRAPHQL - { - expansionRaw { - name - sym - alwaysCachedValue - } - } - GRAPHQL - - res = InterpreterTest::Schema.execute(query_str) - assert_equal({ sym: "RAW", name: "Raw expansion", always_cached_value: 42 }, res["data"]["expansionRaw"]) - end - end - - describe "returning raw values and resolved fields" do - it "returns raw value" do - query_str = <<-GRAPHQL - { - expansionRaw { - name - sym - alwaysCachedValue - } - } - GRAPHQL - - res = InterpreterTest::Schema.execute(query_str) - assert_equal({ sym: "RAW", name: "Raw expansion", always_cached_value: 42 }, res["data"]["expansionRaw"]) - end - end - - describe "Lazy skips" do - class LazySkipSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - def self.authorized?(obj, ctx) - -> { true } - end - field :skip, String - - def skip - context.skip - end - - field :lazy_skip, String - def lazy_skip - -> { context.skip } - end - - field :mixed_skips, [String] - def mixed_skips - [ - "a", - context.skip, - "c", - -> { context.skip }, - "e", - ] - end - end - - class NothingSubscription < GraphQL::Schema::Subscription - field :nothing, String - def authorized?(*) - -> { true } - end - - def update - { nothing: object } - end - end - - class Subscription < GraphQL::Schema::Object - field :nothing, subscription: NothingSubscription - end - - query Query - subscription Subscription - use InMemoryBackend::Subscriptions, extra: nil - lazy_resolve Proc, :call - end - - it "skips properly" do - res = LazySkipSchema.execute("{ skip }") - assert_equal({}, res["data"]) - refute res.key?("errors") - - res = LazySkipSchema.execute("{ mixedSkips }") - assert_equal({ "mixedSkips" => ["a", "c", "e"] }, res["data"]) - refute res.key?("errors") - - res = LazySkipSchema.execute("{ lazySkip }") - assert_equal({}, res["data"]) - refute res.key?("errors") - - res = LazySkipSchema.execute("subscription { nothing { nothing } }") - assert_equal({}, res["data"]) - refute res.key?("errors") - # Make sure an update works properly - LazySkipSchema.subscriptions.trigger(:nothing, {}, :nothing_at_all) - _key, updates = LazySkipSchema.subscriptions.deliveries.first - assert_equal "nothing_at_all", updates[0]["data"]["nothing"]["nothing"] - end - end - - describe "GraphQL::ExecutionErrors from connection fields" do - module ConnectionErrorTest - class BaseField < GraphQL::Schema::Field - def authorized?(obj, args, ctx) - ctx[:authorized_calls] ||= 0 - ctx[:authorized_calls] += 1 - raise GraphQL::ExecutionError, "#{name} is not authorized" - end - end - - class BaseConnection < GraphQL::Types::Relay::BaseConnection - node_nullable(false) - edge_nullable(false) - edges_nullable(false) - end - - class BaseEdge < GraphQL::Types::Relay::BaseEdge - node_nullable(false) - end - - class Thing < GraphQL::Schema::Object - field_class BaseField - connection_type_class BaseConnection - edge_type_class BaseEdge - field :title, String, null: false - field :body, String, null: false - end - - class Query < GraphQL::Schema::Object - field :things, Thing.connection_type, null: false - - def things - [{title: "a"}, {title: "b"}, {title: "c"}] - end - - field :thing, Thing, null: false - - def thing - { - title: "a", - body: "b", - } - end - end - - class Schema < GraphQL::Schema - query Query - end - end - - it "returns only 1 error and stops resolving fields after that" do - res = ConnectionErrorTest::Schema.execute("{ things { nodes { title } } }") - assert_equal 1, res["errors"].size - assert_equal 1, res.context[:authorized_calls] - - res = ConnectionErrorTest::Schema.execute("{ things { edges { node { title } } } }") - assert_equal 1, res["errors"].size - assert_equal 1, res.context[:authorized_calls] - - res = ConnectionErrorTest::Schema.execute("{ thing { title body } }") - assert_equal 1, res["errors"].size - assert_equal 1, res.context[:authorized_calls] - end - end - - describe "GraphQL::ExecutionErrors from non-null list fields" do - module ListErrorTest - class BaseField < GraphQL::Schema::Field - def authorized?(*) - raise GraphQL::ExecutionError, "#{name} is not authorized" - end - end - - class Thing < GraphQL::Schema::Object - field_class BaseField - field :title, String, null: false - end - - class Query < GraphQL::Schema::Object - field :things, [Thing], null: false - - def things - [{title: "a"}, {title: "b"}, {title: "c"}] - end - end - - class Schema < GraphQL::Schema - query Query - end - end - - it "returns only 1 error" do - res = ListErrorTest::Schema.execute("{ things { title } }") - assert_equal 1, res["errors"].size - end - end - - describe "Invalid null from raised execution error doesn't halt parent fields" do - class RaisedErrorSchema < GraphQL::Schema - module Iface - include GraphQL::Schema::Interface - - field :bar, String, null: false - end - - class Txn < GraphQL::Schema::Object - field :fails, String, null: false - - def fails - raise GraphQL::ExecutionError, "boom" - end - end - - class Concrete < GraphQL::Schema::Object - implements Iface - - field :txn, Txn - - def txn - {} - end - - field :msg, String - - def msg - "THIS SHOULD SHOW UP" - end - end - - class Query < GraphQL::Schema::Object - field :iface, Iface - - def iface - {} - end - end - - query(Query) - orphan_types([Concrete]) - - def self.resolve_type(type, obj, ctx) - Concrete - end - end - - it "resolves fields on the parent object" do - querystring = """ - { - iface { - ... on Concrete { - txn { - fails - } - msg - } - } - } - """ - - result = RaisedErrorSchema.execute(querystring) - expected_result = { - "data" => { - "iface" => { "txn" => nil, "msg" => "THIS SHOULD SHOW UP" }, - }, - "errors" => [ - { - "message"=>"boom", - "locations"=>[{"line"=>6, "column"=>15}], - "path"=>["iface", "txn", "fails"] - }, - ], - } - assert_equal expected_result, result.to_h - end - end - - it "supports extras: [:parent]" do - query_str = <<-GRAPHQL - { - card(name: "Dark Confidant") { - parentClassName - } - expansion(sym: "RAV") { - cards { - parentClassName - } - } - } - GRAPHQL - res = InterpreterTest::Schema.execute(query_str, context: { calls: 0 }) - - assert_equal "NilClass", res["data"]["card"].fetch("parentClassName") - assert_equal "InterpreterTest::Query::ExpansionData", res["data"]["expansion"]["cards"].first["parentClassName"] - end - - describe "fragment used twice in different ways" do - class FragmentBugSchema < GraphQL::Schema - class ProductVariant < GraphQL::Schema::Object - field :product, "FragmentBugSchema::Product" - end - - class Product < GraphQL::Schema::Object - field :id, ID - field :variants, [ProductVariant] - - def variants - [{ product: { id: "1" } }] - end - end - - class Query < GraphQL::Schema::Object - field :variant, ProductVariant - - def variant - { product: { id: "1" } } - end - end - - query(Query) - end - - it "executes successfully" do - query_str = <<-GRAPHQL - { - variant { - ...variantFields - ... on ProductVariant { - product { - variants { - ...variantFields - } - } - } - } - } - - fragment variantFields on ProductVariant { - product { - id - } - } - GRAPHQL - - res = FragmentBugSchema.execute(query_str).to_h - - expected_result = { "variant" => { "product" => { "id" => "1", "variants" => [ { "product" => { "id" => "1" } } ] } } } - assert_equal(expected_result, res["data"]) - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/lazy/lazy_method_map_spec.rb b/vendor/gems/graphql/spec/graphql/execution/lazy/lazy_method_map_spec.rb deleted file mode 100644 index 94c4a73e041..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/lazy/lazy_method_map_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Execution::Lazy::LazyMethodMap do - def self.test_lazy_method_map - it "handles multithreaded access" do - a = Class.new - b = Class.new(a) - c = Class.new(b) - lazy_method_map.set(a, :a) - threads = 1000.times.map do |i| - Thread.new { - d = Class.new(c) - assert_equal :a, lazy_method_map.get(d.new) - } - end - threads.map(&:join) - end - - it "dups" do - a = Class.new - b = Class.new(a) - c = Class.new(b) - lazy_method_map.set(a, :a) - lazy_method_map.get(b.new) - lazy_method_map.get(c.new) - - dup_map = lazy_method_map.dup - assert_equal 3, dup_map.instance_variable_get(:@storage).size - assert_equal :a, dup_map.get(a.new) - assert_equal :a, dup_map.get(b.new) - assert_equal :a, dup_map.get(c.new) - end - end - - describe "with a plain hash" do - let(:lazy_method_map) { GraphQL::Execution::Lazy::LazyMethodMap.new(use_concurrent: false) } - test_lazy_method_map - - it "has a Ruby Hash inside" do - storage = lazy_method_map - .instance_variable_get(:@storage) - .instance_variable_get(:@storage) - assert_instance_of Hash, storage - end - end - - describe "with a Concurrent::Map" do - let(:lazy_method_map) { GraphQL::Execution::Lazy::LazyMethodMap.new(use_concurrent: true) } - test_lazy_method_map - - it "has a Concurrent::Map inside" do - storage = lazy_method_map.instance_variable_get(:@storage) - assert_instance_of Concurrent::Map, storage - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/lazy_spec.rb b/vendor/gems/graphql/spec/graphql/execution/lazy_spec.rb deleted file mode 100644 index 3eaa9798157..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/lazy_spec.rb +++ /dev/null @@ -1,244 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Execution::Lazy do - include LazyHelpers - - describe "resolving" do - it "calls value handlers" do - res = run_query('{ int(value: 2, plus: 1) }') - assert_equal 3, res["data"]["int"] - end - - it "Works with Query.new" do - query_str = '{ int(value: 2, plus: 1) }' - query = GraphQL::Query.new(LazyHelpers::LazySchema, query_str) - res = query.result - assert_equal 3, res["data"]["int"] - end - - it "can do nested lazy values" do - res = run_query %| - { - a: nestedSum(value: 3) { - value - nestedSum(value: 7) { - value - nestedSum(value: 1) { - value - nestedSum(value: -50) { - value - } - } - } - } - b: nestedSum(value: 2) { - value - nestedSum(value: 11) { - value - nestedSum(value: 2) { - value - nestedSum(value: -50) { - value - } - } - } - } - - c: listSum(values: [1,2]) { - nestedSum(value: 3) { - value - } - } - } - | - - expected_data = { - "a"=>{"value"=>14, "nestedSum"=>{ - "value"=>46, - "nestedSum"=>{ - "value"=>95, - "nestedSum"=>{"value"=>90} - } - }}, - "b"=>{"value"=>14, "nestedSum"=>{ - "value"=>46, - "nestedSum"=>{ - "value"=>95, - "nestedSum"=>{"value"=>90} - } - }}, - "c"=>[ - {"nestedSum"=>{"value"=>14}}, - {"nestedSum"=>{"value"=>14}} - ], - } - - assert_equal expected_data, res["data"] - end - - [ - [1, 2, LazyHelpers::MAGIC_NUMBER_WITH_LAZY_AUTHORIZED_HOOK], - [2, LazyHelpers::MAGIC_NUMBER_WITH_LAZY_AUTHORIZED_HOOK, 1], - [LazyHelpers::MAGIC_NUMBER_WITH_LAZY_AUTHORIZED_HOOK, 1, 2], - ].each do |ordered_values| - it "resolves each field at one depth before proceeding to the next depth (using #{ordered_values})" do - res = run_query <<-GRAPHQL, variables: { values: ordered_values } - query($values: [Int!]!) { - listSum(values: $values) { - nestedSum(value: 3) { - value - } - } - } - GRAPHQL - - # Even though magic number `44`'s `.authorized?` hook returns a lazy value, - # these fields should be resolved together and return the same value. - assert_equal 56, res["data"]["listSum"][0]["nestedSum"]["value"] - assert_equal 56, res["data"]["listSum"][1]["nestedSum"]["value"] - assert_equal 56, res["data"]["listSum"][2]["nestedSum"]["value"] - end - end - - it "Handles fields that return nil" do - values = [ - LazyHelpers::MAGIC_NUMBER_THAT_RETURNS_NIL, - LazyHelpers::MAGIC_NUMBER_WITH_LAZY_AUTHORIZED_HOOK, - 1, - 2, - ] - - res = run_query <<-GRAPHQL, variables: { values: values } - query($values: [Int!]!) { - listSum(values: $values) { - nullableNestedSum(value: 3) { - value - } - } - } - GRAPHQL - - values = res["data"]["listSum"].map { |s| s && s["nullableNestedSum"]["value"] } - assert_equal [nil, 56, 56, 56], values - end - - it "propagates nulls to the root" do - res = run_query %| - { - nestedSum(value: 1) { - value - nestedSum(value: 2) { - nestedSum(value: 13) { - value - } - } - } - }| - - assert_nil(res["data"]) - assert_equal 1, res["errors"].length - end - - it "propagates partial nulls" do - res = run_query %| - { - nullableNestedSum(value: 1) { - value - nullableNestedSum(value: 2) { - ns: nestedSum(value: 13) { - value - } - } - } - }| - - expected_data = { - "nullableNestedSum" => { - "value" => 1, - "nullableNestedSum" => nil, - } - } - assert_equal(expected_data, res["data"]) - assert_equal 1, res["errors"].length - end - - it "handles raised errors" do - res = run_query %| - { - a: nullableNestedSum(value: 1) { value } - b: nullableNestedSum(value: 13) { value } - c: nullableNestedSum(value: 2) { value } - }| - - expected_data = { - "a" => { "value" => 3 }, - "b" => nil, - "c" => { "value" => 3 }, - } - assert_equal expected_data, res["data"] - - expected_errors = [{ - "message"=>"13 is unlucky", - "locations"=>[{"line"=>4, "column"=>9}], - "path"=>["b"], - }] - assert_equal expected_errors, res["errors"] - end - - it "resolves mutation fields right away" do - res = run_query %| - { - a: nestedSum(value: 2) { value } - b: nestedSum(value: 4) { value } - c: nestedSum(value: 6) { value } - }| - - assert_equal [12, 12, 12], res["data"].values.map { |d| d["value"] } - - res = run_query %| - mutation { - a: nestedSum(value: 2) { value } - b: nestedSum(value: 4) { value } - c: nestedSum(value: 6) { value } - } - | - - assert_equal [2, 4, 6], res["data"].values.map { |d| d["value"] } - end - end - - describe "Schema#sync_lazy(object)" do - it "Passes objects to that hook at runtime" do - res = run_query <<-GRAPHQL - { - a: nullableNestedSum(value: 1001) { value } - b: nullableNestedSum(value: 1013) { value } - c: nullableNestedSum(value: 1002) { value } - } - GRAPHQL - - # This odd, non-adding behavior is hacked into `#sync_lazy` - assert_equal 101, res["data"]["a"]["value"] - assert_equal 113, res["data"]["b"]["value"] - assert_equal 102, res["data"]["c"]["value"] - end - end - - describe "LazyMethodMap" do - class SubWrapper < LazyHelpers::Wrapper; end - - let(:map) { GraphQL::Execution::Lazy::LazyMethodMap.new } - - it "finds methods for classes and subclasses" do - map.set(LazyHelpers::Wrapper, :item) - map.set(LazyHelpers::SumAll, :value) - b = LazyHelpers::Wrapper.new(1) - sub_b = LazyHelpers::Wrapper.new(2) - s = LazyHelpers::SumAll.new(3) - assert_equal(:item, map.get(b)) - assert_equal(:item, map.get(sub_b)) - assert_equal(:value, map.get(s)) - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/lookahead_spec.rb b/vendor/gems/graphql/spec/graphql/execution/lookahead_spec.rb deleted file mode 100644 index eb87ca33f7b..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/lookahead_spec.rb +++ /dev/null @@ -1,866 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Execution::Lookahead do - module LookaheadTest - DATA = [ - OpenStruct.new(name: "Cardinal", is_waterfowl: false, similar_species_names: ["Scarlet Tanager"], genus: OpenStruct.new(latin_name: "Piranga")), - OpenStruct.new(name: "Scarlet Tanager", is_waterfowl: false, similar_species_names: ["Cardinal"], genus: OpenStruct.new(latin_name: "Cardinalis")), - OpenStruct.new(name: "Great Egret", is_waterfowl: false, similar_species_names: ["Great Blue Heron"], genus: OpenStruct.new(latin_name: "Ardea")), - OpenStruct.new(name: "Great Blue Heron", is_waterfowl: true, similar_species_names: ["Great Egret"], genus: OpenStruct.new(latin_name: "Ardea")), - ] - - def DATA.find_by_name(name) - DATA.find { |b| b.name == name } - end - - module Node - include GraphQL::Schema::Interface - field :id, ID, null: false - end - - class BirdGenus < GraphQL::Schema::Object - implements Node - field :name, String, null: false - field :latin_name, String, null: false - field :id, ID, null: false, method: :latin_name - end - - class BirdSpecies < GraphQL::Schema::Object - implements Node - field :name, String, null: false - field :id, ID, null: false, method: :name - field :is_waterfowl, Boolean, null: false - field :similar_species, [BirdSpecies], null: false - - def similar_species - object.similar_species_names.map { |n| DATA.find_by_name(n) } - end - - field :genus, BirdGenus, null: false, - extras: [:lookahead] - - def genus(lookahead:) - if lookahead.selects?(:latin_name) - context[:lookahead_latin_name] += 1 - end - object.genus - end - end - - class PlantSpecies < GraphQL::Schema::Object - implements Node - field :name, String, null: false - field :id, ID, null: false, method: :name - field :is_edible, Boolean, null: false - end - - class Species < GraphQL::Schema::Union - possible_types BirdSpecies, PlantSpecies - end - - class Query < GraphQL::Schema::Object - field :find_bird_species, BirdSpecies do - argument :by_name, String - end - - def find_bird_species(by_name:) - DATA.find_by_name(by_name) - end - - field :node, Node do - argument :id, ID - end - - def node(id:) - if (node = DATA.find_by_name(id)) - node - else - DATA.map { |d| d.genus }.select { |g| g.name == id } - end - end - - field :species, Species do - argument :id, ID - end - - def species(id:) - DATA.find_by_name(id) - end - end - - module LookaheadInstrumenter - def execute_query(query:) - query.context[:root_lookahead_selections] = query.lookahead.selections - super - end - end - - class Schema < GraphQL::Schema - query(Query) - trace_with LookaheadInstrumenter - end - - class AlwaysVisibleSchema < Schema - use GraphQL::Schema::AlwaysVisible - end - end - - describe "looking ahead" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query($name: String!){ - findBirdSpecies(byName: $name) { - name - similarSpecies { - likesWater: isWaterfowl - } - } - t: __typename - } - GRAPHQL - } - let(:schema) { LookaheadTest::Schema } - let(:query) { - GraphQL::Query.new(schema, document: document, variables: { name: "Cardinal" }) - } - - it "has a good test setup" do - res = query.result - assert_equal [false], res["data"]["findBirdSpecies"]["similarSpecies"].map { |s| s["likesWater"] } - end - - it "can detect fields on objects with symbol or string" do - lookahead = query.lookahead.selection("findBirdSpecies") - assert_equal true, lookahead.selects?("similarSpecies") - assert_equal true, lookahead.selects?(:similar_species) - assert_equal false, lookahead.selects?("isWaterfowl") - assert_equal false, lookahead.selects?(:is_waterfowl) - end - - it "detects by name, not by alias" do - assert_equal true, query.lookahead.selects?("__typename") - end - - it "uses null lookahead when no operation is selected" do - query = GraphQL::Query.new(schema, document: document, variables: { name: "Cardinal" }, operation_name: "Invalid") - assert_selection_is_null query.lookahead - end - - describe "with a NullWarden" do - let(:schema) { LookaheadTest::AlwaysVisibleSchema } - - it "works" do - lookahead = query.lookahead.selection("findBirdSpecies") - assert_equal true, lookahead.selects?("similarSpecies") - assert_equal true, lookahead.selects?(:similar_species) - assert_equal false, lookahead.selects?("isWaterfowl") - assert_equal false, lookahead.selects?(:is_waterfowl) - end - end - - describe "on unions" do - let(:document) { - GraphQL.parse <<-GRAPHQL - { - species(id: "Cardinal") { - ... on BirdSpecies { - name - isWaterfowl - } - ... on PlantSpecies { - name - isEdible - } - } - } - GRAPHQL - } - - it "works" do - lookahead = query.lookahead.selection(:species) - assert lookahead.selects?(:name) - assert_equal [:name, :is_waterfowl, :name, :is_edible], lookahead.selections.map(&:name) - end - - it "works with different selected types" do - lookahead = query.lookahead.selection(:species) - # Both have `name` - assert lookahead.selects?(:name, selected_type: LookaheadTest::BirdSpecies) - assert lookahead.selects?(:name, selected_type: LookaheadTest::PlantSpecies) - # Only birds have `isWaterfowl` - assert lookahead.selects?(:is_waterfowl, selected_type: LookaheadTest::BirdSpecies) - refute lookahead.selects?(:is_waterfowl, selected_type: LookaheadTest::PlantSpecies) - # Only plants have `isEdible` - refute lookahead.selects?(:is_edible, selected_type: LookaheadTest::BirdSpecies) - assert lookahead.selects?(:is_edible, selected_type: LookaheadTest::PlantSpecies) - end - end - - describe "fields on interfaces" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - node(id: "Cardinal") { - id - ... on BirdSpecies { - name - } - ...Other - } - } - fragment Other on BirdGenus { - latinName - } - GRAPHQL - } - - it "finds fields on object types and interface types" do - node_lookahead = query.lookahead.selection("node") - assert_equal [:id, :name, :latin_name], node_lookahead.selections.map(&:name) - end - end - - describe "inspect" do - it "works for root lookaheads" do - assert_includes query.lookahead.inspect, "# "Cardinal" }) - end - - it "is true when no constraints are given" do - assert_equal true, lookahead.selects?(:find_bird_species, arguments: {}) - assert_equal true, lookahead.selects?("__typename", arguments: {}) - end - - it "is false when some given constraints aren't satisfied" do - assert_equal false, lookahead.selects?(:find_bird_species, arguments: { by_name: "Chickadee" }) - assert_equal false, lookahead.selects?(:find_bird_species, arguments: { by_name: "Cardinal", other: "Nonsense" }) - end - - describe "with literal values" do - let(:document) { - GraphQL.parse <<-GRAPHQL - { - findBirdSpecies(byName: "Great Blue Heron") { - isWaterfowl - } - } - GRAPHQL - } - - it "works" do - assert_equal true, lookahead.selects?(:find_bird_species, arguments: { by_name: "Great Blue Heron" }) - end - end - end - - it "can do a chained lookahead" do - next_lookahead = query.lookahead.selection(:find_bird_species, arguments: { by_name: "Cardinal" }) - assert_equal true, next_lookahead.selected? - nested_selection = next_lookahead.selection(:similar_species).selection(:is_waterfowl, arguments: {}) - assert_equal true, nested_selection.selected? - assert_equal false, next_lookahead.selection(:similar_species).selection(:name).selected? - end - - it "can detect fields on lists with symbol or string" do - assert_equal true, query.lookahead.selection(:find_bird_species).selection(:similar_species).selection(:is_waterfowl).selected? - assert_equal true, query.lookahead.selection("findBirdSpecies").selection("similarSpecies").selection("isWaterfowl").selected? - end - - describe "merging branches and fragments" do - let(:document) { - GraphQL.parse <<-GRAPHQL - { - findBirdSpecies(name: "Cardinal") { - similarSpecies { - __typename - } - } - ...F - ... { - findBirdSpecies(name: "Cardinal") { - similarSpecies { - isWaterfowl - } - } - } - } - - fragment F on Query { - findBirdSpecies(name: "Cardinal") { - similarSpecies { - name - } - } - } - GRAPHQL - } - - it "finds selections using merging" do - merged_lookahead = query.lookahead.selection(:find_bird_species).selection(:similar_species) - assert merged_lookahead.selects?(:__typename) - assert merged_lookahead.selects?(:is_waterfowl) - assert merged_lookahead.selects?(:name) - end - end - end - - describe "in queries" do - it "can be an extra" do - query_str = <<-GRAPHQL - { - cardinal: findBirdSpecies(byName: "Cardinal") { - genus { __typename } - } - scarletTanager: findBirdSpecies(byName: "Scarlet Tanager") { - genus { latinName } - } - greatBlueHeron: findBirdSpecies(byName: "Great Blue Heron") { - genus { latinName } - } - } - GRAPHQL - context = {lookahead_latin_name: 0} - res = LookaheadTest::Schema.execute(query_str, context: context) - refute res.key?("errors") - assert_equal 2, context[:lookahead_latin_name] - assert_equal [:find_bird_species], context[:root_lookahead_selections].map(&:name).uniq - assert_equal( - [{ by_name: "Cardinal" }, { by_name: "Scarlet Tanager" }, { by_name: "Great Blue Heron" }], - context[:root_lookahead_selections].map(&:arguments) - ) - end - - it "works for invalid queries" do - context = {lookahead_latin_name: 0} - res = LookaheadTest::Schema.execute("{ doesNotExist }", context: context) - assert res.key?("errors") - assert_equal 0, context[:lookahead_latin_name] - end - - describe "When there is an argument error" do - class NestedArgumentErrorSchema < GraphQL::Schema - class Data < GraphQL::Schema::Object - field :echo, String do - argument :input, String - end - - def echo(input:) - input - end - end - - class Query < GraphQL::Schema::Object - field :data, Data, extras: [:lookahead] - - def data(lookahead:) - context[:args_class] = lookahead.selection(:echo).arguments.class - {} - end - end - - query(Query) - end - - it "uses empty arguments" do - query_str = "query getEcho($input: String = null) { data { echo(input: $input) } }" - res = NestedArgumentErrorSchema.execute(query_str, variables: {}) - assert_equal ["`null` is not a valid input for `String!`, please provide a value for this argument."], res["errors"].map { |err| err["message"] } - assert_equal Hash, res.context[:args_class] - - good_res = NestedArgumentErrorSchema.execute("{ data { echo(input: \"Hello\") } }") - assert_equal "Hello", good_res["data"]["data"]["echo"] - assert_equal Hash, good_res.context[:args_class] - end - end - end - - describe '#selections' do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - findBirdSpecies(byName: "Laughing Gull") { - name - similarSpecies { - likesWater: isWaterfowl - } - } - } - GRAPHQL - } - - def query(doc = document) - GraphQL::Query.new(LookaheadTest::Schema, document: doc) - end - - it "provides a list of all selections" do - ast_node = document.definitions.first.selections.first - field = LookaheadTest::Query.fields["findBirdSpecies"] - lookahead = GraphQL::Execution::Lookahead.new(query: query, ast_nodes: [ast_node], field: field) - assert_equal [:name, :similar_species], lookahead.selections.map(&:name) - end - - it "filters outs selections which do not match arguments" do - ast_node = document.definitions.first - lookahead = GraphQL::Execution::Lookahead.new(query: query, ast_nodes: [ast_node], root_type: LookaheadTest::Query) - arguments = { by_name: "Cardinal" } - - assert_equal lookahead.selections(arguments: arguments).map(&:name), [] - end - - it "includes selections which match arguments" do - ast_node = document.definitions.first - lookahead = GraphQL::Execution::Lookahead.new(query: query, ast_nodes: [ast_node], root_type: LookaheadTest::Query) - arguments = { by_name: "Laughing Gull" } - - assert_equal lookahead.selections(arguments: arguments).map(&:name), [:find_bird_species] - end - - it 'handles duplicate selections across fragments' do - doc = GraphQL.parse <<-GRAPHQL - query { - ... on Query { - ...MoreFields - } - } - - fragment MoreFields on Query { - findBirdSpecies(byName: "Laughing Gull") { - name - } - findBirdSpecies(byName: "Laughing Gull") { - ...EvenMoreFields - } - } - - fragment EvenMoreFields on BirdSpecies { - similarSpecies { - likesWater: isWaterfowl - } - } - GRAPHQL - - lookahead = query(doc).lookahead - - root_selections = lookahead.selections - assert_equal [:find_bird_species], root_selections.map(&:name), "Selections are merged" - assert_equal 2, root_selections.first.ast_nodes.size, "It represents both nodes" - - assert_equal [:name, :similar_species], root_selections.first.selections.map(&:name), "Subselections are merged" - end - - it "avoids merging selections for same field name on distinct types" do - query = GraphQL::Query.new(LookaheadTest::Schema, <<-GRAPHQL) - query { - node(id: "Cardinal") { - ... on BirdSpecies { - name - } - ... on BirdGenus { - name - } - id - } - } - GRAPHQL - - node_lookahead = query.lookahead.selection("node") - assert_equal( - [[LookaheadTest::Node, :id], [LookaheadTest::BirdSpecies, :name], [LookaheadTest::BirdGenus, :name]], - node_lookahead.selections.map { |s| [s.owner_type, s.name] } - ) - end - - it "works for missing selections" do - ast_node = document.definitions.first.selections.first - field = LookaheadTest::Query.fields["findBirdSpecies"] - lookahead = GraphQL::Execution::Lookahead.new(query: query, ast_nodes: [ast_node], field: field) - null_lookahead = lookahead.selection(:genus) - # This is an implementation detail, but I want to make sure the test is set up right - assert_instance_of GraphQL::Execution::Lookahead::NullLookahead, null_lookahead - assert_equal [], null_lookahead.selections - end - - it "excludes fields skipped by directives" do - document = GraphQL.parse <<-GRAPHQL - query($skipName: Boolean!, $includeGenus: Boolean!){ - findBirdSpecies(byName: "Cardinal") { - id - name @skip(if: $skipName) - genus @include(if: $includeGenus) - } - } - GRAPHQL - query = GraphQL::Query.new(LookaheadTest::Schema, document: document, - variables: { skipName: false, includeGenus: true }) - lookahead = query.lookahead.selection("findBirdSpecies") - assert_equal [:id, :name, :genus], lookahead.selections.map(&:name) - assert_equal true, lookahead.selects?(:name) - - query = GraphQL::Query.new(LookaheadTest::Schema, document: document, - variables: { skipName: true, includeGenus: false }) - lookahead = query.lookahead.selection("findBirdSpecies") - assert_equal [:id], lookahead.selections.map(&:name) - assert_equal false, lookahead.selects?(:name) - end - end - - def assert_selection_exists(selection) - assert GraphQL::Execution::Lookahead::NULL_LOOKAHEAD != selection - end - - def assert_selection_is_null(selection) - assert_equal GraphQL::Execution::Lookahead::NULL_LOOKAHEAD, selection - end - - describe "#selection" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - findBirdSpecies(byName: "Laughing Gull") { - name - similarSpecies { - likesWater: isWaterfowl - } - } - } - GRAPHQL - } - - def query(doc = document) - GraphQL::Query.new(LookaheadTest::Schema, document: doc) - end - - it "returns selection by field name" do - ast_node = document.definitions.first.selections.first - field = LookaheadTest::Query.fields["findBirdSpecies"] - lookahead = GraphQL::Execution::Lookahead.new(query: query, ast_nodes: [ast_node], field: field) - assert_selection_exists lookahead.selection("similarSpecies") - end - - describe "when same field is selected twice" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - gull: findBirdSpecies(byName: "Laughing Gull") { - name - } - - tanager: findBirdSpecies(byName: "Scarlet Tanager") { - name - } - } - GRAPHQL - } - - let(:graphql_query) do - GraphQL::Query.new(LookaheadTest::Schema, document: document) - end - - it "returns lookahead with two ast_nodes" do - assert_equal 2, graphql_query.lookahead.selection("findBirdSpecies").ast_nodes.length - end - end - - describe "when query has alias" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - findBirdSpecies(byName: "Laughing Gull") { - name - similar: similarSpecies { - likesWater: isWaterfowl - } - } - } - GRAPHQL - } - - let(:graphql_query) do - GraphQL::Query.new(LookaheadTest::Schema, document: document) - end - - let(:species_lookahead) do - graphql_query.lookahead.selection("findBirdSpecies") - end - - it "returns selection when field name is passed" do - assert_selection_exists species_lookahead.selection("similarSpecies") - end - - it "returns null when alias name is passed" do - assert_selection_is_null species_lookahead.selection("similar") - end - - describe "when alias has arguments" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - gull: findBirdSpecies(byName: "Laughing Gull") { - name - } - } - GRAPHQL - } - - it "returns selection when field name is passed" do - assert_selection_exists graphql_query.lookahead.selection("findBirdSpecies") - end - - it "returns null when alias name is passed" do - assert_selection_is_null graphql_query.lookahead.selection("gull") - end - - describe "when same field is selected twice" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - gull: findBirdSpecies(byName: "Laughing Gull") { - name - } - - tanager: findBirdSpecies(byName: "Scarlet Tanager") { - name - } - } - GRAPHQL - } - - it "returns null when alias name is passed" do - assert_selection_is_null graphql_query.lookahead.selection("gull") - assert_selection_is_null graphql_query.lookahead.selection("tanager") - end - end - end - end - end - - describe "#alias_selection" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - findBirdSpecies(byName: "Laughing Gull") { - name - similar: similarSpecies { - likesWater: isWaterfowl - } - } - } - GRAPHQL - } - - def query(doc = document) - GraphQL::Query.new(LookaheadTest::Schema, document: doc) - end - - let(:graphql_query) do - GraphQL::Query.new(LookaheadTest::Schema, document: document) - end - - let(:species_lookahead) do - graphql_query.lookahead.selection("findBirdSpecies") - end - - describe "when alias name is passed" do - it "returns selection" do - assert_selection_exists species_lookahead.alias_selection("similar") - end - - it "returns true from selects_alias?" do - assert true, species_lookahead.selects_alias?("similar") - end - - describe "when the aliased field is deeply nested" do - it "not finds the deeply-nested alias" do - assert_equal [:name, :similar_species], species_lookahead.selections.map(&:name) - assert_equal false, species_lookahead.selects_alias?("likesWater") - end - end - end - - describe "when the same field is executed with the same arguments but different aliases" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - egret: findBirdSpecies(byName: "Great Egret") { - isWaterfowl - } - otherEgret: findBirdSpecies(byName: "Great Egret") { - name - } - findBirdSpecies(byName: "Great Egret") { - __typename - } - } - GRAPHQL - } - - it "distinguishes between the aliased fields" do - lookahead = query.lookahead - assert_equal [:is_waterfowl], lookahead.alias_selection("egret").selections.map(&:name) - assert_equal [:name], lookahead.alias_selection("otherEgret").selections.map(&:name) - assert_equal [], lookahead.alias_selection("findBirdSpecies").selections.map(&:name) - end - - it "filters aliased fields by arguments" do - lookahead = query.lookahead - # No `arguments:` performs no filtering - assert_equal [:is_waterfowl], lookahead.alias_selection("egret").selections.map(&:name) - # Matching arguments filters to the expected field: - assert_equal [:is_waterfowl], lookahead.alias_selection("egret", arguments: {by_name: "Great Egret"}).selections.map(&:name) - # Empty `arguments:` matches nothing: - assert_equal [], lookahead.alias_selection("egret", arguments: {}).selections.map(&:name) - # Mismatching `arguments:` filters to nothing: - assert_equal [], lookahead.alias_selection("egret", arguments: {by_name: "Macaw"}).selections.map(&:name) - end - end - - describe "when field name is passed" do - it "returns null_lookahead" do - assert_selection_is_null species_lookahead.alias_selection("similarSpecies") - end - - it "returns false from selects_alias?" do - assert_equal false, species_lookahead.selects_alias?("similarSpecies") - end - end - - describe "when alias is inside fragment" do - let(:document) { - GraphQL.parse <<-GRAPHQL - fragment BirdSpeciesFragment on BirdSpecies { - name - similar: similarSpecies { - likesWater: isWaterfowl - } - } - - query { - findBirdSpecies(byName: "Laughing Gull") { - ...BirdSpeciesFragment - } - } - GRAPHQL - } - - it "returns selection" do - assert_selection_exists species_lookahead.alias_selection("similar") - end - - it "returns true from selects_alias?" do - assert true, species_lookahead.selects_alias?("similar") - end - - describe "when fragment name is wrong" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - findBirdSpecies(byName: "Laughing Gull") { - ...WrongFragment - } - } - GRAPHQL - } - - it "raises error" do - assert_raises(RuntimeError) { - species_lookahead.selects_alias?("similar") - } - end - end - end - - describe "when alias is inside inline fragment" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - findBirdSpecies(byName: "Laughing Gull") { - ...on BirdSpecies { - name - similar: similarSpecies { - likesWater: isWaterfowl - } - } - } - } - GRAPHQL - } - - it "returns selection" do - assert_selection_exists species_lookahead.alias_selection("similar") - end - - it "returns true from selects_alias?" do - assert true, species_lookahead.selects_alias?("similar") - end - end - - describe "when alias has arguments" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - gull: findBirdSpecies(byName: "Laughing Gull") { - name - } - } - GRAPHQL - } - - it "returns selection" do - assert_selection_exists graphql_query.lookahead.alias_selection("gull") - end - - it "returns true from selects_alias?" do - assert true, graphql_query.lookahead.selects_alias?("gull") - end - - describe "when same field is selected twice" do - let(:document) { - GraphQL.parse <<-GRAPHQL - query { - gull: findBirdSpecies(byName: "Laughing Gull") { - name - } - - tanager: findBirdSpecies(byName: "Scarlet Tanager") { - name - } - } - GRAPHQL - } - - it "returns selection when alias name is passed" do - graphql_query.lookahead.alias_selection("gull", arguments: { by_name: "Laughing Gull" }).tap do |selection| - assert_selection_exists selection - assert_equal({ by_name: "Laughing Gull" }, selection.arguments) - assert_equal 1, selection.ast_nodes.length - end - - graphql_query.lookahead.alias_selection("tanager", arguments: { by_name: "Scarlet Tanager" }).tap do |selection| - assert_selection_exists selection - assert_equal({ by_name: "Scarlet Tanager" }, selection.arguments) - assert_equal 1, selection.ast_nodes.length - end - end - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution/multiplex_spec.rb b/vendor/gems/graphql/spec/graphql/execution/multiplex_spec.rb deleted file mode 100644 index ec545720d8e..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution/multiplex_spec.rb +++ /dev/null @@ -1,286 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Execution::Multiplex do - def multiplex(*a, **kw) - LazyHelpers::LazySchema.multiplex(*a, **kw) - end - - let(:q1) { <<-GRAPHQL - query Q1 { - nestedSum(value: 3) { - value - nestedSum(value: 7) { - value - } - } - } - GRAPHQL - } - let(:q2) { <<-GRAPHQL - query Q2 { - nestedSum(value: 2) { - value - nestedSum(value: 11) { - value - } - } - } - GRAPHQL - } - let(:q3) { <<-GRAPHQL - query Q3 { - listSum(values: [1,2]) { - nestedSum(value: 3) { - value - } - } - } - GRAPHQL - } - - let(:queries) { [{query: q1}, {query: q2}, {query: q3}] } - - describe "multiple queries in the same lazy context" do - it "runs multiple queries in the same lazy context" do - expected_data = [ - {"data"=>{"nestedSum"=>{"value"=>14, "nestedSum"=>{"value"=>46}}}}, - {"data"=>{"nestedSum"=>{"value"=>14, "nestedSum"=>{"value"=>46}}}}, - {"data"=>{"listSum"=>[{"nestedSum"=>{"value"=>14}}, {"nestedSum"=>{"value"=>14}}]}}, - ] - - res = multiplex(queries) - assert_equal expected_data, res - end - - it "returns responses in the same order as their respective requests" do - queries = 2000.times.map do |index| - case index % 3 - when 0 - {query: q1} - when 1 - {query: q2} - when 2 - {query: q3} - end - end - - responses = multiplex(queries) - - responses.each.with_index do |response, index| - case index % 3 - when 0 - assert_equal "Q1", response.query.operation_name - when 1 - assert_equal "Q2", response.query.operation_name - when 2 - assert_equal "Q3", response.query.operation_name - end - end - end - end - - describe "when some have validation errors or runtime errors" do - let(:q1) { " { success: nullableNestedSum(value: 1) { value } }" } - let(:q2) { " { runtimeError: nullableNestedSum(value: 13) { value } }" } - let(:q3) { "{ - invalidNestedNull: nullableNestedSum(value: 1) { - value - nullableNestedSum(value: 2) { - nestedSum(value: 13) { - value - } - # This field will never get executed - ns2: nestedSum(value: 13) { - value - } - } - } - }" } - let(:q4) { " { validationError: nullableNestedSum(value: true) }"} - - it "returns a mix of errors and values" do - expected_res = [ - { - "data"=>{"success"=>{"value"=>2}} - }, - { - "data"=>{"runtimeError"=>nil}, - "errors"=>[{ - "message"=>"13 is unlucky", - "locations"=>[{"line"=>1, "column"=>4}], - "path"=>["runtimeError"] - }] - }, - { - "data"=>{"invalidNestedNull"=>{"value" => 2,"nullableNestedSum" => nil}}, - "errors"=>[{ - "message"=>"Cannot return null for non-nullable field LazySum.nestedSum", - "path"=>["invalidNestedNull", "nullableNestedSum", "nestedSum"], - "locations"=>[{"line"=>5, "column"=>11}], - }], - }, - { - "errors" => [{ - "message"=>"Field must have selections (field 'nullableNestedSum' returns LazySum but has no selections. Did you mean 'nullableNestedSum { ... }'?)", - "locations"=>[{"line"=>1, "column"=>4}], - "path"=>["query", "validationError"], - "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"field 'nullableNestedSum'", "typeName"=>"LazySum"} - }] - }, - ] - - res = multiplex([ - {query: q1}, - {query: q2}, - {query: q3}, - {query: q4}, - ]) - assert_equal expected_res, res.map(&:to_h) - end - end - - describe "context shared by a multiplex run" do - it "is provided as context:" do - checks = [] - multiplex(queries, context: { instrumentation_checks: checks }) - assert_equal ["before multiplex 1", "before multiplex 2", "after multiplex 2", "after multiplex 1"], checks - end - end - - describe "instrumenting a multiplex run" do - it "runs query instrumentation for each query and multiplex-level instrumentation" do - checks = [] - queries_with_context = queries.map { |q| q.merge(context: { instrumentation_checks: checks }) } - multiplex(queries_with_context, context: { instrumentation_checks: checks }) - assert_equal [ - "before multiplex 1", - "before multiplex 2", - "before Q1", "before Q2", "before Q3", - "after Q3", "after Q2", "after Q1", - "after multiplex 2", - "after multiplex 1", - ], checks - end - end - - describe "max_complexity" do - it "can successfully calculate complexity" do - message = "Query has complexity of 11, which exceeds max complexity of 10" - results = multiplex(queries, max_complexity: 10) - - results.each do |res| - assert_equal message, res["errors"][0]["message"] - end - end - end - - describe "execute_query when errors are raised" do - module InspectQueryInstrumentation - def execute_multiplex(multiplex:) - super - ensure - InspectQueryInstrumentation.last_json = multiplex.queries.first.result.to_json - end - - class << self - attr_accessor :last_json - end - end - - class InspectSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :raise_execution_error, String - - def raise_execution_error - raise GraphQL::ExecutionError, "Whoops" - end - - field :raise_error, String - - def raise_error - raise GraphQL::Error, "Crash" - end - - field :raise_syntax_error, String - - def raise_syntax_error - raise SyntaxError - end - - field :raise_exception, String - - def raise_exception - raise Exception - end - end - - query(Query) - trace_with(InspectQueryInstrumentation) - end - - unhandled_err_json = '{}' - - it "can access the query results" do - InspectSchema.execute("{ raiseExecutionError }") - handled_err_json = '{"errors":[{"message":"Whoops","locations":[{"line":1,"column":3}],"path":["raiseExecutionError"]}],"data":{"raiseExecutionError":null}}' - assert_equal handled_err_json, InspectQueryInstrumentation.last_json - - - assert_raises(GraphQL::Error) do - InspectSchema.execute("{ raiseError }") - end - - assert_equal unhandled_err_json, InspectQueryInstrumentation.last_json - end - - it "can access the query results when the error is not a StandardError" do - assert_raises(SyntaxError) do - InspectSchema.execute("{ raiseSyntaxError }") - end - assert_equal unhandled_err_json, InspectQueryInstrumentation.last_json - - assert_raises(Exception) do - InspectSchema.execute("{ raiseException }") - end - assert_equal unhandled_err_json, InspectQueryInstrumentation.last_json - end - end - - describe "context[:trace]" do - class MultiplexTraceSchema < GraphQL::Schema - class Query < GraphQL::Schema::Object - field :int, Integer - def int; 1; end - end - - class Trace < GraphQL::Tracing::Trace - def execute_multiplex(multiplex:) - @execute_multiplex_count ||= 0 - @execute_multiplex_count += 1 - super - end - - def execute_query(query:) - @execute_query_count ||= 0 - @execute_query_count += 1 - super - end - - attr_reader :execute_multiplex_count, :execute_query_count - end - - query(Query) - end - - it "uses it instead of making a new trace" do - query_str = "{ int }" - trace_instance = MultiplexTraceSchema::Trace.new - res = MultiplexTraceSchema.multiplex([{query: query_str}, {query: query_str}], context: { trace: trace_instance }) - assert_equal [1, 1], res.map { |r| r["data"]["int"]} - - assert_equal 1, trace_instance.execute_multiplex_count - assert_equal 2, trace_instance.execute_query_count - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/execution_error_spec.rb b/vendor/gems/graphql/spec/graphql/execution_error_spec.rb deleted file mode 100644 index 432a1a6e691..00000000000 --- a/vendor/gems/graphql/spec/graphql/execution_error_spec.rb +++ /dev/null @@ -1,497 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::ExecutionError do - let(:result) { Dummy::Schema.execute(query_string) } - describe "when returned from a field" do - let(:query_string) {%| - { - cheese(id: 1) { - id - error1: similarCheese(source: [YAK]) { - ... similarCheeseFields - } - error2: similarCheese(source: [YAK]) { - ... similarCheeseFields - } - nonError: similarCheese(source: [SHEEP]) { - ... similarCheeseFields - } - flavor - } - allDairy { - ... on Cheese { - flavor - } - ... on Milk { - source - executionError - } - } - dairyErrors: allDairy(executionErrorAtIndex: 1) { - __typename - } - dairy { - milks { - source - executionError - allDairy { - __typename - ... on Milk { - origin - executionError - } - } - } - } - executionError - valueWithExecutionError - } - - fragment similarCheeseFields on Cheese { - id, flavor - } - |} - - it "the error is inserted into the errors key and the rest of the query is fulfilled" do - expected_result = { - "data"=>{ - "dairy" => { - "milks" => [ - { - "source" => "COW", - "executionError" => nil, - "allDairy" => [ - { "__typename" => "Cheese" }, - { "__typename" => "Cheese" }, - { "__typename" => "Cheese" }, - { "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil } - ] - } - ] - }, - "executionError" => nil, - "valueWithExecutionError" => 0, - "cheese"=>{ - "id" => 1, - "flavor" => "Brie", - "error1"=> nil, - "error2"=> nil, - "nonError"=> { - "id" => 3, - "flavor" => "Manchego", - }, - }, - "allDairy" => [ - { "flavor" => "Brie" }, - { "flavor" => "Gouda" }, - { "flavor" => "Manchego" }, - { "source" => "COW", "executionError" => nil } - ], - "dairyErrors" => [ - { "__typename" => "Cheese" }, - nil, - { "__typename" => "Cheese" }, - { "__typename" => "Milk" } - ], - }, - "errors"=>[ - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>41, "column"=>5}], - "path"=>["executionError"] - }, - { - "message"=>"Could not fetch latest value", - "locations"=>[{"line"=>42, "column"=>5}], - "path"=>["valueWithExecutionError"] - }, - { - "message"=>"missing dairy", - "locations"=>[{"line"=>25, "column"=>5}], - "path"=>["dairyErrors", 1] - }, - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>31, "column"=>9}], - "path"=>["dairy", "milks", 0, "executionError"] - }, - { - "message"=>"No cheeses are made from Yak milk!", - "locations"=>[{"line"=>5, "column"=>7}], - "path"=>["cheese", "error1"] - }, - { - "message"=>"No cheeses are made from Yak milk!", - "locations"=>[{"line"=>8, "column"=>7}], - "path"=>["cheese", "error2"] - }, - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>22, "column"=>9}], - "path"=>["allDairy", 3, "executionError"] - }, - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>36, "column"=>13}], - "path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"] - }, - ] - } - assert_equal(expected_result, result.to_h) - end - end - - describe "named query when returned from a field" do - let(:query_string) {%| - query MilkQuery { - dairy { - milks { - source - executionError - allDairy { - __typename - ... on Milk { - origin - executionError - } - } - } - } - } - |} - it "the error is inserted into the errors key and the rest of the query is fulfilled" do - expected_result = { - "data"=>{ - "dairy" => { - "milks" => [ - { - "source" => "COW", - "executionError" => nil, - "allDairy" => [ - { "__typename" => "Cheese" }, - { "__typename" => "Cheese" }, - { "__typename" => "Cheese" }, - { "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil } - ] - } - ] - } - }, - "errors"=>[ - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>6, "column"=>11}], - "path"=>["dairy", "milks", 0, "executionError"] - }, - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>11, "column"=>15}], - "path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"] - } - ] - } - assert_equal(expected_result, result) - end - end - - describe "minimal lazy non-error case" do - let(:query_string) {%| - { - cheese(id: 1) { - nonError: similarCheese(source: [SHEEP]) { - id - } - } - } - |} - - it "does lazy non-errors right" do - # This is extracted from the test above -- it kept breaking - # when working on dataloader, so I isolated it to keep an eye - # on the minimal reproduction - # - # It's `def self.authorized?` is lazy, and it requires - # _both_ a lazy resolution and a dataloader run - # in order to resolve properly. - expected_result = { - "data"=>{ - "cheese"=>{ - "nonError"=> { - "id" => 3, - }, - }, - } - } - assert_equal(expected_result, result.to_h) - end - end - - describe "fragment query when returned from a field" do - let(:query_string) {%| - query MilkQuery { - dairy { - ...Dairy - } - } - - fragment Dairy on Dairy { - milks { - source - executionError - allDairy { - __typename - ...Milk - } - } - } - - fragment Milk on Milk { - origin - executionError - } - |} - it "the error is inserted into the errors key and the rest of the query is fulfilled" do - expected_result = { - "data"=>{ - "dairy" => { - "milks" => [ - { - "source" => "COW", - "executionError" => nil, - "allDairy" => [ - { "__typename" => "Cheese" }, - { "__typename" => "Cheese" }, - { "__typename" => "Cheese" }, - { "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil } - ] - } - ] - } - }, - "errors"=>[ - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>11, "column"=>9}], - "path"=>["dairy", "milks", 0, "executionError"] - }, - { - "message"=>"There was an execution error", - "locations"=>[{"line"=>21, "column"=>7}], - "path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"] - } - ] - } - assert_equal(expected_result, result) - end - end - - describe "options in ExecutionError" do - let(:query_string) {%| - { - executionErrorWithOptions - } - |} - it "the error is inserted into the errors key and the rest of the query is fulfilled" do - expected_result = { - "data"=>{"executionErrorWithOptions"=>nil}, - "errors"=> - [{"message"=>"Permission Denied!", - "locations"=>[{"line"=>3, "column"=>7}], - "path"=>["executionErrorWithOptions"], - "code"=>"permission_denied"}] - } - assert_equal(expected_result, result) - end - end - - describe "extensions in ExecutionError" do - let(:query_string) {%| - { - executionErrorWithExtensions - } - |} - it "the error is inserted into the errors key with custom data set in `extensions`" do - expected_result = { - "data"=>{"executionErrorWithExtensions"=>nil}, - "errors"=> - [{"message"=>"Permission Denied!", - "locations"=>[{"line"=>3, "column"=>7}], - "path"=>["executionErrorWithExtensions"], - "extensions"=>{"code"=>"permission_denied"}}] - } - assert_equal(expected_result, result) - end - end - - describe "more than one ExecutionError" do - let(:query_string) { %|{ multipleErrorsOnNonNullableField} |} - it "the errors are inserted into the errors key and the data is nil even for a NonNullable field" do - expected_result = { - "data"=>nil, - "errors"=> - [{"message"=>"This is an error message for some error.", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["multipleErrorsOnNonNullableField"]}, - {"message"=>"This is another error message for a different error.", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["multipleErrorsOnNonNullableField"]}], - } - assert_equal(expected_result, result) - end - - describe "more than one ExecutionError on a field defined to return a list" do - let(:query_string) { %|{ multipleErrorsOnNonNullableListField} |} - it "the errors are inserted into the errors key and the data is nil even for a NonNullable field" do - expected_result = { - "data"=>{"multipleErrorsOnNonNullableListField"=>[nil, nil]}, - "errors"=> - [{"message"=>"The first error message for a field defined to return a list of strings.", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["multipleErrorsOnNonNullableListField", 0]}, - {"message"=>"The second error message for a field defined to return a list of strings.", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["multipleErrorsOnNonNullableListField", 1]}], - } - assert_equal(expected_result, result) - end - end - end - - it "supports arrays containing only execution errors for list fields" do - schema = GraphQL::Schema.from_definition <<-GRAPHQL - type Query { - testArray: [String]! - } - GRAPHQL - - root_value = OpenStruct.new(testArray: [GraphQL::ExecutionError.new("boom!"), GraphQL::ExecutionError.new("bang!"), "OK"]) - result = schema.execute("{ testArray }", root_value: root_value) - assert_equal({ "testArray" => [nil, nil, "OK"]}, result["data"]) - expected_errors = [ - { - "message"=>"boom!", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["testArray", 0] - }, - { - "message"=>"bang!", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["testArray", 1] - } - ] - assert_equal(expected_errors, result["errors"]) - - root_value_errors_only = OpenStruct.new(testArray: [GraphQL::ExecutionError.new("zing!"), GraphQL::ExecutionError.new("fizz!")]) - result = schema.execute("{ testArray }", root_value: root_value_errors_only) - assert_equal({ "testArray" => [nil, nil] }, result["data"]) - expected_errors = [ - { - "message"=>"zing!", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["testArray", 0] - }, - { - "message"=>"fizz!", - "locations"=>[{"line"=>1, "column"=>3}], - "path"=>["testArray", 1] - } - ] - assert_equal(expected_errors, result["errors"]) - end - - describe "when using DataLoaders" do - let(:schema) do - item_error_loader = Class.new(GraphQL::Dataloader::Source) do - def fetch(keys) - keys.map { |key| GraphQL::ExecutionError.new("Error for #{key}") } - end - end - - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name "Query" - field :item, String do - argument :key, String - end - define_method(:item) do |key:| - dataloader.with(item_error_loader).load(key) - end - end - - Class.new(GraphQL::Schema) do - query query_type - use GraphQL::Dataloader - end - end - - let(:result) { schema.execute(query_string) } - - describe "when querying for unique items" do - let(:query_string) { - <<-GRAPHQL - query { - query0: item(key: "a") - query1: item(key: "b") - } - GRAPHQL - } - - it "returns unique execution errors locations and paths" do - expected_result = { - "data" => { - "query0" => nil, - "query1" => nil - }, - "errors" => [ - { - "message" => "Error for a", - "locations" => [{"line" => 2, "column" => 13}], - "path" => ["query0"] - }, - { - "message" => "Error for b", - "locations" => [{"line" => 3, "column" => 13}], - "path" => ["query1"] - } - ] - } - - assert_equal(expected_result, result.to_h) - end - end - - describe "when querying for duplicate items" do - let(:query_string) { - <<-GRAPHQL - query { - query0: item(key: "a") - query1: item(key: "a") - } - GRAPHQL - } - - it "returns execution errors for duplicate items" do - expected_result = { - "data" => { - "query0" => nil, - "query1" => nil - }, - "errors" => [ - { - "message" => "Error for a", - "locations" => [{"line" => 2, "column" => 13}], - "path" => ["query0"] - }, - { - "message" => "Error for a", - "locations" => [{"line" => 3, "column" => 13}], - "path" => ["query1"] - } - ] - } - - assert_equal(expected_result, result.to_h) - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/introspection/directive_type_spec.rb b/vendor/gems/graphql/spec/graphql/introspection/directive_type_spec.rb deleted file mode 100644 index 2df958f741a..00000000000 --- a/vendor/gems/graphql/spec/graphql/introspection/directive_type_spec.rb +++ /dev/null @@ -1,164 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Introspection::DirectiveType do - let(:query_string) {%| - query getDirectives { - __schema { - directives { - name, - args { name, type { kind, name, ofType { name } } }, - locations - isRepeatable - # Deprecated fields: - onField - onFragment - onOperation - } - } - } - |} - - let(:directive_with_deprecated_arg) do - Class.new(GraphQL::Schema::Directive) do - graphql_name "customTransform" - locations GraphQL::Schema::Directive::FIELD - argument :old_way, String, required: false, deprecation_reason: "Use the newWay" - argument :new_way, String, required: false - end - end - - let(:schema) { Class.new(Dummy::Schema) { directive(Class.new(GraphQL::Schema::Directive) { graphql_name("doStuff"); repeatable(true) })}} - let(:result) { schema.execute(query_string) } - before do - schema.max_depth(100) - end - - it "shows directive info " do - expected = { "data" => { - "__schema" => { - "directives" => [ - { - "name" => "deprecated", - "args" => [ - {"name"=>"reason", "type"=>{"kind"=>"SCALAR", "name"=>"String", "ofType"=>nil}} - ], - "locations"=>["FIELD_DEFINITION", "ENUM_VALUE", "ARGUMENT_DEFINITION", "INPUT_FIELD_DEFINITION"], - "isRepeatable" => false, - "onField" => false, - "onFragment" => false, - "onOperation" => false, - }, - { - "name"=>"directiveForVariableDefinition", - "args"=>[], - "locations"=>["VARIABLE_DEFINITION"], - "isRepeatable"=>false, - "onField"=>false, - "onFragment"=>false, - "onOperation"=>false, - }, - { - "name"=>"doStuff", - "args"=>[], - "locations"=>[], - "isRepeatable"=>true, - "onField"=>false, - "onFragment"=>false, - "onOperation"=>false, - }, - { - "name" => "include", - "args" => [ - {"name"=>"if", "type"=>{"kind"=>"NON_NULL", "name"=>nil, "ofType"=>{"name"=>"Boolean"}}} - ], - "locations"=>["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], - "isRepeatable" => false, - "onField" => true, - "onFragment" => true, - "onOperation" => false, - }, - { - "name" => "oneOf", - "args" => [], - "locations"=>["INPUT_OBJECT"], - "isRepeatable" => false, - "onField" => false, - "onFragment" => false, - "onOperation" => false, - }, - { - "name" => "skip", - "args" => [ - {"name"=>"if", "type"=>{"kind"=>"NON_NULL", "name"=>nil, "ofType"=>{"name"=>"Boolean"}}} - ], - "locations"=>["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], - "isRepeatable" => false, - "onField" => true, - "onFragment" => true, - "onOperation" => false, - }, - { - "name" => "specifiedBy", - "args" => [ - {"name"=>"url", "type"=>{"kind"=>"NON_NULL", "name"=>nil, "ofType"=>{"name"=>"String"}}} - ], - "locations"=>["SCALAR"], - "isRepeatable" => false, - "onField" => false, - "onFragment" => false, - "onOperation" => false, - }, - ] - } - }} - assert_equal(expected, result.to_h) - end - - it "hides deprecated arguments by default" do - schema.directive(directive_with_deprecated_arg) - result = schema.execute <<-GRAPHQL - { - __schema { - directives { - name - args { - name - } - } - } - } - GRAPHQL - - directive_result = result["data"]["__schema"]["directives"].find { |d| d["name"] == "customTransform" } - expected = [ - {"name" => "newWay"} - ] - assert_equal(expected, directive_result["args"]) - end - - it "can expose deprecated arguments" do - schema.directive(directive_with_deprecated_arg) - result = schema.execute <<-GRAPHQL - { - __schema { - directives { - name - args(includeDeprecated: true) { - name - isDeprecated - deprecationReason - } - } - } - } - GRAPHQL - - directive_result = result["data"]["__schema"]["directives"].find { |d| d["name"] == "customTransform" } - expected = [ - {"name" => "oldWay", "isDeprecated" => true, "deprecationReason" => "Use the newWay"}, - {"name" => "newWay", "isDeprecated" => false, "deprecationReason" => nil} - ] - assert_equal(expected, directive_result["args"]) - end -end diff --git a/vendor/gems/graphql/spec/graphql/introspection/entry_points_spec.rb b/vendor/gems/graphql/spec/graphql/introspection/entry_points_spec.rb deleted file mode 100644 index 2ef503d2a62..00000000000 --- a/vendor/gems/graphql/spec/graphql/introspection/entry_points_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Introspection::EntryPoints do - describe "#__type" do - let(:schema) do - nested_invisible_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'NestedInvisible' - field :foo, String, null: false - end - - invisible_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Invisible' - field :foo, String, null: false - field :nested_invisible, nested_invisible_type, null: false - - def self.visible?(context) - false - end - end - - visible_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Visible' - field :foo, String, null: false - end - - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Query' - field :foo, String, null: false - field :invisible, invisible_type, null: false - field :visible, visible_type, null: false - end - - Class.new(GraphQL::Schema) do - query query_type - use GraphQL::Schema::Warden if ADD_WARDEN - end - end - - let(:query_string) {%| - query getType($name: String!) { - __type(name: $name) { - name - } - } - |} - - it "returns reachable types" do - result = schema.execute(query_string, variables: { name: 'Visible' }) - type_name = result['data']['__type']['name'] - assert_equal('Visible', type_name) - end - - it "returns nil for unreachable types" do - result = schema.execute(query_string, variables: { name: 'NestedInvisible' }) - type_name = result['data']['__type'] - assert_nil(type_name) - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/introspection/input_value_type_spec.rb b/vendor/gems/graphql/spec/graphql/introspection/input_value_type_spec.rb deleted file mode 100644 index 78a997ffc07..00000000000 --- a/vendor/gems/graphql/spec/graphql/introspection/input_value_type_spec.rb +++ /dev/null @@ -1,144 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - - -describe GraphQL::Introspection::InputValueType do - let(:query_string) {%| - { - __type(name: "DairyProductInput") { - name - description - kind - inputFields { - name - type { kind, name } - defaultValue - description - } - } - } - |} - let(:result) { Dummy::Schema.execute(query_string) } - - it "exposes metadata about input objects, giving extra quotes for strings" do - expected = { "data" => { - "__type" => { - "name"=>"DairyProductInput", - "description"=>"Properties for finding a dairy product", - "kind"=>"INPUT_OBJECT", - "inputFields"=>[ - {"name"=>"source", "type"=>{"kind"=>"NON_NULL", "name" => nil}, "defaultValue"=>nil, - "description" => "Where it came from"}, - {"name"=>"originDairy", "type"=>{"kind"=>"SCALAR", "name" => "String"}, "defaultValue"=>"\"Sugar Hollow Dairy\"", - "description" => "Dairy which produced it"}, - {"name"=>"fatContent", "type"=>{"kind"=>"SCALAR", "name" => "Float"}, "defaultValue"=>"0.3", - "description" => "How much fat it has"}, - {"name"=>"organic", "type"=>{"kind"=>"SCALAR", "name" => "Boolean"}, "defaultValue"=>"false", - "description" => nil}, - {"name"=>"order_by", "type"=>{"kind"=>"INPUT_OBJECT", "name"=>"ResourceOrderType"}, "defaultValue"=>"{direction: \"ASC\"}", - "description" => nil}, - ] - } - }} - assert_equal(expected, result.to_h) - end - - let(:cheese_type) { - Dummy::Schema.execute(%| - { - __type(name: "Cheese") { - fields { - name - args { - name - defaultValue - } - } - } - } - |) - } - - it "converts default values to GraphQL values" do - field = cheese_type['data']['__type']['fields'].detect { |f| f['name'] == 'similarCheese' } - arg = field['args'].detect { |a| a['name'] == 'nullableSource' } - - assert_equal('[COW]', arg['defaultValue']) - end - - it "supports list of enum default values" do - schema = GraphQL::Schema.from_definition(%| - type Query { - hello(enums: [MyEnum] = [A, B]): String - } - - enum MyEnum { - A - B - } - |) - - result = schema.execute(%| - { - __type(name: "Query") { - fields { - args { - defaultValue - } - } - } - } - |) - - expected = { - "data" => { - "__type" => { - "fields" => [{ - "args" => [{ - "defaultValue" => "[A, B]" - }] - }] - } - } - } - - assert_equal expected, result - end - - it "supports null default values" do - schema = GraphQL::Schema.from_definition(%| - type Query { - hello(person: Person): String - } - - input Person { - firstName: String! - lastName: String = null - } - |) - - result = schema.execute(%| - { - __type(name: "Person") { - inputFields { - name - defaultValue - } - } - } - |) - - expected = { - "data" => { - "__type" => { - "inputFields" => [ - { "name" => "firstName", "defaultValue" => nil}, - { "name" => "lastName", "defaultValue" => "null"} - ] - } - } - } - - assert_equal expected, result - end -end diff --git a/vendor/gems/graphql/spec/graphql/introspection/introspection_query_spec.rb b/vendor/gems/graphql/spec/graphql/introspection/introspection_query_spec.rb deleted file mode 100644 index d2523ca139c..00000000000 --- a/vendor/gems/graphql/spec/graphql/introspection/introspection_query_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe "GraphQL::Introspection::INTROSPECTION_QUERY" do - let(:schema) { - Class.new(Dummy::Schema) do - max_depth(15) - end - } - let(:query_string) { GraphQL::Introspection::INTROSPECTION_QUERY } - let(:result) { schema.execute(query_string) } - - it "runs" do - assert(result["data"]) - end - - it "is limited to the max query depth" do - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name "DeepQuery" - - field :foo, [[[Float]]], null: false - end - - deep_schema = Class.new(GraphQL::Schema) do - query query_type - end - - result = deep_schema.execute(query_string) - assert(GraphQL::Schema::Loader.load(result)) - end - - it "doesn't handle too deeply nested (< 8) schemas" do - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name "DeepQuery" - - field :foo, [[[[Float]]]], null: false - end - - deep_schema = Class.new(GraphQL::Schema) do - query query_type - end - - result = deep_schema.execute(query_string) - assert_raises(KeyError) { - GraphQL::Schema::Loader.load(result) - } - end - - it "doesn't contain blank lines" do - int_query = GraphQL::Introspection.query - refute_includes int_query, "\n\n" - - int_query_with_options = GraphQL::Introspection.query( - include_deprecated_args: true, - include_schema_description: true, - include_is_repeatable: true, - include_specified_by_url: true, - include_is_one_of: true - ) - refute_includes int_query_with_options, "\n\n" - end -end diff --git a/vendor/gems/graphql/spec/graphql/introspection/schema_type_spec.rb b/vendor/gems/graphql/spec/graphql/introspection/schema_type_spec.rb deleted file mode 100644 index bf6bdc2d52e..00000000000 --- a/vendor/gems/graphql/spec/graphql/introspection/schema_type_spec.rb +++ /dev/null @@ -1,203 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Introspection::SchemaType do - let(:schema) { Class.new(Dummy::Schema) { description("Cool schema") }} - let(:query_string) {%| - query getSchema { - __schema { - description - types { name } - queryType { fields { name }} - mutationType { fields { name }} - } - } - |} - let(:result) { schema.execute(query_string) } - - it "exposes the schema" do - expected = { "data" => { - "__schema" => { - "description" => "Cool schema", - "types" => schema.types.values.sort_by(&:graphql_name).map { |t| t.graphql_name.nil? ? (p t; raise("no name for #{t}")) : {"name" => t.graphql_name} }, - "queryType"=>{ - "fields"=>[ - {"name"=>"allAnimal"}, - {"name"=>"allAnimalAsCow"}, - {"name"=>"allDairy"}, - {"name"=>"allEdible"}, - {"name"=>"allEdibleAsMilk"}, - {"name"=>"cheese"}, - {"name"=>"cow"}, - {"name"=>"dairy"}, - {"name"=>"deepNonNull"}, - {"name"=>"error"}, - {"name"=>"exampleBeverage"}, - {"name"=>"executionError"}, - {"name"=>"executionErrorWithExtensions"}, - {"name"=>"executionErrorWithOptions"}, - {"name"=>"favoriteEdible"}, - {"name"=>"fromSource"}, - {"name"=>"hugeInteger"}, - {"name"=>"maybeNull"}, - {"name"=>"milk"}, - {"name"=>"multipleErrorsOnNonNullableField"}, - {"name"=>"multipleErrorsOnNonNullableListField"}, - {"name"=>"root"}, - {"name"=>"searchDairy"}, - {"name"=>"tracingScalar"}, - {"name"=>"valueWithExecutionError"}, - ] - }, - "mutationType"=> { - "fields"=>[ - {"name"=>"pushValue"}, - {"name"=>"replaceValues"}, - ] - }, - } - }} - assert_equal(expected, result.to_h) - end - - describe "when the schema has types that are only reachable through hidden types" do - let(:schema) do - nested_invisible_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'NestedInvisible' - field :foo, String, null: false - end - - invisible_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Invisible' - field :foo, String, null: false - field :nested_invisible, nested_invisible_type, null: false - - def self.visible?(context) - false - end - end - - invisible_input_type = Class.new(GraphQL::Schema::InputObject) do - graphql_name 'InvisibleInput' - argument :foo, String, required: false - - def self.visible?(context) - false - end - end - - visible_input_type = Class.new(GraphQL::Schema::InputObject) do - graphql_name 'VisibleInput' - argument :foo, String, required: false - end - - visible_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Visible' - field :foo, String, null: false - end - - invisible_orphan_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'InvisibleOrphan' - field :foo, String, null: false - - def self.visible?(context) - false - end - end - - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Query' - field :foo, String, null: false - field :invisible, invisible_type, null: false - field :visible, visible_type, null: false - field :with_invisible_args, String, null: false do - argument :invisible, invisible_input_type, required: false - argument :visible, visible_input_type - end - end - - Class.new(GraphQL::Schema) do - query query_type - orphan_types invisible_orphan_type - use GraphQL::Schema::Warden if ADD_WARDEN - end - end - - let(:query_string) {%| - query getSchema { - __schema { - types { name } - } - } - |} - - it "only returns reachable types" do - expected_types = [ - 'Boolean', - 'Query', - 'String', - 'Visible', - 'VisibleInput', - '__Directive', - '__DirectiveLocation', - '__EnumValue', - '__Field', - '__InputValue', - '__Schema', - '__Type', - '__TypeKind' - ] - types = result['data']['__schema']['types'].map { |type| type.fetch('name') } - assert_equal(expected_types, types) - end - end - - describe "when the schema has hidden directives" do - let(:schema) do - invisible_directive = Class.new(GraphQL::Schema::Directive) do - graphql_name 'invisibleDirective' - locations(GraphQL::Schema::Directive::QUERY) - argument(:val, Integer, "Initial integer value.", required: false) - - def self.visible?(context) - false - end - end - - visible_directive = Class.new(GraphQL::Schema::Directive) do - graphql_name 'visibleDirective' - locations(GraphQL::Schema::Directive::QUERY) - argument(:val, Integer, "Initial integer value.", required: false) - - def self.visible?(context) - true - end - end - - query_type = Class.new(GraphQL::Schema::Object) do - graphql_name 'Query' - field :foo, String, null: false - end - - Class.new(GraphQL::Schema) do - use GraphQL::Schema::Visibility - query query_type - directives invisible_directive, visible_directive - end - end - - let(:query_string) {%| - query getSchema { - __schema { - directives { name } - } - } - |} - - it "only returns visible directives" do - expected_dirs = ['deprecated', 'include', 'skip', 'oneOf', 'specifiedBy', 'visibleDirective'] - directives = result['data']['__schema']['directives'].map { |dir| dir.fetch('name') } - assert_equal(expected_dirs.sort, directives.sort) - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/introspection/type_type_spec.rb b/vendor/gems/graphql/spec/graphql/introspection/type_type_spec.rb deleted file mode 100644 index ca1c9431fed..00000000000 --- a/vendor/gems/graphql/spec/graphql/introspection/type_type_spec.rb +++ /dev/null @@ -1,235 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Introspection::TypeType do - let(:query_string) {%| - query introspectionQuery { - cheeseType: __type(name: "Cheese") { name, kind, fields { name, isDeprecated, type { kind, name, ofType { name } } } } - milkType: __type(name: "Milk") { interfaces { name }, fields { type { kind, name, ofType { name } } } } - dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: false) { name, isDeprecated } } - dairyProduct: __type(name: "DairyProduct") { name, kind, possibleTypes { name } } - animalProduct: __type(name: "AnimalProduct") { name, kind, specifiedByURL, possibleTypes { name }, fields { name } } - missingType: __type(name: "NotAType") { name } - timeType: __type(name: "Time") { specifiedByURL } - } - |} - let(:result) { Dummy::Schema.execute(query_string, context: {}, variables: {"cheeseId" => 2}) } - let(:cheese_fields) {[ - {"name"=>"dairyProduct", "isDeprecated" => false, "type"=>{"kind"=>"UNION", "name"=>"DairyProduct", "ofType"=>nil}}, - {"name"=>"deeplyNullableCheese", "isDeprecated" => false, "type"=>{ "kind" => "OBJECT", "name" => "Cheese", "ofType" => nil}}, - {"name"=>"flavor", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "String"}}}, - {"name"=>"id", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "Int"}}}, - {"name"=>"nullableCheese", "isDeprecated"=>false, "type"=>{ "kind" => "OBJECT", "name" => "Cheese", "ofType"=>nil}}, - {"name"=>"origin", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "String"}}}, - {"name"=>"selfAsEdible", "isDeprecated"=>false, "type"=>{"kind"=>"INTERFACE", "name"=>"Edible", "ofType"=>nil}}, - {"name"=>"similarCheese", "isDeprecated"=>false, "type"=>{ "kind" => "OBJECT", "name"=>"Cheese", "ofType"=>nil}}, - {"name"=>"source", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "DairyAnimal"}}}, - ]} - - let(:dairy_animals) {[ - {"name"=>"NONE", "isDeprecated"=> false }, - {"name"=>"COW", "isDeprecated"=> false }, - {"name"=>"DONKEY", "isDeprecated"=> false }, - {"name"=>"GOAT", "isDeprecated"=> false }, - {"name"=>"REINDEER", "isDeprecated"=> false }, - {"name"=>"SHEEP", "isDeprecated"=> false }, - ]} - it "exposes metadata about types" do - expected = {"data"=> { - "cheeseType" => { - "name"=> "Cheese", - "kind" => "OBJECT", - "fields"=> cheese_fields - }, - "milkType"=>{ - "interfaces"=>[ - {"name"=>"AnimalProduct"}, - {"name"=>"Edible"}, - {"name"=>"EdibleAsMilk"}, - {"name"=>"LocalProduct"}, - ], - "fields"=>[ - {"type"=>{"kind"=>"LIST","name"=>nil, "ofType"=>{"name"=>"DairyProduct"}}}, - {"type"=>{"kind"=>"SCALAR","name"=>"String", "ofType"=>nil}}, - {"type"=>{"kind"=>"NON_NULL","name"=>nil, "ofType"=>{"name"=>"Float"}}}, - {"type"=>{"kind"=>"LIST","name"=>nil, "ofType"=>{"name"=>"String"}}}, - {"type"=>{"kind"=>"NON_NULL","name"=>nil, "ofType"=>{"name"=>"ID"}}}, - {"type"=>{"kind"=>"NON_NULL","name"=>nil, "ofType"=>{"name"=>"String"}}}, - {"type"=>{"kind"=>"INTERFACE", "name"=>"Edible", "ofType"=>nil}}, - {"type"=>{"kind"=>"NON_NULL","name"=>nil,"ofType"=>{"name"=>"DairyAnimal"}}}, - ] - }, - "dairyAnimal"=>{ - "name"=>"DairyAnimal", - "kind"=>"ENUM", - "enumValues"=> dairy_animals, - }, - "dairyProduct"=>{ - "name"=>"DairyProduct", - "kind"=>"UNION", - "possibleTypes"=>[{"name"=>"Cheese"}, {"name"=>"Milk"}], - }, - "animalProduct" => { - "name"=>"AnimalProduct", - "kind"=>"INTERFACE", - "specifiedByURL" => nil, - "possibleTypes"=>[{"name"=>"Cheese"}, {"name"=>"Honey"}, {"name"=>"Milk"}], - "fields"=>[ - {"name"=>"source"}, - ] - }, - "missingType" => nil, - "timeType" => { "specifiedByURL" => "https://time.graphql"} - }} - assert_equal(expected, result.to_h) - end - - describe "deprecated fields" do - let(:query_string) {%| - query introspectionQuery { - cheeseType: __type(name: "Cheese") { name, kind, fields(includeDeprecated: true) { name, isDeprecated, type { kind, name, ofType { name } } } } - dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: true) { name, isDeprecated } } - } - |} - let(:deprecated_fields) { {"name"=>"fatContent", "isDeprecated"=>true, "type"=>{"kind"=>"NON_NULL","name"=>nil, "ofType"=>{"name"=>"Float"}}} } - - it "can expose deprecated fields" do - new_cheese_fields = ([deprecated_fields] + cheese_fields).sort_by { |f| f["name"] } - expected = { "data" => { - "cheeseType" => { - "name"=> "Cheese", - "kind" => "OBJECT", - "fields"=> new_cheese_fields - }, - "dairyAnimal"=>{ - "name"=>"DairyAnimal", - "kind"=>"ENUM", - "enumValues"=> dairy_animals + [{"name" => "YAK", "isDeprecated" => true}], - }, - }} - assert_equal(expected, result) - end - - it "hides deprecated field arguments by default" do - result = Dummy::Schema.execute <<-GRAPHQL - { - __type(name: "Query") { - fields { - name - args { - name - } - } - } - } - GRAPHQL - - from_source_field = result['data']['__type']['fields'].find { |f| f['name'] == 'fromSource' } - expected = [ - {"name" => "source"} - ] - assert_equal(expected, from_source_field['args']) - end - - it "can expose deprecated field arguments" do - result = Dummy::Schema.execute <<-GRAPHQL - { - __type(name: "Query") { - fields { - name - args(includeDeprecated: true) { - name - isDeprecated - deprecationReason - } - } - } - } - GRAPHQL - - from_source_field = result['data']['__type']['fields'].find { |f| f['name'] == 'fromSource' } - expected = [ - {"name" => "source", "isDeprecated" => false, "deprecationReason" => nil}, - {"name" => "oldSource", "isDeprecated" => true, "deprecationReason" => "No longer supported"} - ] - assert_equal(expected, from_source_field['args']) - end - end - - describe "input objects" do - let(:query_string) {%| - query introspectionQuery { - __type(name: "DairyProductInput") { name, description, kind, inputFields { name, type { kind, name }, defaultValue } } - } - |} - - it "exposes metadata about input objects" do - expected = { "data" => { - "__type" => { - "name"=>"DairyProductInput", - "description"=>"Properties for finding a dairy product", - "kind"=>"INPUT_OBJECT", - "inputFields"=>[ - {"name"=>"source", "type"=>{"kind"=>"NON_NULL","name"=>nil, }, "defaultValue"=>nil}, - {"name"=>"originDairy", "type"=>{"kind"=>"SCALAR","name"=>"String"}, "defaultValue"=>"\"Sugar Hollow Dairy\""}, - {"name"=>"fatContent", "type"=>{"kind"=>"SCALAR","name" => "Float"}, "defaultValue"=>"0.3"}, - {"name"=>"organic", "type"=>{"kind"=>"SCALAR","name" => "Boolean"}, "defaultValue"=>"false"}, - {"name"=>"order_by", "type"=>{"kind"=>"INPUT_OBJECT", "name"=>"ResourceOrderType"}, "defaultValue"=>"{direction: \"ASC\"}"}, - ] - } - }} - assert_equal(expected, result) - end - - it "can expose deprecated input fields" do - result = Dummy::Schema.execute <<-GRAPHQL - { - __type(name: "DairyProductInput") { - inputFields(includeDeprecated: true) { - name - isDeprecated - deprecationReason - } - } - } - GRAPHQL - - expected = { - "data" => { - "__type" => { - "inputFields" => [ - {"name" => "source", "isDeprecated" => false, "deprecationReason" => nil}, - {"name" => "originDairy", "isDeprecated" => false, "deprecationReason" => nil}, - {"name" => "fatContent", "isDeprecated" => false, "deprecationReason" => nil}, - {"name" => "organic", "isDeprecated" => false, "deprecationReason" => nil}, - {"name" => "order_by", "isDeprecated" => false, "deprecationReason" => nil}, - {"name" => "oldSource", "isDeprecated" => true, "deprecationReason" => "No longer supported"}, - ] - } - } - } - assert_equal(expected, result) - end - - it "includes Relay fields" do - res = StarWars::Schema.execute <<-GRAPHQL - { - __schema { - types { - name - fields { - name - args { name } - } - } - } - } - GRAPHQL - type_result = res["data"]["__schema"]["types"].find { |t| t["name"] == "Faction" } - field_result = type_result["fields"].find { |f| f["name"] == "bases" } - all_arg_names = ["after", "before", "first", "last", "nameIncludes", "complexOrder"] - returned_arg_names = field_result["args"].map { |a| a["name"] } - assert_equal all_arg_names.sort, returned_arg_names.sort - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/invalid_null_error_spec.rb b/vendor/gems/graphql/spec/graphql/invalid_null_error_spec.rb deleted file mode 100644 index 6c1b9c7885e..00000000000 --- a/vendor/gems/graphql/spec/graphql/invalid_null_error_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe "GraphQL::InvalidNullError" do - it "can be inspected" do - assert_equal "GraphQL::InvalidNullError", GraphQL::InvalidNullError.inspect - end -end diff --git a/vendor/gems/graphql/spec/graphql/language/block_string_spec.rb b/vendor/gems/graphql/spec/graphql/language/block_string_spec.rb deleted file mode 100644 index 05ded99d711..00000000000 --- a/vendor/gems/graphql/spec/graphql/language/block_string_spec.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Language::BlockString do - describe "trimming whitespace" do - def trim_whitespace(str) - GraphQL::Language::BlockString.trim_whitespace(str) - end - - it "matches the examples in graphql-js" do - # these are taken from: - # https://github.com/graphql/graphql-js/blob/36ec0e9d34666362ff0e2b2b18edeb98e3c9abee/src/language/__tests__/blockStringValue-test.js#L12 - # A set of [before, after] pairs: - examples = [ - [ - # Removes common whitespace: - " - Hello, - World! - - Yours, - GraphQL. - ", - "Hello,\n World!\n\nYours,\n GraphQL." - ], - [ - # Removes leading and trailing newlines: - " - - Hello, - World! - - Yours, - GraphQL. - - ", - "Hello,\n World!\n\nYours,\n GraphQL." - ], - [ - # Removes blank lines (with whitespace _and_ newlines:) - "\n \n - Hello, - World! - - Yours, - GraphQL. - - \n \n", - "Hello,\n World!\n\nYours,\n GraphQL." - ], - [ - # Retains indentation from the first line - " Hello,\n World!\n\n Yours,\n GraphQL.", - " Hello,\n World!\n\nYours,\n GraphQL.", - ], - [ - # Doesn't alter trailing spaces - "\n \n Hello, \n World! \n\n Yours, \n GraphQL. ", - "Hello, \n World! \n\nYours, \n GraphQL. ", - - ], - [ - # Doesn't crash when the string is only a newline - "\n", - "" - ], - [ - # Removes long blank lines - " \n \n - Hello, - World! - - Yours, - GraphQL. - - \n \n", - "Hello,\n World!\n\nYours,\n GraphQL." - ] - ] - - examples.each_with_index do |(before, after), idx| - transformed_str = trim_whitespace(before) - assert_equal(after, transformed_str, "Example ##{idx + 1}") - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/language/clexer_spec.rb b/vendor/gems/graphql/spec/graphql/language/clexer_spec.rb deleted file mode 100644 index e97a428a901..00000000000 --- a/vendor/gems/graphql/spec/graphql/language/clexer_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" -require_relative "./lexer_examples" - -if defined?(GraphQL::CParser::Lexer) - describe GraphQL::CParser::Lexer do - subject { GraphQL::CParser::Lexer } - - def assert_bad_unicode(string, _message = nil) - assert_equal :BAD_UNICODE_ESCAPE, subject.tokenize(string).first[0] - end - - it "makes tokens like the other lexer" do - str = "{ f1(type: \"str\") ...F2 }\nfragment F2 on SomeType { f2 }" - tokens = GraphQL.scan_with_c(str).map { |t| [*t.first(4), t[3].encoding] } - old_tokens = GraphQL.scan_with_ruby(str).map { |t| [*t, t[3].encoding] } - - assert_equal [ - [:LCURLY, 1, 1, "{", Encoding::UTF_8], - [:IDENTIFIER, 1, 3, "f1", Encoding::UTF_8], - [:LPAREN, 1, 5, "(", Encoding::UTF_8], - [:TYPE, 1, 6, "type", Encoding::UTF_8], - [:COLON, 1, 10, ":", Encoding::UTF_8], - [:STRING, 1, 12, "str", Encoding::UTF_8], - [:RPAREN, 1, 17, ")", Encoding::UTF_8], - [:ELLIPSIS, 1, 19, "...", Encoding::UTF_8], - [:IDENTIFIER, 1, 22, "F2", Encoding::UTF_8], - [:RCURLY, 1, 25, "}", Encoding::UTF_8], - [:FRAGMENT, 2, 1, "fragment", Encoding::UTF_8], - [:IDENTIFIER, 2, 10, "F2", Encoding::UTF_8], - [:ON, 2, 13, "on", Encoding::UTF_8], - [:IDENTIFIER, 2, 16, "SomeType", Encoding::UTF_8], - [:LCURLY, 2, 25, "{", Encoding::UTF_8], - [:IDENTIFIER, 2, 27, "f2", Encoding::UTF_8], - [:RCURLY, 2, 30, "}", Encoding::UTF_8] - ], tokens - assert_equal(old_tokens, tokens) - end - - it "makes frozen strings when using SchemaParser" do - str = "type Query { f1: Int }" - schema_ast = GraphQL::CParser::SchemaParser.new(str, nil, GraphQL::Tracing::NullTrace, nil).result - default_ast = GraphQL::CParser::Parser.new(str, nil, GraphQL::Tracing::NullTrace, nil).result - - # Equivalent ASTs: - assert_equal schema_ast, default_ast - - # But this one is frozen: - assert_equal "Query", schema_ast.definitions.first.name - assert schema_ast.definitions.first.name.frozen? - - # And this one isn't: - assert_equal "Query", default_ast.definitions.first.name - refute default_ast.definitions.first.name.frozen? - end - - it "exposes tokens_count" do - str = "type Query { f1: Int }" - parser = GraphQL::CParser::Parser.new(str, nil, GraphQL::Tracing::NullTrace, nil) - - assert_equal 7, parser.tokens_count - end - - include LexerExamples - end -end diff --git a/vendor/gems/graphql/spec/graphql/language/definition_slice_spec.rb b/vendor/gems/graphql/spec/graphql/language/definition_slice_spec.rb deleted file mode 100644 index 0ac9edf845f..00000000000 --- a/vendor/gems/graphql/spec/graphql/language/definition_slice_spec.rb +++ /dev/null @@ -1,226 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Language::DefinitionSlice do - let(:document) { GraphQL.parse(query_string) } - - describe "anonymous query with no dependencies" do - let(:query_string) {%| - { - version - } - |} - - it "is already the smallest slice" do - assert_equal document.to_query_string, - document.slice_definition(nil).to_query_string - end - end - - describe "anonymous mutation with no dependencies" do - let(:query_string) {%| - mutation { - ping { - message - } - } - |} - - it "is already the smallest slice" do - assert_equal document.to_query_string, - document.slice_definition(nil).to_query_string - end - end - - describe "anonymous fragment with no dependencies" do - let(:query_string) {%| - fragment on User { - name - } - |} - - it "is already the smallest slice" do - assert_equal document.to_query_string, - document.slice_definition(nil).to_query_string - end - end - - describe "named query with no dependencies" do - let(:query_string) {%| - query getVersion { - version - } - |} - - it "is already the smallest slice" do - assert_equal document.to_query_string, - document.slice_definition("getVersion").to_query_string - end - end - - describe "named fragment with no dependencies" do - let(:query_string) {%| - fragment profileFields on User { - firstName - lastName - } - |} - - it "is already the smallest slice" do - assert_equal document.to_query_string, - document.slice_definition("profileFields").to_query_string - end - end - - describe "document with multiple queries but no subdependencies" do - let(:query_string) {%| - query getVersion { - version - } - - query getTime { - time - } - |} - - it "returns just the query definition" do - assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string, - document.slice_definition("getVersion").to_query_string - assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[1]]).to_query_string, - document.slice_definition("getTime").to_query_string - end - end - - describe "document with multiple fragments but no subdependencies" do - let(:query_string) {%| - fragment profileFields on User { - firstName - lastName - } - - fragment avatarFields on User { - avatarURL(size: 80) - } - |} - - it "returns just the fragment definition" do - assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string, - document.slice_definition("profileFields").to_query_string - assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[1]]).to_query_string, - document.slice_definition("avatarFields").to_query_string - end - end - - describe "query with missing spread" do - let(:query_string) {%| - query getUser { - viewer { - ...profileFields - } - } - |} - - it "is ignored" do - assert_equal document.to_query_string, - document.slice_definition("getUser").to_query_string - end - end - - describe "query and fragment subdependency" do - let(:query_string) {%| - query getUser { - viewer { - ...profileFields - } - } - - fragment profileFields on User { - firstName - lastName - } - |} - - it "returns query and fragment dependency" do - assert_equal document.to_query_string, - document.slice_definition("getUser").to_query_string - end - end - - describe "query and fragment nested subdependencies" do - let(:query_string) {%| - query getUser { - viewer { - ...viewerInfo - } - } - - fragment viewerInfo on User { - ...profileFields - } - - fragment profileFields on User { - firstName - lastName - ...avatarFields - } - - fragment avatarFields on User { - avatarURL(size: 80) - } - |} - - it "returns query and all fragment dependencies" do - assert_equal document.to_query_string, - document.slice_definition("getUser").to_query_string - end - end - - describe "fragment subdependency referenced multiple times" do - let(:query_string) {%| - query getUser { - viewer { - ...viewerInfo - ...moreViewerInfo - } - } - - fragment viewerInfo on User { - ...profileFields - } - - fragment moreViewerInfo on User { - ...profileFields - } - - fragment profileFields on User { - firstName - lastName - } - |} - - it "is only returned once" do - assert_equal document.to_query_string, - document.slice_definition("getUser").to_query_string - end - end - - describe "query and unused fragment" do - let(:query_string) {%| - query getUser { - viewer { - id - } - } - - fragment profileFields on User { - firstName - lastName - } - |} - - it "returns just the query definition" do - assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string, - document.slice_definition("getUser").to_query_string - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/language/document_from_schema_definition_spec.rb b/vendor/gems/graphql/spec/graphql/language/document_from_schema_definition_spec.rb deleted file mode 100644 index e6e8d566170..00000000000 --- a/vendor/gems/graphql/spec/graphql/language/document_from_schema_definition_spec.rb +++ /dev/null @@ -1,921 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Language::DocumentFromSchemaDefinition do - let(:subject) { GraphQL::Language::DocumentFromSchemaDefinition } - - describe "#document" do - let(:schema_idl) { <<-GRAPHQL - type QueryType { - foo: Foo - u: Union - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Site - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type MutationType { - a(input: InputType): String - } - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | QueryType - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:schema) { GraphQL::Schema.from_definition(schema_idl) } - - let(:expected_document) { GraphQL.parse(expected_idl) } - - describe "when schemas have enums and directives" do - let(:schema_idl) { <<-GRAPHQL -directive @locale(lang: LangEnum!) on FIELD - -directive @secret(top: Boolean = false) on FIELD_DEFINITION - -enum LangEnum { - en - ru -} - -type Query { - i: Int @secret - ssn: String @secret(top: true) -} - GRAPHQL - } - - class DirectiveSchema < GraphQL::Schema - class Secret < GraphQL::Schema::Directive - argument :top, Boolean, required: false, default_value: false - locations FIELD_DEFINITION - end - - class Query < GraphQL::Schema::Object - field :i, Int do - directive Secret - end - - field :ssn, String do - directive Secret, top: true - end - end - - class Locale < GraphQL::Schema::Directive - class LangEnum < GraphQL::Schema::Enum - value "en" - value "ru" - end - locations GraphQL::Schema::Directive::FIELD - - argument :lang, LangEnum - end - - query(Query) - directive(Locale) - end - - it "dumps them into the string" do - assert_equal schema_idl, DirectiveSchema.to_definition - end - end - - describe "when it has an enum_value with an adjacent custom directive" do - let(:schema_idl) { <<-GRAPHQL -directive @customEnumValueDirective(fakeArgument: String!) on ENUM_VALUE - -enum FakeEnum { - VALUE1 - VALUE2 @customEnumValueDirective(fakeArgument: "Value1 is better...") -} - -type Query { - fakeQueryField: FakeEnum! -} - GRAPHQL - } - - class EnumValueDirectiveSchema < GraphQL::Schema - class CustomEnumValueDirective < GraphQL::Schema::Directive - locations GraphQL::Schema::Directive::ENUM_VALUE - - argument :fake_argument, String - end - - class FakeEnum < GraphQL::Schema::Enum - value "VALUE1" - value "VALUE2" do - directive CustomEnumValueDirective, fake_argument: "Value1 is better..." - end - end - - class Query < GraphQL::Schema::Object - field :fake_query_field, FakeEnum, null: false - end - - query(Query) - end - - it "dumps the custom directive definition to the IDL" do - assert_equal schema_idl, EnumValueDirectiveSchema.to_definition - end - end - - describe "when printing and schema respects root name conventions" do - let(:schema_idl) { <<-GRAPHQL - type Query { - foo: Foo - u: Union - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Site - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type Mutation { - a(input: InputType): String - } - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | Query - - schema { - query: Query - mutation: Mutation - } - GRAPHQL - } - - let(:expected_idl) { <<-GRAPHQL - type QueryType { - foo: Foo - u: Union - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Site - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type MutationType { - a(input: InputType): String - } - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | QueryType - GRAPHQL - } - - let(:document) { - subject.new( - schema - ).document - } - - it "returns the IDL without introspection, built ins and schema root" do - assert equivalent_node?(expected_document, document) - end - end - - describe "with defaults" do - let(:expected_idl) { <<-GRAPHQL - type QueryType { - foo: Foo - u: Union - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Site - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type MutationType { - a(input: InputType): String - } - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | QueryType - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:document) { - subject.new( - schema - ).document - } - - it "returns the IDL without introspection, built ins and schema if it doesnt respect name conventions" do - assert equivalent_node?(expected_document, document) - end - end - - describe "with a visibility check" do - let(:expected_idl) { <<-GRAPHQL - type QueryType { - foo: Foo - u: Union - } - - type Foo implements Bar { - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): Site - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type MutationType { - a(input: InputType): String - } - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:schema) { - Class.new(GraphQL::Schema.from_definition(schema_idl)) do - def self.visible?(m, ctx) - m.graphql_name != "Type" - end - end - } - - let(:document) { - doc_schema = Class.new(schema) do - use GraphQL::Schema::Visibility - def self.visible?(m, _ctx) - m.respond_to?(:graphql_name) && m.graphql_name != "Type" - end - end - - subject.new(doc_schema).document - } - - it "returns the IDL minus the filtered members" do - assert equivalent_node?(expected_document, document) - end - end - - describe "with an only filter" do - let(:expected_idl) { <<-GRAPHQL - type QueryType { - foo: Foo - u: Union - } - - type Foo implements Bar { - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): Site - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type MutationType { - a(input: InputType): String - } - - enum Site { - DESKTOP - MOBILE - } - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:schema) { - Class.new(GraphQL::Schema.from_definition(schema_idl)) do - def self.visible?(m, ctx) - !(m.respond_to?(:kind) && m.kind.scalar? && m.name == "CustomScalar") - end - end - } - - let(:document) { - doc_schema = Class.new(schema) do - def self.visible?(m, _ctx) - !(m.respond_to?(:kind) && m.kind.scalar? && m.name == "CustomScalar") - end - end - - subject.new(doc_schema).document - } - - it "returns the IDL minus the filtered members" do - assert equivalent_node?(expected_document, document) - end - end - - describe "when excluding built ins and introspection types" do - let(:expected_idl) { <<-GRAPHQL - type QueryType { - foo: Foo - u: Union - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Site - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - type MutationType { - a(input: InputType): String - } - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | QueryType - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:document) { - subject.new( - schema, - always_include_schema: true - ).document - } - - it "returns the schema idl besides introspection types and built ins" do - assert equivalent_node?(expected_document, document) - end - end - - describe "when printing excluding only introspection types" do - let(:expected_idl) { <<-GRAPHQL - # Represents `true` or `false` values. - scalar Boolean - - # Represents textual data as UTF-8 character sequences. This type is most often - # used by GraphQL to represent free-form human-readable text. - scalar String - - type QueryType { - foo: Foo - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Type - three(argument: InputType, other: String): CustomScalar - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - # Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. - scalar Int - - type MutationType { - a(input: InputType): String - } - - # Represents signed double-precision fractional values as specified by [IEEE - # 754](https://en.wikipedia.org/wiki/IEEE_floating_point). - scalar Float - - # Represents a unique identifier that is Base64 obfuscated. It is often used to - # refetch an object or as key for a cache. The ID type appears in a JSON response - # as a String; however, it is not intended to be human-readable. When expected as - # an input type, any string (such as `"VXNlci0xMA=="`) or integer (such as `4`) - # input value will be accepted as an ID. - scalar ID - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | QueryType - - directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - # Marks an element of a GraphQL schema as no longer supported. - directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:document) { - subject.new( - schema, - include_built_in_scalars: true, - include_built_in_directives: true, - ).document - } - - it "returns the schema IDL including only the built ins and not introspection types" do - assert equivalent_node?(expected_document, document) - end - end - - describe "when printing the full schema" do - let(:expected_idl) { <<-GRAPHQL - # Represents `true` or `false` values. - scalar Boolean - - # Represents textual data as UTF-8 character sequences. This type is most often - # used by GraphQL to represent free-form human-readable text. - scalar String - - # The fundamental unit of any GraphQL Schema is the type. There are many kinds of - # types in GraphQL as represented by the `__TypeKind` enum. - # - # Depending on the kind of a type, certain fields describe information about that - # type. Scalar types provide no information beyond a name and description, while - # Enum types provide their values. Object and Interface types provide the fields - # they describe. Abstract types, Union and Interface, provide the Object types - # possible at runtime. List and NonNull types compose other types. - type __Type { - kind: __TypeKind! - name: String - description: String - fields(includeDeprecated: Boolean = false): [__Field!] - interfaces: [__Type!] - possibleTypes: [__Type!] - enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - inputFields: [__InputValue!] - ofType: __Type - } - - # An enum describing what kind of type a given `__Type` is. - enum __TypeKind { - # Indicates this type is a scalar. - SCALAR - - # Indicates this type is an object. `fields` and `interfaces` are valid fields. - OBJECT - - # Indicates this type is an interface. `fields` and `possibleTypes` are valid fields. - INTERFACE - - # Indicates this type is a union. `possibleTypes` is a valid field. - UNION - - # Indicates this type is an enum. `enumValues` is a valid field. - ENUM - - # Indicates this type is an input object. `inputFields` is a valid field. - INPUT_OBJECT - - # Indicates this type is a list. `ofType` is a valid field. - LIST - - # Indicates this type is a non-null. `ofType` is a valid field. - NON_NULL - } - - # Object and Interface types are described by a list of Fields, each of which has - # a name, potentially a list of arguments, and a return type. - type __Field { - name: String! - description: String - args: [__InputValue!]! - type: __Type! - isDeprecated: Boolean! - deprecationReason: String - } - - # Arguments provided to Fields or Directives and the input fields of an - # InputObject are represented as Input Values which describe their type and - # optionally a default value. - type __InputValue { - name: String! - description: String - type: __Type! - - # A GraphQL-formatted string representing the default value for this input value. - defaultValue: String - } - - # One possible value for a given Enum. Enum values are unique values, not a - # placeholder for a string or numeric value. However an Enum value is returned in - # a JSON response as a string. - type __EnumValue { - name: String! - description: String - isDeprecated: Boolean! - deprecationReason: String - } - - type QueryType { - foo: Foo - } - - type Foo implements Bar { - one: Type - two(argument: InputType!): Type - three(argument: InputType, other: String): Int - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: String): Type - } - - interface Bar { - one: Type - four(argument: String = "string"): String - } - - type Type { - a: String - } - - input InputType { - key: String! - answer: Int = 42 - } - - # Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. - scalar Int - - type MutationType { - a(input: InputType): String - } - - # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all - # available types and directives on the server, as well as the entry points for - # query, mutation, and subscription operations. - type __Schema { - # A list of all types supported by this server. - types: [__Type!]! - - # The type that query operations will be rooted at. - queryType: __Type! - - # If this server supports mutation, the type that mutation operations will be rooted at. - mutationType: __Type - - # If this server support subscription, the type that subscription operations will be rooted at. - subscriptionType: __Type - - # A list of all directives supported by this server. - directives: [__Directive!]! - } - - # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. - # - # In some cases, you need to provide options to alter GraphQL's execution behavior - # in ways field arguments will not suffice, such as conditionally including or - # skipping a field. Directives provide this by describing additional information - # to the executor. - type __Directive { - name: String! - description: String - locations: [__DirectiveLocation!]! - args: [__InputValue!]! - onOperation: Boolean! - onFragment: Boolean! - onField: Boolean! - } - - # A Directive can be adjacent to many parts of the GraphQL language, a - # __DirectiveLocation describes one such possible adjacencies. - enum __DirectiveLocation { - # Location adjacent to a query operation. - QUERY - - # Location adjacent to a mutation operation. - MUTATION - - # Location adjacent to a subscription operation. - SUBSCRIPTION - - # Location adjacent to a field. - FIELD - - # Location adjacent to a fragment definition. - FRAGMENT_DEFINITION - - # Location adjacent to a fragment spread. - FRAGMENT_SPREAD - - # Location adjacent to an inline fragment. - INLINE_FRAGMENT - - # Location adjacent to a schema definition. - SCHEMA - - # Location adjacent to a scalar definition. - SCALAR - - # Location adjacent to an object type definition. - OBJECT - - # Location adjacent to a field definition. - FIELD_DEFINITION - - # Location adjacent to an argument definition. - ARGUMENT_DEFINITION - - # Location adjacent to an interface definition. - INTERFACE - - # Location adjacent to a union definition. - UNION - - # Location adjacent to an enum definition. - ENUM - - # Location adjacent to an enum value definition. - ENUM_VALUE - - # Location adjacent to an input object type definition. - INPUT_OBJECT - - # Location adjacent to an input object field definition. - INPUT_FIELD_DEFINITION - } - - # Represents signed double-precision fractional values as specified by [IEEE - # 754](https://en.wikipedia.org/wiki/IEEE_floating_point). - scalar Float - - # Represents a unique identifier that is Base64 obfuscated. It is often used to - # refetch an object or as key for a cache. The ID type appears in a JSON response - # as a String; however, it is not intended to be human-readable. When expected as - # an input type, any string (such as `"VXNlci0xMA=="`) or integer (such as `4`) - # input value will be accepted as an ID. - scalar ID - - # Scalar description - scalar CustomScalar - - enum Site { - DESKTOP - MOBILE - } - - union Union = Type | QueryType - - directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - # Marks an element of a GraphQL schema as no longer supported. - directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE - - schema { - query: QueryType - mutation: MutationType - } - GRAPHQL - } - - let(:document) { - subject.new( - schema, - include_introspection_types: true, - include_built_in_directives: true, - include_built_in_scalars: true, - always_include_schema: true, - ).document - } - - it "returns the full document AST from the given schema including built ins and introspection" do - assert equivalent_node?(expected_document, document) - end - end - end - - private - - def equivalent_node?(expected, node) - return false unless expected.is_a?(node.class) - - if expected.respond_to?(:children) && expected.respond_to?(:scalars) - children_equal = expected.children.all? do |expected_child| - node.children.find { |child| equivalent_node?(expected_child, child) } - end - - scalars_equal = expected.children.all? do |expected_child| - node.children.find { |child| equivalent_node?(expected_child, child) } - end - - children_equal && scalars_equal - else - expected == node - end - end - - describe "custom SDL directives" do - class CustomSDLDirectiveSchema < GraphQL::Schema - class CustomThing < GraphQL::Schema::Directive - locations(FIELD_DEFINITION) - argument :stuff, String - end - - directive CustomThing - - class Query < GraphQL::Schema::Object - field :f, Int, directives: { CustomThing => { stuff: "ok" } } - end - query(Query) - end - - it "prints them out" do - expected_str = <<~GRAPHQL - directive @customThing(stuff: String!) on FIELD_DEFINITION - - type Query { - f: Int @customThing(stuff: "ok") - } - GRAPHQL - assert_equal expected_str, CustomSDLDirectiveSchema.to_definition - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/language/equality_spec.rb b/vendor/gems/graphql/spec/graphql/language/equality_spec.rb deleted file mode 100644 index 62a44ed489f..00000000000 --- a/vendor/gems/graphql/spec/graphql/language/equality_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Language::Nodes::AbstractNode do - describe ".eql?" do - let(:document1) { GraphQL.parse(query_string1) } - let(:document2) { GraphQL.parse(query_string2) } - - describe "large identical document" do - let(:query_string1) {%| - query getStuff($someVar: Int = 1, $anotherVar: [String!], $skipNested: Boolean! = false) @skip(if: false) { - myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever") - anotherField(someArg: [1, 2, 3]) { - nestedField - ...moreNestedFields @skip(if: $skipNested) - } - ... on OtherType @include(unless: false) { - field(arg: [{ key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER }]) - anotherField - } - ... { - id - } - } - - fragment moreNestedFields on NestedType @or(something: "ok") { - anotherNestedField - } - |} - let(:query_string2) { query_string1 } - - it "should be equal" do - assert document1 == document2 - assert document2 == document1 - end - end - - describe "different operations" do - let(:query_string1) { "query { field }" } - let(:query_string2) { "mutation { setField }" } - - it "should not be equal" do - refute document1 == document2 - refute document2 == document1 - end - end - - describe "different query fields" do - let(:query_string1) { "query { foo }" } - let(:query_string2) { "query { bar }" } - - it "should not be equal" do - refute document1 == document2 - refute document2 == document1 - end - end - - describe "different schemas" do - let(:query_string1) {%| - schema { - query: Query - } - - type Query { - field: String! - } - |} - let(:query_string2) {%| - schema { - query: Query - } - - type Query { - field: Int! - } - |} - - it "should not be equal" do - refute document1.eql?(document2) - refute document2.eql?(document1) - end - end - end -end diff --git a/vendor/gems/graphql/spec/graphql/language/generation_spec.rb b/vendor/gems/graphql/spec/graphql/language/generation_spec.rb deleted file mode 100644 index 7f887b7be55..00000000000 --- a/vendor/gems/graphql/spec/graphql/language/generation_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true -require "spec_helper" - -describe GraphQL::Language::Generation do - describe "#to_query_tring" do - let(:document) { - GraphQL.parse('type Query { a: String! }') - } - - let(:custom_printer_class) { - Class.new(GraphQL::Language::Printer) { - def print_field_definition(print_field_definition) - print_string("