From 2e0f8f913f5218c61b4e6c7529345f200aaf82a5 Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Wed, 10 Jun 2026 21:34:30 -0500 Subject: [PATCH 1/5] Load the JSON ingestion spec extension default lazily Removes the load-time availability probe (`begin/rescue LoadError`) from `spec_support/schema_definition_helpers.rb`, as suggested on #1205. The `require` now happens only when a spec relies on the default extension modules, so the file loads fine in spec bundles that exclude the optional `elasticgraph-json_ingestion` gem (those suites pass `extension_modules:` explicitly), and a suite that relies on the default without the gem gets a clear `LoadError` instead of silently building artifacts without JSON schemas. Spec files that reference the extension constant directly now require it themselves instead of depending on the probe's load-time side effect. --- .../indexer/record_preparer_spec.rb | 1 + .../runtime_metadata_support.rb | 1 + .../spec_support/schema_definition_helpers.rb | 22 +++++-------------- spec_support/spec_helper.rb | 4 ++-- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/elasticgraph-indexer/spec/unit/elastic_graph/indexer/record_preparer_spec.rb b/elasticgraph-indexer/spec/unit/elastic_graph/indexer/record_preparer_spec.rb index 1ecc94fc3..bfea42d70 100644 --- a/elasticgraph-indexer/spec/unit/elastic_graph/indexer/record_preparer_spec.rb +++ b/elasticgraph-indexer/spec/unit/elastic_graph/indexer/record_preparer_spec.rb @@ -7,6 +7,7 @@ # frozen_string_literal: true require "elastic_graph/indexer/record_preparer" +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/spec_support/schema_definition_helpers" require "support/multiple_version_support" diff --git a/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb index c4c8bfb4e..31d7b76ff 100644 --- a/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb +++ b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb @@ -6,6 +6,7 @@ # # frozen_string_literal: true +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/spec_support/schema_definition_helpers" require "elastic_graph/spec_support/runtime_metadata_support" diff --git a/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb b/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb index d279fd460..16f584de2 100644 --- a/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb +++ b/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb @@ -8,21 +8,6 @@ require "elastic_graph/schema_definition/test_support" -module ElasticGraph - module SpecSupport - DEFAULT_SCHEMA_DEFINITION_EXTENSION_MODULES = begin - require "elastic_graph/json_ingestion/schema_definition/api_extension" - [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension] - rescue LoadError => e - # :nocov: -- per-gem spec bundles may not include the optional `elasticgraph-json_ingestion` gem. - raise unless e.path == "elastic_graph/json_ingestion/schema_definition/api_extension" - - [] - # :nocov: - end.freeze - end -end - # Combines `:capture_logs` with `ElasicGraph::SchemaDefinition::TestSupport` in order # to silence log output and fail if any tests result in logged warnings. ::RSpec.shared_context "SchemaDefinitionHelpers", :capture_logs do @@ -50,7 +35,12 @@ def define_schema_with_schema_elements(schema_elements, extension_modules: defau ) end + # The require is performed lazily (only when a spec relies on the default) so that this file can be + # loaded in spec bundles that do not include the optional `elasticgraph-json_ingestion` gem. Suites in + # such bundles must pass `extension_modules:` explicitly; if one relies on the default it will get a + # clear `LoadError` here rather than silently building schema artifacts without JSON schemas. def default_schema_definition_extension_modules - ::ElasticGraph::SpecSupport::DEFAULT_SCHEMA_DEFINITION_EXTENSION_MODULES.dup + require "elastic_graph/json_ingestion/schema_definition/api_extension" + [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension] end end diff --git a/spec_support/spec_helper.rb b/spec_support/spec_helper.rb index 6de600e91..42bc1a81c 100644 --- a/spec_support/spec_helper.rb +++ b/spec_support/spec_helper.rb @@ -363,8 +363,8 @@ def generate_schema_artifacts( enum_value_overrides_by_type: {}, reload_schema_artifacts: false ) + require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/schema_definition/test_support" - require "elastic_graph/spec_support/schema_definition_helpers" require "stringio" output = ::StringIO.new # to silence warnings. @@ -373,7 +373,7 @@ def generate_schema_artifacts( schema_element_name_overrides: schema_element_name_overrides, derived_type_name_formats: derived_type_name_formats, enum_value_overrides_by_type: enum_value_overrides_by_type, - extension_modules: ::ElasticGraph::SpecSupport::DEFAULT_SCHEMA_DEFINITION_EXTENSION_MODULES.dup, + extension_modules: [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension], reload_schema_artifacts: reload_schema_artifacts, output: output ) do |schema| From 4b3fc9bea8c82e48575ad08dc6983f05bf52c79c Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Thu, 11 Jun 2026 09:59:33 -0500 Subject: [PATCH 2/5] Thread schema definition extension modules through the spec builders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lazy default now lives in `CommonSpecHelpers` so that `generate_schema_artifacts` (and `build_datastore_core`, via a new `schema_definition_extension_modules` option) can use it too. The `elasticgraph-health_check` and `elasticgraph-query_interceptor` suites — whose bundles exclude `elasticgraph-json_ingestion` — pass `[]` explicitly. Spec files that reference the extension constant directly now require it themselves rather than relying on the deleted probe's load-time side effect. --- .../health_check/health_checker_spec.rb | 1 + .../spec/unit/envoy_extension_spec.rb | 2 +- .../indexing/json_schema_with_metadata_spec.rb | 1 + .../json_schema_field_metadata_spec.rb | 1 + .../schema_elements/scalar_type_extension_spec.rb | 1 + .../query_interceptor/graphql_extension_spec.rb | 2 +- .../runtime_metadata/runtime_metadata_support.rb | 1 - .../spec_support/builds_datastore_core.rb | 2 ++ .../spec_support/schema_definition_helpers.rb | 11 +++-------- spec_support/spec_helper.rb | 13 +++++++++++-- 10 files changed, 22 insertions(+), 13 deletions(-) diff --git a/elasticgraph-health_check/spec/unit/elastic_graph/health_check/health_checker_spec.rb b/elasticgraph-health_check/spec/unit/elastic_graph/health_check/health_checker_spec.rb index f1ee584da..806dc7357 100644 --- a/elasticgraph-health_check/spec/unit/elastic_graph/health_check/health_checker_spec.rb +++ b/elasticgraph-health_check/spec/unit/elastic_graph/health_check/health_checker_spec.rb @@ -467,6 +467,7 @@ def build_health_checker(health_check:, latest: {}, index_definitions: nil, sche clients_by_name: datastore_clients_by_name, clock: class_double(::Time, now: now), schema_definition: schema_definition, + schema_definition_extension_modules: [], index_definitions: index_definitions, extension_settings: {"health_check" => health_check_settings}.compact ) diff --git a/elasticgraph-health_check/spec/unit/envoy_extension_spec.rb b/elasticgraph-health_check/spec/unit/envoy_extension_spec.rb index acd06b7b7..b4f1c4782 100644 --- a/elasticgraph-health_check/spec/unit/envoy_extension_spec.rb +++ b/elasticgraph-health_check/spec/unit/envoy_extension_spec.rb @@ -145,7 +145,7 @@ def process(http_method, url, with_configured_path_segment:, body: nil, cluster_ def build_graphql_for_path(http_path_segment) config = {http_path_segment: http_path_segment}.compact - schema_artifacts = generate_schema_artifacts do |schema| + schema_artifacts = generate_schema_artifacts(extension_modules: []) do |schema| schema.register_graphql_extension(EnvoyExtension, defined_at: "elastic_graph/health_check/envoy_extension", **config) schema.object_type "Widget" do |t| t.field "id", "ID" diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb index 021d71793..a05977498 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb @@ -6,6 +6,7 @@ # # frozen_string_literal: true +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata" require "elastic_graph/spec_support/schema_definition_helpers" diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb index 0ebfeafe0..d6bd4d827 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb @@ -6,6 +6,7 @@ # # frozen_string_literal: true +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/indexing/json_schema_field_metadata" require "elastic_graph/spec_support/schema_definition_helpers" diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb index 364dfe2ee..8df14eb67 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb @@ -8,6 +8,7 @@ require "elastic_graph/constants" require "elastic_graph/errors" +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension" require "elastic_graph/spec_support/schema_definition_helpers" diff --git a/elasticgraph-query_interceptor/spec/unit/elastic_graph/query_interceptor/graphql_extension_spec.rb b/elasticgraph-query_interceptor/spec/unit/elastic_graph/query_interceptor/graphql_extension_spec.rb index 8407e57c4..cf61aa6ba 100644 --- a/elasticgraph-query_interceptor/spec/unit/elastic_graph/query_interceptor/graphql_extension_spec.rb +++ b/elasticgraph-query_interceptor/spec/unit/elastic_graph/query_interceptor/graphql_extension_spec.rb @@ -170,7 +170,7 @@ def expect_configured_interceptors(graphql) end def generate_schema_artifacts - super do |schema| + super(extension_modules: []) do |schema| yield schema # Ensure there's at least one indexed type defined to avoid GraphQL validation errors. diff --git a/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb index 31d7b76ff..c4c8bfb4e 100644 --- a/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb +++ b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/runtime_metadata/runtime_metadata_support.rb @@ -6,7 +6,6 @@ # # frozen_string_literal: true -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/spec_support/schema_definition_helpers" require "elastic_graph/spec_support/runtime_metadata_support" diff --git a/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb b/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb index bca599c27..d3d7dc9a9 100644 --- a/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb +++ b/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb @@ -29,6 +29,7 @@ def build_datastore_core( clusters: nil, schema_artifacts_directory: nil, schema_artifacts: nil, + schema_definition_extension_modules: nil, datastore_backend: nil, reload_schema_artifacts: false, **config_overrides, @@ -59,6 +60,7 @@ def build_datastore_core( schema_element_name_overrides: schema_element_name_overrides, derived_type_name_formats: derived_type_name_formats, enum_value_overrides_by_type: enum_value_overrides_by_type, + extension_modules: schema_definition_extension_modules || default_schema_definition_extension_modules, reload_schema_artifacts: reload_schema_artifacts, &schema_definition ) diff --git a/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb b/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb index 16f584de2..4f2c88af6 100644 --- a/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb +++ b/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb @@ -35,12 +35,7 @@ def define_schema_with_schema_elements(schema_elements, extension_modules: defau ) end - # The require is performed lazily (only when a spec relies on the default) so that this file can be - # loaded in spec bundles that do not include the optional `elasticgraph-json_ingestion` gem. Suites in - # such bundles must pass `extension_modules:` explicitly; if one relies on the default it will get a - # clear `LoadError` here rather than silently building schema artifacts without JSON schemas. - def default_schema_definition_extension_modules - require "elastic_graph/json_ingestion/schema_definition/api_extension" - [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension] - end + # `default_schema_definition_extension_modules` is provided by `CommonSpecHelpers`, which performs + # the `elasticgraph-json_ingestion` require lazily so this file can be loaded in spec bundles that + # do not include that optional gem. end diff --git a/spec_support/spec_helper.rb b/spec_support/spec_helper.rb index 42bc1a81c..08f01344f 100644 --- a/spec_support/spec_helper.rb +++ b/spec_support/spec_helper.rb @@ -356,14 +356,23 @@ def without_vcr end # :nocov: + # The require is performed lazily (only when a spec relies on the default) so that this can be used + # from spec bundles that do not include the optional `elasticgraph-json_ingestion` gem. Suites in + # such bundles must pass `extension_modules:` explicitly; if one relies on the default it will get a + # clear `LoadError` here rather than silently building schema artifacts without JSON schemas. + def default_schema_definition_extension_modules + require "elastic_graph/json_ingestion/schema_definition/api_extension" + [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension] + end + def generate_schema_artifacts( schema_element_name_form: :snake_case, schema_element_name_overrides: {}, derived_type_name_formats: {}, enum_value_overrides_by_type: {}, + extension_modules: default_schema_definition_extension_modules, reload_schema_artifacts: false ) - require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/schema_definition/test_support" require "stringio" @@ -373,7 +382,7 @@ def generate_schema_artifacts( schema_element_name_overrides: schema_element_name_overrides, derived_type_name_formats: derived_type_name_formats, enum_value_overrides_by_type: enum_value_overrides_by_type, - extension_modules: [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension], + extension_modules: extension_modules, reload_schema_artifacts: reload_schema_artifacts, output: output ) do |schema| From 6d16940150cfe5535035259edfe7392131c97277 Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Thu, 11 Jun 2026 11:54:37 -0500 Subject: [PATCH 3/5] Make schema definition extension modules fully opt-in in specs Nothing needs the defaulting anymore. The shared spec helpers no longer apply the JSON ingestion extension to schema definitions; instead, each place that genuinely needs JSON schemas opts in explicitly: - `generate_schema_artifacts` includes the extension only when loading the repository's main test schema (`config/schema.rb`), which uses the JSON ingestion schema definition DSL. - `build_indexer` includes the extension when it generates artifacts itself, since the indexer can only ingest JSON events today. The require is lazy so bundles without `elasticgraph-json_ingestion` can still build an indexer from an externally built `datastore_core:`. - Specs that index documents with inline schemas (and so need JSON schema validation) pass the extension at their build call sites. - Vestigial `t.json_schema` calls in unit specs that never index are removed, along with a `JsonSafeLong` placeholder assertion that duplicated coverage now living in elasticgraph-json_ingestion. --- .../rollover_index_template_spec.rb | 7 ++++ ...elasticgraph_graphql_acceptance_support.rb | 4 +++ .../graphql/datastore_query/misc_spec.rb | 3 +- .../resolvers/nested_relationships_spec.rb | 11 ++++--- .../graphql/query_executor_spec.rb | 1 - .../resolvers/get_record_field_value_spec.rb | 1 - .../scalar_coercion_adapters/no_op_spec.rb | 1 - .../elastic_graph/graphql/schema/type_spec.rb | 5 --- .../unit/elastic_graph/graphql/schema_spec.rb | 1 - .../spec/support/multiple_version_support.rb | 8 ++++- .../indexing/wrappers_spec.rb | 5 +-- .../json_schema_pruner_spec.rb | 3 +- .../schema_definition/json_schema_spec.rb | 21 ++++++------ .../scalar_type_extension_spec.rb | 6 ++-- .../schema_definition/test_support.rb | 2 ++ .../spec_support/builds_datastore_core.rb | 4 +-- .../spec_support/builds_indexer.rb | 20 +++++++++++- .../spec_support/schema_definition_helpers.rb | 32 ++++++------------- spec_support/spec_helper.rb | 20 ++++++------ 19 files changed, 88 insertions(+), 67 deletions(-) diff --git a/elasticgraph-datastore_core/spec/integration/elastic_graph/datastore_core/index_definition/rollover_index_template_spec.rb b/elasticgraph-datastore_core/spec/integration/elastic_graph/datastore_core/index_definition/rollover_index_template_spec.rb index eac218050..3843513a4 100644 --- a/elasticgraph-datastore_core/spec/integration/elastic_graph/datastore_core/index_definition/rollover_index_template_spec.rb +++ b/elasticgraph-datastore_core/spec/integration/elastic_graph/datastore_core/index_definition/rollover_index_template_spec.rb @@ -8,6 +8,7 @@ require "elastic_graph/constants" require "elastic_graph/datastore_core/index_definition" +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/support/hash_util" require "stringio" require_relative "implementation_shared_examples" @@ -16,6 +17,12 @@ module ElasticGraph class DatastoreCore module IndexDefinition RSpec.describe RolloverIndexTemplate, :uses_datastore, :builds_indexer do + # Documents indexed by these specs are validated against the JSON schemas, so the schemas + # defined in this file all include the JSON ingestion schema definition extension. + def build_datastore_core(**options, &block) + super(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], **options, &block) + end + # Use different index names than any other tests use, because most tests expect a specific index # configuration (based on `config/schema.graphql`) and we do not want to mess with it here. let(:index_prefix) { unique_index_name } diff --git a/elasticgraph-graphql/spec/acceptance/elasticgraph_graphql_acceptance_support.rb b/elasticgraph-graphql/spec/acceptance/elasticgraph_graphql_acceptance_support.rb index 9956e95e9..e7b3c5304 100644 --- a/elasticgraph-graphql/spec/acceptance/elasticgraph_graphql_acceptance_support.rb +++ b/elasticgraph-graphql/spec/acceptance/elasticgraph_graphql_acceptance_support.rb @@ -7,6 +7,7 @@ # frozen_string_literal: true require "elastic_graph/graphql/datastore_search_router" +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/schema_definition/schema_elements/type_namer" require "elastic_graph/spec_support/builds_admin" require "graphql" @@ -91,6 +92,9 @@ def self.with_both_casing_forms(&block) schema_element_name_form: :camelCase, derived_type_name_formats: derived_type_name_formats, enum_value_overrides_by_type: enum_value_overrides_by_type, + # The camelCase schema definition below is derived from the repository's main test schema, + # which uses the JSON ingestion schema definition DSL, so it requires this extension. + schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: ->(schema) do # standard:disable Security/Eval -- it's ok here in a test. schema.as_active_instance { eval(camel_case_schema_def) } diff --git a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/misc_spec.rb b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/misc_spec.rb index 90ccfe347..e44d8d0e5 100644 --- a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/misc_spec.rb +++ b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/misc_spec.rb @@ -9,6 +9,7 @@ require_relative "datastore_query_integration_support" require "elastic_graph/errors" require "elastic_graph/graphql/datastore_search_router" +require "elastic_graph/json_ingestion/schema_definition/api_extension" module ElasticGraph class GraphQL @@ -85,7 +86,7 @@ def msearch(body:, **args, &block) context "when the indexed type has nested `default_sort_fields`" do let(:graphql) do - build_graphql(schema_definition: lambda do |schema| + build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| schema.object_type "Money" do |t| t.field "currency", "String!" t.field "amount_cents", "Int!" diff --git a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_spec.rb b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_spec.rb index a09583209..2657de370 100644 --- a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_spec.rb +++ b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_spec.rb @@ -7,6 +7,7 @@ # frozen_string_literal: true require "elastic_graph/graphql/resolvers/nested_relationships" +require "elastic_graph/json_ingestion/schema_definition/api_extension" module ElasticGraph class GraphQL @@ -16,7 +17,7 @@ module Resolvers # is implemented via a filter on `id` (the search routing field) context "when the field being resolved is a relay connection field", :expect_search_routing do let(:graphql) do - build_graphql(schema_definition: lambda do |schema| + build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| schema.object_type "Component" do |t| t.field "id", "ID!" t.field "name", "String!" @@ -62,7 +63,7 @@ module Resolvers describe "a relates_to_many/relates_to_one bidirectional relationship with an array foreign key from the one to the many" do let(:graphql) do - build_graphql(schema_definition: lambda do |schema| + build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| schema.object_type "Money" do |t| t.field "currency", "String!" t.field "amount_cents", "Int" @@ -241,7 +242,7 @@ module Resolvers describe "a relates_to_many/relates_to_one bidirectional relationship with a scalar foreign key from the many to the one" do let(:graphql) do - build_graphql(schema_definition: lambda do |schema| + build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| schema.object_type "ElectricalPart" do |t| t.field "id", "ID!" t.field "name", "String!" @@ -381,7 +382,7 @@ module Resolvers describe "a relates_to_many/relates_to_many bidirectional relationship with an array foreign key from a many to a many" do let(:graphql) do - build_graphql(schema_definition: lambda do |schema| + build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| schema.object_type "Component" do |t| t.field "id", "ID!" t.field "name", "String!" @@ -573,7 +574,7 @@ module Resolvers describe "a relates_to_one/relates_to_one bidirectional relationship with a scalar foreign key from a one to a one" do let(:graphql) do - build_graphql(schema_definition: lambda do |schema| + build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| schema.object_type "Manufacturer" do |t| t.field "id", "ID!" t.field "name", "String" diff --git a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/query_executor_spec.rb b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/query_executor_spec.rb index 7cf193e1d..ec1e962a7 100644 --- a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/query_executor_spec.rb +++ b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/query_executor_spec.rb @@ -447,7 +447,6 @@ def resolve(field:, object:, args:, context:) schema.scalar_type "Operands" do |t| t.mapping type: nil - t.json_schema type: "null" end schema.object_type "Widget" do |t| diff --git a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/resolvers/get_record_field_value_spec.rb b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/resolvers/get_record_field_value_spec.rb index 89479c1f8..10c32c891 100644 --- a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/resolvers/get_record_field_value_spec.rb +++ b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/resolvers/get_record_field_value_spec.rb @@ -19,7 +19,6 @@ module Resolvers self.schema_artifacts = generate_schema_artifacts do |schema| schema.scalar_type "MyInt" do |t| t.mapping type: "integer" - t.json_schema type: "integer" end schema.object_type "PersonIdentifiers" do |t| diff --git a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/scalar_coercion_adapters/no_op_spec.rb b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/scalar_coercion_adapters/no_op_spec.rb index cb84c3ff4..73bb09df8 100644 --- a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/scalar_coercion_adapters/no_op_spec.rb +++ b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/scalar_coercion_adapters/no_op_spec.rb @@ -14,7 +14,6 @@ module ScalarCoercionAdapters RSpec.describe "NoOp" do include_context("scalar coercion adapter support", "SomeCustomScalar", schema_definition: ->(schema) do schema.scalar_type "SomeCustomScalar" do |t| - t.json_schema type: "null" t.mapping type: nil end diff --git a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema/type_spec.rb b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema/type_spec.rb index 48dc10135..90f21fdb8 100644 --- a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema/type_spec.rb +++ b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema/type_spec.rb @@ -955,7 +955,6 @@ def search_index_definitions_from(type_name: "TheType") schema = define_schema do |s| s.scalar_type "CustomScalar" do |t| t.mapping type: "keyword" - t.json_schema type: "string" end end @@ -967,7 +966,6 @@ def search_index_definitions_from(type_name: "TheType") schema = define_schema do |s| s.scalar_type "CustomScalar" do |t| t.mapping type: "keyword" - t.json_schema type: "string" t.grouping_missing_value_placeholder "MISSING" end end @@ -980,7 +978,6 @@ def search_index_definitions_from(type_name: "TheType") schema = define_schema do |s| s.scalar_type "CustomScalar" do |t| t.mapping type: "keyword" - t.json_schema type: "string" t.grouping_missing_value_placeholder nil end end @@ -1007,7 +1004,6 @@ def search_index_definitions_from(type_name: "TheType") t.field "name", "String" t.field "count", "Int" t.field "price", "Float" - t.field "big_number", "JsonSafeLong" t.index "things" end end @@ -1016,7 +1012,6 @@ def search_index_definitions_from(type_name: "TheType") expect(schema.type_named("String").grouping_missing_value_placeholder).to eq MISSING_STRING_PLACEHOLDER_VALUE expect(schema.type_named("Int").grouping_missing_value_placeholder).to eq MISSING_NUMERIC_PLACEHOLDER expect(schema.type_named("Float").grouping_missing_value_placeholder).to eq MISSING_NUMERIC_PLACEHOLDER - expect(schema.type_named("JsonSafeLong").grouping_missing_value_placeholder).to eq MISSING_NUMERIC_PLACEHOLDER end it "returns nil for object types" do diff --git a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema_spec.rb b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema_spec.rb index af1a8465e..2327ee1ad 100644 --- a/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema_spec.rb +++ b/elasticgraph-graphql/spec/unit/elastic_graph/graphql/schema_spec.rb @@ -16,7 +16,6 @@ class GraphQL define_schema do |schema| schema.scalar_type "_FieldSet" do |t| t.mapping type: "keyword" - t.json_schema type: "string" end schema.object_type "Widget" do |t| diff --git a/elasticgraph-indexer/spec/support/multiple_version_support.rb b/elasticgraph-indexer/spec/support/multiple_version_support.rb index f385d8cd4..9831a2174 100644 --- a/elasticgraph-indexer/spec/support/multiple_version_support.rb +++ b/elasticgraph-indexer/spec/support/multiple_version_support.rb @@ -6,6 +6,7 @@ # # frozen_string_literal: true +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/spec_support/schema_definition_helpers" module ElasticGraph @@ -15,7 +16,12 @@ class Indexer def build_indexer_with_multiple_schema_versions(schema_versions:) results_by_version = schema_versions.to_h do |json_schema_version, prior_def| - results = define_schema(schema_element_name_form: :snake_case, json_schema_version: json_schema_version, &prior_def) + results = define_schema( + schema_element_name_form: :snake_case, + json_schema_version: json_schema_version, + extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], + &prior_def + ) [json_schema_version, results] end diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb index feb265303..4af8657d3 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb @@ -8,6 +8,7 @@ require "elastic_graph/constants" require "elastic_graph/errors" +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/indexing/field" require "elastic_graph/json_ingestion/schema_definition/indexing/field_reference" require "elastic_graph/json_ingestion/schema_definition/indexing/field_type/object" @@ -160,7 +161,7 @@ def indexing_leaf_field_type(leaf_kind) end def object_types_by_name - @object_types_by_name ||= define_schema(schema_element_name_form: "snake_case") do |s| + @object_types_by_name ||= define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.enum_type "Color" do |t| t.values "RED", "BLUE" end @@ -195,7 +196,7 @@ def object_types_by_name end def dump_schema(&schema_definition) - define_schema(schema_element_name_form: "snake_case", &schema_definition).current_public_json_schema + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension], &schema_definition).current_public_json_schema end def json_schema_ref(type, is_keyword_type: %w[ID! ID String! String].include?(type)) diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb index c8327661f..ab2d2f217 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb @@ -7,6 +7,7 @@ # frozen_string_literal: true require "elastic_graph/constants" +require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/json_schema_pruner" require "elastic_graph/spec_support/schema_definition_helpers" @@ -120,7 +121,7 @@ module JSONIngestion::SchemaDefinition end def dump_schema(&schema_definition) - schema_definition_results = define_schema(schema_element_name_form: "snake_case", &schema_definition) + schema_definition_results = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension], &schema_definition) latest_json_schema_version = schema_definition_results.latest_json_schema_version schema_definition_results.json_schemas_for(latest_json_schema_version) diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb index 1c964801d..31352364f 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb @@ -2972,7 +2972,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) end it "sets json_schema_version to the specified (valid) value" do - result = define_schema(schema_element_name_form: "snake_case") do |s| + result = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version 1 end.json_schemas_for(1) @@ -2980,7 +2980,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) end it "allows json_schema_version enforcement to be disabled" do - result = define_schema(schema_element_name_form: "snake_case") do |s| + result = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.enforce_json_schema_version false end @@ -2989,7 +2989,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "fails if json_schema_version enforcement is set to a non-boolean value" do expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.enforce_json_schema_version nil end }.to raise_error(Errors::SchemaError, a_string_including("must be a boolean", "nil")) @@ -2997,25 +2997,25 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "fails if json_schema_version is set to invalid values" do expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version 0.5 end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: 0.5")) expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version "asd" end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: asd")) expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version 0 end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: 0")) expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version(-1) end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: -1")) @@ -3023,13 +3023,13 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "fails if json_schema_version is left unset" do expect { - define_schema(schema_element_name_form: "snake_case", json_schema_version: nil) {}.available_json_schema_versions + define_schema(schema_element_name_form: "snake_case", json_schema_version: nil, extension_modules: [APIExtension]) {}.available_json_schema_versions }.to raise_error(Errors::SchemaError, a_string_including("must be specified in the schema")) end it "fails if json_schema_version is set multiple times" do expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version 1 s.json_schema_version 2 end @@ -3038,7 +3038,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "is unable to return a non-existent schema version" do expect { - define_schema(schema_element_name_form: "snake_case") do |s| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| s.json_schema_version 1 end.json_schemas_for(2) }.to raise_error(Errors::NotFoundError, a_string_including("The requested json schema version (2) is not available", "Available versions: 1")) @@ -3179,6 +3179,7 @@ def all_type_definitions_for(&schema_definition) def dump_schema(type_name_overrides: {}, enum_value_overrides_by_type: {}, &schema_definition) define_schema( schema_element_name_form: "snake_case", + extension_modules: [APIExtension], type_name_overrides: type_name_overrides, enum_value_overrides_by_type: enum_value_overrides_by_type, &schema_definition diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb index 8df14eb67..085deb99b 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb @@ -21,7 +21,7 @@ module SchemaElements it "requires custom scalar types to declare their JSON schema representation" do expect { - define_schema(schema_element_name_form: "snake_case") do |schema| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |schema| schema.scalar_type "BigInt" do |type| type.mapping type: "long" end @@ -174,7 +174,7 @@ module SchemaElements end it "has the expected placeholder for each built-in scalar type, including the JSON-safe-range-aware `JsonSafeLong` inference" do - results = define_schema(schema_element_name_form: "snake_case") { |schema| } + results = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) { |schema| } built_in_scalars = results.state.scalar_types_by_name.keys scalar_types_by_name = results.runtime_metadata.scalar_types_by_name @@ -200,7 +200,7 @@ module SchemaElements end def grouping_missing_value_placeholder_for(mapping_type, **json_schema_options) - define_schema(schema_element_name_form: "snake_case") do |schema| + define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |schema| schema.scalar_type "CustomScalar" do |type| type.mapping type: mapping_type type.json_schema(**json_schema_options) diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/test_support.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/test_support.rb index 9dcc2533a..5a2402501 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/test_support.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/test_support.rb @@ -79,8 +79,10 @@ def define_schema_with_schema_elements( # exists when an extension that implements JSON schema generation (such as # `elasticgraph-json_ingestion`) is loaded; without one there is no JSON schema version to set. if !json_schema_version.nil? && api.respond_to?(:json_schema_version) + # :nocov: -- only entered when a JSON schema extension is loaded, which is not the case in elasticgraph-schema_definition's tests. versioned_api = api # : untyped versioned_api.json_schema_version(json_schema_version) if versioned_api.state.json_schema_version.nil? + # :nocov: end # :nocov: -- the else branch and code past this aren't used by tests in elasticgraph-schema_definition. diff --git a/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb b/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb index d3d7dc9a9..0edb7c041 100644 --- a/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb +++ b/spec_support/lib/elastic_graph/spec_support/builds_datastore_core.rb @@ -29,7 +29,7 @@ def build_datastore_core( clusters: nil, schema_artifacts_directory: nil, schema_artifacts: nil, - schema_definition_extension_modules: nil, + schema_definition_extension_modules: [], datastore_backend: nil, reload_schema_artifacts: false, **config_overrides, @@ -60,7 +60,7 @@ def build_datastore_core( schema_element_name_overrides: schema_element_name_overrides, derived_type_name_formats: derived_type_name_formats, enum_value_overrides_by_type: enum_value_overrides_by_type, - extension_modules: schema_definition_extension_modules || default_schema_definition_extension_modules, + extension_modules: schema_definition_extension_modules, reload_schema_artifacts: reload_schema_artifacts, &schema_definition ) diff --git a/spec_support/lib/elastic_graph/spec_support/builds_indexer.rb b/spec_support/lib/elastic_graph/spec_support/builds_indexer.rb index 23fa72092..33ae7fbf3 100644 --- a/spec_support/lib/elastic_graph/spec_support/builds_indexer.rb +++ b/spec_support/lib/elastic_graph/spec_support/builds_indexer.rb @@ -21,11 +21,18 @@ def build_indexer( datastore_router: nil, clock: nil, monotonic_clock: nil, + schema_definition_extension_modules: nil, **datastore_core_options, &customize_datastore_config ) + datastore_core ||= build_datastore_core( + schema_definition_extension_modules: schema_definition_extension_modules || default_indexer_schema_definition_extension_modules, + **datastore_core_options, + &customize_datastore_config + ) + Indexer.new( - datastore_core: datastore_core || build_datastore_core(**datastore_core_options, &customize_datastore_config), + datastore_core: datastore_core, config: Indexer::Config.new( latency_slo_thresholds_by_timestamp_in_ms: latency_slo_thresholds_by_timestamp_in_ms, skip_derived_indexing_type_updates: skip_derived_indexing_type_updates @@ -35,6 +42,17 @@ def build_indexer( monotonic_clock: monotonic_clock ) end + + private + + # The indexer can only ingest JSON events today, so artifacts generated for an indexer must + # include the JSON schemas; this is the one spec builder that opts into the extension by default. + # The require is lazy so that gems whose bundles do not include `elasticgraph-json_ingestion` + # can still use this builder with an externally built `datastore_core:`. + def default_indexer_schema_definition_extension_modules + require "elastic_graph/json_ingestion/schema_definition/api_extension" + [JSONIngestion::SchemaDefinition::APIExtension] + end end RSpec.configure do |c| diff --git a/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb b/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb index 4f2c88af6..de63f4e7d 100644 --- a/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb +++ b/spec_support/lib/elastic_graph/spec_support/schema_definition_helpers.rb @@ -13,29 +13,17 @@ ::RSpec.shared_context "SchemaDefinitionHelpers", :capture_logs do include ::ElasticGraph::SchemaDefinition::TestSupport - # Defaults `extension_modules` and `output` for tests; all other options are forwarded to - # `TestSupport` unchanged. `output` must be handled with `||` (rather than a keyword default) - # because `TestSupport#define_schema` passes `output: nil` explicitly when no output is given. - def define_schema(extension_modules: default_schema_definition_extension_modules, output: nil, **options, &block) - super( - extension_modules: extension_modules, - output: output || log_device, - **options, - &block - ) + # Defaults `output` for tests; all other options are forwarded to `TestSupport` unchanged. + # `output` must be handled with `||` (rather than a keyword default) because + # `TestSupport#define_schema` passes `output: nil` explicitly when no output is given. + # + # Note: schema definition extension modules (such as the one from `elasticgraph-json_ingestion`) + # are intentionally not defaulted; specs that need an extension must opt in explicitly. + def define_schema(output: nil, **options, &block) + super(output: output || log_device, **options, &block) end - def define_schema_with_schema_elements(schema_elements, extension_modules: default_schema_definition_extension_modules, output: nil, **options, &block) - super( - schema_elements, - extension_modules: extension_modules, - output: output || log_device, - **options, - &block - ) + def define_schema_with_schema_elements(schema_elements, output: nil, **options, &block) + super(schema_elements, output: output || log_device, **options, &block) end - - # `default_schema_definition_extension_modules` is provided by `CommonSpecHelpers`, which performs - # the `elasticgraph-json_ingestion` require lazily so this file can be loaded in spec bundles that - # do not include that optional gem. end diff --git a/spec_support/spec_helper.rb b/spec_support/spec_helper.rb index 08f01344f..86191369f 100644 --- a/spec_support/spec_helper.rb +++ b/spec_support/spec_helper.rb @@ -356,26 +356,26 @@ def without_vcr end # :nocov: - # The require is performed lazily (only when a spec relies on the default) so that this can be used - # from spec bundles that do not include the optional `elasticgraph-json_ingestion` gem. Suites in - # such bundles must pass `extension_modules:` explicitly; if one relies on the default it will get a - # clear `LoadError` here rather than silently building schema artifacts without JSON schemas. - def default_schema_definition_extension_modules - require "elastic_graph/json_ingestion/schema_definition/api_extension" - [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension] - end - + # `extension_modules` intentionally defaults to none: specs must opt in explicitly to schema + # definition extensions (such as the one from `elasticgraph-json_ingestion`). def generate_schema_artifacts( + extension_modules: [], schema_element_name_form: :snake_case, schema_element_name_overrides: {}, derived_type_name_formats: {}, enum_value_overrides_by_type: {}, - extension_modules: default_schema_definition_extension_modules, reload_schema_artifacts: false ) require "elastic_graph/schema_definition/test_support" require "stringio" + unless block_given? + # When no block is given we load the repository's main test schema (`config/schema.rb`), + # which uses the JSON ingestion schema definition DSL, so it requires this extension. + require "elastic_graph/json_ingestion/schema_definition/api_extension" + extension_modules += [JSONIngestion::SchemaDefinition::APIExtension] + end + output = ::StringIO.new # to silence warnings. ::ElasticGraph::SchemaDefinition::TestSupport.define_schema( schema_element_name_form: schema_element_name_form, From 6a733f19e3d40cf0f649ad4ed3d43885aab4d2ba Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Thu, 11 Jun 2026 21:41:12 -0500 Subject: [PATCH 4/5] Apply the JSON ingestion spec extension via a gem-local shared context Every spec in elasticgraph-json_ingestion exercises behavior provided by APIExtension, so a gem-local "JSONIngestionSchemaDefinitionHelpers" context (which extends the shared "SchemaDefinitionHelpers" context) now applies the extension to every schema its specs define, instead of each `define_schema` call site passing `extension_modules:` explicitly. --- ...son_ingestion_schema_definition_helpers.rb | 28 +++++++++++++++++ .../json_schema_with_metadata_spec.rb | 6 ++-- .../indexing/wrappers_spec.rb | 9 +++--- .../json_schema_field_metadata_spec.rb | 6 ++-- .../json_schema_pruner_spec.rb | 7 ++--- .../schema_definition/json_schema_spec.rb | 31 ++++++++----------- .../scalar_type_extension_spec.rb | 10 +++--- 7 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb diff --git a/elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb b/elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb new file mode 100644 index 000000000..1cae87905 --- /dev/null +++ b/elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb @@ -0,0 +1,28 @@ +# Copyright 2024 - 2026 Block, Inc. +# +# Use of this source code is governed by an MIT-style +# license that can be found in the LICENSE file or at +# https://opensource.org/licenses/MIT. +# +# frozen_string_literal: true + +require "elastic_graph/json_ingestion/schema_definition/api_extension" +require "elastic_graph/spec_support/schema_definition_helpers" + +# Extends the shared "SchemaDefinitionHelpers" context to automatically apply this gem's +# `APIExtension` to every defined schema, since every spec in this gem exercises behavior +# provided by that extension. Additional extension modules can still be passed via +# `extension_modules:` and will be applied alongside it. +::RSpec.shared_context "JSONIngestionSchemaDefinitionHelpers" do + include_context "SchemaDefinitionHelpers" + + def define_schema_with_schema_elements(schema_elements, extension_modules: [], output: nil, **options, &block) + super( + schema_elements, + extension_modules: [::ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension] | extension_modules, + output: output || log_device, + **options, + &block + ) + end +end diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb index a05977498..a116267d2 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata_spec.rb @@ -6,15 +6,14 @@ # # frozen_string_literal: true -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata" -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion::SchemaDefinition module Indexing ::RSpec.describe JSONSchemaWithMetadata do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" it "ignores derived indexed types that do not show up in the JSON schema" do v1_json_schema = dump_versioned_json_schema do |schema| @@ -1057,7 +1056,6 @@ def metadata_for(json_schema, type, field) def define_schema(&schema_definition) super( schema_element_name_form: "snake_case", - extension_modules: [APIExtension], &schema_definition ) end diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb index 4af8657d3..790c9e2fb 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb @@ -8,17 +8,16 @@ require "elastic_graph/constants" require "elastic_graph/errors" -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/indexing/field" require "elastic_graph/json_ingestion/schema_definition/indexing/field_reference" require "elastic_graph/json_ingestion/schema_definition/indexing/field_type/object" -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" require "support/json_schema_matcher" module ElasticGraph module JSONIngestion::SchemaDefinition ::RSpec.describe "JSON schema indexing wrappers" do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" # `FieldReference#resolve` is a lazy reference: the referenced type need not exist when a field is # defined, only when artifacts are dumped. These two specs drive both outcomes (resolves / never @@ -161,7 +160,7 @@ def indexing_leaf_field_type(leaf_kind) end def object_types_by_name - @object_types_by_name ||= define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + @object_types_by_name ||= define_schema(schema_element_name_form: "snake_case") do |s| s.enum_type "Color" do |t| t.values "RED", "BLUE" end @@ -196,7 +195,7 @@ def object_types_by_name end def dump_schema(&schema_definition) - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension], &schema_definition).current_public_json_schema + define_schema(schema_element_name_form: "snake_case", &schema_definition).current_public_json_schema end def json_schema_ref(type, is_keyword_type: %w[ID! ID String! String].include?(type)) diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb index d6bd4d827..b06ac7980 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_field_metadata_spec.rb @@ -6,14 +6,13 @@ # # frozen_string_literal: true -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/indexing/json_schema_field_metadata" -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion::SchemaDefinition ::RSpec.describe "JSON schema field metadata generation" do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" it "generates no field metadata for built-in scalar and enum types" do metadata_by_type_and_field_name = dump_metadata @@ -144,7 +143,6 @@ def dump_metadata(&schema_definition) def define_schema(&schema_definition) super( schema_element_name_form: "snake_case", - extension_modules: [APIExtension], &schema_definition ) end diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb index ab2d2f217..9a6b878c6 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_pruner_spec.rb @@ -7,14 +7,13 @@ # frozen_string_literal: true require "elastic_graph/constants" -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/json_schema_pruner" -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion::SchemaDefinition RSpec.describe JSONSchemaPruner do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" describe ".prune" do subject { described_class.prune(schema) } @@ -121,7 +120,7 @@ module JSONIngestion::SchemaDefinition end def dump_schema(&schema_definition) - schema_definition_results = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension], &schema_definition) + schema_definition_results = define_schema(schema_element_name_form: "snake_case", &schema_definition) latest_json_schema_version = schema_definition_results.latest_json_schema_version schema_definition_results.json_schemas_for(latest_json_schema_version) diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb index 31352364f..507eaef43 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/json_schema_spec.rb @@ -8,14 +8,13 @@ require "elastic_graph/constants" require "elastic_graph/errors" -require "elastic_graph/json_ingestion/schema_definition/api_extension" -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" require "support/json_schema_matcher" module ElasticGraph module JSONIngestion::SchemaDefinition ::RSpec.describe "JSON schema generation" do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" json_schema_id = {"allOf" => [{"$ref" => "#/$defs/ID"}, {"maxLength" => DEFAULT_MAX_KEYWORD_LENGTH}]} json_schema_float = {"$ref" => "#/$defs/Float"} json_schema_integer = {"$ref" => "#/$defs/Int"} @@ -113,10 +112,7 @@ module JSONIngestion::SchemaDefinition it "configures built-in scalar JSON schema before user schema blocks are evaluated" do json_schema_options_in_schema_block = nil - define_schema( - schema_element_name_form: "snake_case", - extension_modules: [JSONIngestion::SchemaDefinition::APIExtension] - ) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| json_schema_options_in_schema_block = s.state.scalar_types_by_name.fetch("String").json_schema_options.dup s.object_type "Widget" do |t| @@ -2972,7 +2968,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) end it "sets json_schema_version to the specified (valid) value" do - result = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + result = define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version 1 end.json_schemas_for(1) @@ -2980,7 +2976,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) end it "allows json_schema_version enforcement to be disabled" do - result = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + result = define_schema(schema_element_name_form: "snake_case") do |s| s.enforce_json_schema_version false end @@ -2989,7 +2985,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "fails if json_schema_version enforcement is set to a non-boolean value" do expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.enforce_json_schema_version nil end }.to raise_error(Errors::SchemaError, a_string_including("must be a boolean", "nil")) @@ -2997,25 +2993,25 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "fails if json_schema_version is set to invalid values" do expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version 0.5 end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: 0.5")) expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version "asd" end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: asd")) expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version 0 end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: 0")) expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version(-1) end }.to raise_error(Errors::SchemaError, a_string_including("must be a positive integer. Specified version: -1")) @@ -3023,13 +3019,13 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "fails if json_schema_version is left unset" do expect { - define_schema(schema_element_name_form: "snake_case", json_schema_version: nil, extension_modules: [APIExtension]) {}.available_json_schema_versions + define_schema(schema_element_name_form: "snake_case", json_schema_version: nil) {}.available_json_schema_versions }.to raise_error(Errors::SchemaError, a_string_including("must be specified in the schema")) end it "fails if json_schema_version is set multiple times" do expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version 1 s.json_schema_version 2 end @@ -3038,7 +3034,7 @@ def link_supertype_to_subtypes(interface_type, *subtype_names) it "is unable to return a non-existent schema version" do expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |s| + define_schema(schema_element_name_form: "snake_case") do |s| s.json_schema_version 1 end.json_schemas_for(2) }.to raise_error(Errors::NotFoundError, a_string_including("The requested json schema version (2) is not available", "Available versions: 1")) @@ -3179,7 +3175,6 @@ def all_type_definitions_for(&schema_definition) def dump_schema(type_name_overrides: {}, enum_value_overrides_by_type: {}, &schema_definition) define_schema( schema_element_name_form: "snake_case", - extension_modules: [APIExtension], type_name_overrides: type_name_overrides, enum_value_overrides_by_type: enum_value_overrides_by_type, &schema_definition diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb index 085deb99b..8497479c6 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension_spec.rb @@ -10,18 +10,18 @@ require "elastic_graph/errors" require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/json_ingestion/schema_definition/schema_elements/scalar_type_extension" -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion module SchemaDefinition module SchemaElements RSpec.describe ScalarTypeExtension do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" it "requires custom scalar types to declare their JSON schema representation" do expect { - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |schema| + define_schema(schema_element_name_form: "snake_case") do |schema| schema.scalar_type "BigInt" do |type| type.mapping type: "long" end @@ -174,7 +174,7 @@ module SchemaElements end it "has the expected placeholder for each built-in scalar type, including the JSON-safe-range-aware `JsonSafeLong` inference" do - results = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) { |schema| } + results = define_schema(schema_element_name_form: "snake_case") { |schema| } built_in_scalars = results.state.scalar_types_by_name.keys scalar_types_by_name = results.runtime_metadata.scalar_types_by_name @@ -200,7 +200,7 @@ module SchemaElements end def grouping_missing_value_placeholder_for(mapping_type, **json_schema_options) - define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |schema| + define_schema(schema_element_name_form: "snake_case") do |schema| schema.scalar_type "CustomScalar" do |type| type.mapping type: mapping_type type.json_schema(**json_schema_options) From e37cb08edf9138e71cfb1eff1f5abf0871e93012 Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Fri, 12 Jun 2026 07:08:53 -0500 Subject: [PATCH 5/5] Skip eager SDL validation on the unresolvable-type wrapper example With `VALIDATE_GRAPHQL_SCHEMAS=1` (as on CI), the eager SDL validation raised the unresolvable-type error during GraphQL schema generation, before JSON schema generation could exercise the wrapped `FieldReference#resolve` nil return the example exists to cover, leaving that branch uncovered. Tagging the example with `:dont_validate_graphql_schema` (the existing opt-out for intentionally invalid schemas) makes the error surface via the JSON schema path in all environments. --- .../schema_definition/indexing/wrappers_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb index 790c9e2fb..8d1ed751a 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/indexing/wrappers_spec.rb @@ -45,7 +45,11 @@ module JSONIngestion::SchemaDefinition }) end - it "raises a clear error (rather than blowing up internally) for a field whose type never resolves" do + # `:dont_validate_graphql_schema` matters here: with `VALIDATE_GRAPHQL_SCHEMAS=1` (as on CI), the + # eager SDL validation would raise this error during GraphQL schema generation, before the JSON + # schema generation path exercises the wrapped `FieldReference#resolve` nil return that this + # example exists to cover. + it "raises a clear error (rather than blowing up internally) for a field whose type never resolves", :dont_validate_graphql_schema do # When a field references a type that is never defined, the wrapped `FieldReference#resolve` # returns `nil`. The schema definition machinery relies on that `nil` to detect the unresolvable # type and surface a helpful error instead of crashing.