From f13692121b10bf024c098f80de4b0d4681ea498f Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Tue, 30 Jun 2026 18:16:22 -0500 Subject: [PATCH 1/2] Make JSON ingestion spec extension opt-in --- .../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 - .../health_check/health_checker_spec.rb | 1 + .../spec/unit/envoy_extension_spec.rb | 2 +- .../spec/support/multiple_version_support.rb | 8 +- .../indexer/record_preparer_spec.rb | 1 + .../schema_definition/factory_extension.rb | 4 +- ...son_ingestion_schema_definition_helpers.rb | 28 ++ .../deprecated_element_spec.rb | 6 +- .../json_schema_with_metadata_spec.rb | 5 +- .../indexing/wrappers_spec.rb | 10 +- .../json_schema_field_metadata_spec.rb | 5 +- .../json_schema_pruner_spec.rb | 4 +- .../schema_definition/json_schema_spec.rb | 10 +- .../scalar_type_extension_spec.rb | 258 ++++++++++-------- .../graphql_extension_spec.rb | 2 +- .../schema_definition/test_support.rb | 2 + .../spec_support/builds_datastore_core.rb | 2 + .../spec_support/builds_indexer.rb | 20 +- .../spec_support/schema_definition_helpers.rb | 47 +--- spec_support/spec_helper.rb | 13 +- 28 files changed, 272 insertions(+), 190 deletions(-) create mode 100644 elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb 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 806440f80..03d04113c 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 c384a1dd0..3450b3bd4 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" @@ -92,6 +93,9 @@ def self.with_both_casing_forms(&block) derived_type_name_formats: derived_type_name_formats, type_name_overrides: {Cursor: "String"}, 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 cba1b2f07..9a22a9f3f 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 59d3339f4..8403b2498 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-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-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-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-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb index 2bff39624..93f2ae09e 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb @@ -90,7 +90,7 @@ def new_scalar_type(name) extended_type.json_schema(**options) end - yield extended_type + yield extended_type if block_given? extended_type.finalize_json_schema_configuration! end end @@ -104,7 +104,7 @@ def new_scalar_indexing_field_type(scalar_type:) def new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) super(schema_kind, name, wrapping_type: wrapping_type, field_factory: field_factory) do |type| extended_type = type.extend(SchemaElements::TypeWithSubfieldsExtension) # : ::ElasticGraph::SchemaDefinition::SchemaElements::TypeWithSubfields & SchemaElements::TypeWithSubfieldsExtension - yield extended_type + yield extended_type if block_given? end end 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/deprecated_element_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb index 13a0c7cdb..b4db4690b 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb @@ -6,16 +6,16 @@ # # frozen_string_literal: true -require "elastic_graph/spec_support/schema_definition_helpers" +require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe DeprecatedElement do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" it "records `deleted_type`, `deleted_field`, and `renamed_from` calls so that schema artifact tooling can consume them" do - state = define_schema(schema_element_name_form: "snake_case", extension_modules: [APIExtension]) do |schema| + state = define_schema(schema_element_name_form: "snake_case") do |schema| schema.deleted_type "OldType" schema.object_type "Widget" do |t| 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 bb3c22a01..2d699583a 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 @@ -7,14 +7,14 @@ # frozen_string_literal: true 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 module 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 +1057,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 0d900395a..95dc5ae4d 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 @@ -11,14 +11,14 @@ 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 module 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 @@ -46,7 +46,11 @@ module 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. 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 cb9c9131b..382bb67b7 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 @@ -7,13 +7,13 @@ # frozen_string_literal: true 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 module 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 +144,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 0f9edbea7..b23acfb8c 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 @@ -8,13 +8,13 @@ require "elastic_graph/constants" 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 module SchemaDefinition RSpec.describe JSONSchemaPruner, :capture_logs do - include_context "SchemaDefinitionHelpers" + include_context "JSONIngestionSchemaDefinitionHelpers" describe ".prune" do subject { described_class.prune(schema) } 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 fed9cd4b0..46819c9c1 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,15 +8,14 @@ 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 module 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"} @@ -114,10 +113,7 @@ module 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| 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 aeabea81d..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 @@ -8,15 +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/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 { @@ -28,145 +29,174 @@ module SchemaElements }.to raise_error Errors::SchemaError, a_string_including("BigInt", "lacks `json_schema`") end - describe "#grouping_missing_value_placeholder" do - it "infers a numeric missing-value placeholder for JSON-safe unsigned_long scalars with custom coercion" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "unsigned_long", - type: "integer", - maximum: JSON_SAFE_LONG_MAX - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + it "extends schema elements created without customization blocks" do + api = build_api + api.enum_type "EmptyEnum" + api.interface_type "EmptyInterface" + direct_type_with_subfields = api.factory.new_type_with_subfields( + :object, + "DirectObject", + wrapping_type: nil, + field_factory: api.factory.method(:new_field) + ) + + # An enum's derived GraphQL types are built from a derived scalar twin, which can only be + # built if `EnumTypeExtension` configured the twin's `json_schema`; otherwise building it + # raises a "lacks `json_schema`" error. + expect { + api.state.enum_types_by_name.fetch("EmptyEnum").derived_graphql_types + }.not_to raise_error - expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) - end + # `json_schema` is only available on types extended with `TypeWithSubfieldsExtension`. + interface_type = api.state.object_types_by_name.fetch("EmptyInterface") + interface_type.json_schema minProperties: 1 + expect(interface_type.json_schema_options).to eq({minProperties: 1}) - it "does not infer a numeric missing-value placeholder for unsigned_long scalars outside the JSON-safe range" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "unsigned_long", - type: "integer", - maximum: JSON_SAFE_LONG_MAX + 1 - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + direct_type_with_subfields.json_schema minProperties: 2 + expect(direct_type_with_subfields.json_schema_options).to eq({minProperties: 2}) - expect(grouping_missing_value_placeholder).to eq(nil) + expect { + build_api.scalar_type "BigInt" + }.to raise_error Errors::SchemaError, a_string_including("BigInt", "lacks `json_schema`") + end + + it "infers a numeric missing-value placeholder for JSON-safe unsigned_long scalars with custom coercion" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "unsigned_long", + type: "integer", + maximum: JSON_SAFE_LONG_MAX + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for JSON-safe unsigned_long scalars with the default coercion adapter (which would not coerce floats back to integers)" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "unsigned_long", - type: "integer", - maximum: JSON_SAFE_LONG_MAX - ) + expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) + end - expect(grouping_missing_value_placeholder).to eq(nil) + it "does not infer a numeric missing-value placeholder for unsigned_long scalars outside the JSON-safe range" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "unsigned_long", + type: "integer", + maximum: JSON_SAFE_LONG_MAX + 1 + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for unsigned_long scalars when no maximum is specified" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("unsigned_long", type: "integer") do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + expect(grouping_missing_value_placeholder).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "does not infer a placeholder for JSON-safe unsigned_long scalars with the default coercion adapter (which would not coerce floats back to integers)" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "unsigned_long", + type: "integer", + maximum: JSON_SAFE_LONG_MAX + ) - it "infers a numeric missing-value placeholder for long scalars exactly at the JSON-safe boundaries with custom coercion" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN, - maximum: JSON_SAFE_LONG_MAX - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + expect(grouping_missing_value_placeholder).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) + it "does not infer a placeholder for unsigned_long scalars when no maximum is specified" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("unsigned_long", type: "integer") do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for JSON-safe long scalars with the default coercion adapter" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN, - maximum: JSON_SAFE_LONG_MAX - ) + expect(grouping_missing_value_placeholder).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(nil) + it "infers a numeric missing-value placeholder for long scalars exactly at the JSON-safe boundaries with custom coercion" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN, + maximum: JSON_SAFE_LONG_MAX + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for long scalars when the minimum is one below the JSON-safe range" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN - 1, - maximum: JSON_SAFE_LONG_MAX - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) + end - expect(grouping_missing_value_placeholder).to eq(nil) + it "does not infer a placeholder for JSON-safe long scalars with the default coercion adapter" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN, + maximum: JSON_SAFE_LONG_MAX + ) + + expect(grouping_missing_value_placeholder).to eq(nil) + end + + it "does not infer a placeholder for long scalars when the minimum is one below the JSON-safe range" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN - 1, + maximum: JSON_SAFE_LONG_MAX + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for long scalars when the maximum is one above the JSON-safe range" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN, - maximum: JSON_SAFE_LONG_MAX + 1 - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + expect(grouping_missing_value_placeholder).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(nil) + it "does not infer a placeholder for long scalars when the maximum is one above the JSON-safe range" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN, + maximum: JSON_SAFE_LONG_MAX + 1 + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for long scalars when only one bound is specified (the other defaults to the LongString range)" do - only_min = grouping_missing_value_placeholder_for("long", type: "integer", minimum: 0) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + expect(grouping_missing_value_placeholder).to eq(nil) + end - only_max = grouping_missing_value_placeholder_for("long", type: "integer", maximum: 1000) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + it "does not infer a placeholder for long scalars when only one bound is specified (the other defaults to the LongString range)" do + only_min = grouping_missing_value_placeholder_for("long", type: "integer", minimum: 0) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - expect(only_min).to eq(nil) - expect(only_max).to eq(nil) + only_max = grouping_missing_value_placeholder_for("long", type: "integer", maximum: 1000) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path end - it "does not infer a placeholder for long scalars when no bounds are specified" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("long", type: "integer") do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + expect(only_min).to eq(nil) + expect(only_max).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(nil) + it "does not infer a placeholder for long scalars when no bounds are specified" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("long", type: "integer") do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path 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| } - built_in_scalars = results.state.scalar_types_by_name.keys - scalar_types_by_name = results.runtime_metadata.scalar_types_by_name + expect(grouping_missing_value_placeholder).to eq(nil) + end - placeholders_by_scalar_type = built_in_scalars.to_h do |scalar_type| - [scalar_type, scalar_types_by_name.fetch(scalar_type).grouping_missing_value_placeholder] - 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| } + built_in_scalars = results.state.scalar_types_by_name.keys + scalar_types_by_name = results.runtime_metadata.scalar_types_by_name - expect(placeholders_by_scalar_type).to eq({ - "Boolean" => nil, - "Cursor" => MISSING_STRING_PLACEHOLDER, - "Date" => nil, - "DateTime" => nil, - "Float" => MISSING_NUMERIC_PLACEHOLDER, - "ID" => MISSING_STRING_PLACEHOLDER, - "Int" => MISSING_NUMERIC_PLACEHOLDER, # GraphQL automatically coerces Int values - "JsonSafeLong" => MISSING_NUMERIC_PLACEHOLDER, # custom coercion adapter coerces floats back to integers - "LocalTime" => nil, - "LongString" => nil, # outside of the JSON safe range. - "String" => MISSING_STRING_PLACEHOLDER, - "TimeZone" => MISSING_STRING_PLACEHOLDER, - "Untyped" => MISSING_STRING_PLACEHOLDER - }) + placeholders_by_scalar_type = built_in_scalars.to_h do |scalar_type| + [scalar_type, scalar_types_by_name.fetch(scalar_type).grouping_missing_value_placeholder] end + + expect(placeholders_by_scalar_type).to eq({ + "Boolean" => nil, + "Cursor" => MISSING_STRING_PLACEHOLDER, + "Date" => nil, + "DateTime" => nil, + "Float" => MISSING_NUMERIC_PLACEHOLDER, + "ID" => MISSING_STRING_PLACEHOLDER, + "Int" => MISSING_NUMERIC_PLACEHOLDER, # GraphQL automatically coerces Int values + "JsonSafeLong" => MISSING_NUMERIC_PLACEHOLDER, # custom coercion adapter coerces floats back to integers + "LocalTime" => nil, + "LongString" => nil, # outside of the JSON safe range. + "String" => MISSING_STRING_PLACEHOLDER, + "TimeZone" => MISSING_STRING_PLACEHOLDER, + "Untyped" => MISSING_STRING_PLACEHOLDER + }) end def grouping_missing_value_placeholder_for(mapping_type, **json_schema_options) @@ -186,6 +216,16 @@ def scalar_coercion_adapter_path # when one worker runs multiple suites. "elastic_graph/spec_support/example_extensions/scalar_coercion_adapter" end + + def build_api + schema_elements = SchemaArtifacts::RuntimeMetadata::SchemaElementNames.new(form: "snake_case") + ::ElasticGraph::SchemaDefinition::API.new( + schema_elements, + true, + extension_modules: [APIExtension], + output: log_device + ) + end end end end 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/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 df5e50e8b..d88fb39e0 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 @@ -30,6 +30,7 @@ def build_datastore_core( clusters: nil, schema_artifacts_directory: nil, schema_artifacts: nil, + schema_definition_extension_modules: [], datastore_backend: nil, reload_schema_artifacts: false, **config_overrides, @@ -61,6 +62,7 @@ def build_datastore_core( derived_type_name_formats: derived_type_name_formats, type_name_overrides: type_name_overrides, enum_value_overrides_by_type: enum_value_overrides_by_type, + 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 d279fd460..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 @@ -8,49 +8,22 @@ 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 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 - ) - 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 - ) + # 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 default_schema_definition_extension_modules - ::ElasticGraph::SpecSupport::DEFAULT_SCHEMA_DEFINITION_EXTENSION_MODULES.dup + def define_schema_with_schema_elements(schema_elements, output: nil, **options, &block) + super(schema_elements, output: output || log_device, **options, &block) end end diff --git a/spec_support/spec_helper.rb b/spec_support/spec_helper.rb index 655e16663..f76e05b0d 100644 --- a/spec_support/spec_helper.rb +++ b/spec_support/spec_helper.rb @@ -356,7 +356,10 @@ def without_vcr end # :nocov: + # `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: {}, @@ -365,9 +368,15 @@ def generate_schema_artifacts( reload_schema_artifacts: false ) require "elastic_graph/schema_definition/test_support" - require "elastic_graph/spec_support/schema_definition_helpers" 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, @@ -375,7 +384,7 @@ def generate_schema_artifacts( derived_type_name_formats: derived_type_name_formats, type_name_overrides: type_name_overrides, enum_value_overrides_by_type: enum_value_overrides_by_type, - extension_modules: ::ElasticGraph::SpecSupport::DEFAULT_SCHEMA_DEFINITION_EXTENSION_MODULES.dup, + extension_modules: extension_modules, reload_schema_artifacts: reload_schema_artifacts, output: output ) do |schema| From 2801ee5bbf0c2dfad63573b1bfdda12ab1380a33 Mon Sep 17 00:00:00 2001 From: myronmarston-toast Date: Tue, 30 Jun 2026 21:22:27 -0700 Subject: [PATCH 2/2] Refine JSON ingestion spec support Keep JSON ingestion schema support opt-in instead of baking it into the indexer helpers. Specs that actually ingest JSON data now use :ingests_json_data, which composes datastore, indexer, and :json_ingestion_schema_definition support. Schema-only specs use :json_ingestion_schema_definition directly. This keeps elasticgraph-indexer tests from assuming JSON as the only ingestion format and leaves room for future non-JSON ingestion paths. The json_ingestion gem applies the shared JSON schema-definition support by convention from its spec helper, so its specs no longer need a separate helper that duplicates APIExtension wiring. Two subtleties matter for keeping the full suite green: - `define_derived_metadata` does not reliably expand a derived tag declared on a `shared_context` into the groups produced when the context is combined with `include_examples` (RSpec raises inside `update_inherited_metadata`; see https://github.com/rspec/rspec/issues/333). So the shared contexts that ingest JSON data list the component tags (`:json_ingestion_schema_definition`, `:uses_datastore`, `:builds_indexer`) literally rather than via `:ingests_json_data`, with a comment referencing the upstream issue so the workaround can be collapsed once it is fixed. Dropping the (redundant) `:capture_logs` tag alone does not avoid the crash, and reverting either shared context to `:ingests_json_data` reintroduces it in the full suite. - The elasticgraph-apollo and elasticgraph-health_check bundles do not include elasticgraph-json_ingestion, and their specs use prebuilt on-disk artifacts, so they stay on `:uses_datastore`/`:builds_indexer` rather than opting into the JSON schema-definition extension they cannot load. Generated with Claude Code --- .../for_index_template_spec.rb | 2 +- .../shared_examples.rb | 2 +- .../rollover_index_template_spec.rb | 9 +- ...elasticgraph_graphql_acceptance_support.rb | 12 +- .../spec/acceptance/schema_evolution_spec.rb | 5 +- .../datastore_query_integration_support.rb | 9 +- .../graphql/datastore_query/misc_spec.rb | 3 +- .../indexed_type_root_fields_resolver_spec.rb | 2 +- .../nested_relationships_source_spec.rb | 2 +- .../resolvers/nested_relationships_spec.rb | 13 +- .../graphql/resolvers/query_source_spec.rb | 2 +- .../elasticgraph-health_check.gemspec | 1 + .../spec/acceptance/health_checker_spec.rb | 2 +- .../health_check/health_checker_spec.rb | 1 - .../spec/unit/envoy_extension_spec.rb | 2 +- .../acceptance/derived_indexing_types_spec.rb | 2 +- .../spec/acceptance/list_fields_spec.rb | 2 +- .../nested_multi_source_indexing_spec.rb | 2 +- .../spec/acceptance/schema_evolution_spec.rb | 5 +- .../top_level_multi_source_indexing_spec.rb | 2 +- .../indexer/datastore_indexing_router_spec.rb | 2 +- .../elastic_graph/indexer/processor_spec.rb | 2 +- .../spec/support/indexing_preparer.rb | 2 +- .../spec/support/multiple_version_support.rb | 10 +- .../indexer/datastore_indexing_router_spec.rb | 2 +- .../indexer/operation/update_spec.rb | 2 +- .../indexer/record_preparer_spec.rb | 9 +- .../schema_definition/factory_extension.rb | 4 +- .../schema_artifact_manager_extension_spec.rb | 3 +- .../spec/spec_helper.rb | 11 + ...son_ingestion_schema_definition_helpers.rb | 28 -- .../deprecated_element_spec.rb | 4 - .../factory_extension_spec.rb | 7 +- .../json_schema_with_metadata_spec.rb | 6 +- .../indexing/wrappers_spec.rb | 3 - .../json_schema_field_metadata_spec.rb | 6 +- .../json_schema_pruner_spec.rb | 3 - .../schema_definition/json_schema_spec.rb | 2 - .../scalar_type_extension_spec.rb | 257 ++++++++---------- .../graphql_extension_spec.rb | 2 +- .../spec_support/builds_indexer.rb | 20 +- .../spec_support/ingests_json_data.rb | 11 + .../json_ingestion_schema_definition.rb | 48 ++++ .../spec_support/uses_datastore.rb | 1 - spec_support/spec_helper.rb | 8 + 45 files changed, 245 insertions(+), 288 deletions(-) delete mode 100644 elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb create mode 100644 spec_support/lib/elastic_graph/spec_support/ingests_json_data.rb create mode 100644 spec_support/lib/elastic_graph/spec_support/json_ingestion_schema_definition.rb diff --git a/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/for_index_template_spec.rb b/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/for_index_template_spec.rb index 2af2dd7cb..ef04c9cd9 100644 --- a/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/for_index_template_spec.rb +++ b/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/for_index_template_spec.rb @@ -138,7 +138,7 @@ def fetch_artifact_configuration(schema_artifacts, index_def_name) end end - context "when a concrete index has been derived from the template", :factories do + context "when a concrete index has been derived from the template", :factories, :ingests_json_data do include ConcreteIndexAdapter # our schema in the tests here is more limited than the main widget schema, so select only some fields. diff --git a/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/shared_examples.rb b/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/shared_examples.rb index a68cc4d7a..0ee07c00a 100644 --- a/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/shared_examples.rb +++ b/elasticgraph-admin/spec/integration/elastic_graph/admin/index_definition_configurator/shared_examples.rb @@ -38,7 +38,7 @@ def simulate_presence_of_extra_setting(admin, index_definition_name, name, value end end - RSpec.shared_examples_for IndexDefinitionConfigurator, :uses_datastore, :builds_indexer do + RSpec.shared_examples_for IndexDefinitionConfigurator, :uses_datastore do let(:output_io) { StringIO.new } let(:clock) { class_double(::Time, now: ::Time.utc(2024, 3, 20, 12, 0, 0)) } let(:mapping_removal_note_snippet) { "extra fields listed here will not actually get removed" } 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 03d04113c..f7d05161b 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,7 +8,6 @@ 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,13 +15,7 @@ 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 - + RSpec.describe RolloverIndexTemplate, :ingests_json_data do # 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 3450b3bd4..fb68a6f00 100644 --- a/elasticgraph-graphql/spec/acceptance/elasticgraph_graphql_acceptance_support.rb +++ b/elasticgraph-graphql/spec/acceptance/elasticgraph_graphql_acceptance_support.rb @@ -7,7 +7,6 @@ # 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" @@ -20,7 +19,13 @@ class << self self.camel_case_cluster_configured = false - RSpec.shared_context "ElasticGraph GraphQL acceptance support", :factories, :uses_datastore, :capture_logs, :builds_indexer, :builds_admin do + # We list the `:ingests_json_data` component tags (`:json_ingestion_schema_definition`, + # `:uses_datastore`, `:builds_indexer`) individually rather than using `:ingests_json_data` + # itself. Applying the derived `:ingests_json_data` tag to a `shared_context` and then combining + # it with `include_examples` crashes RSpec (`undefined method 'key?' for nil`), whereas literal + # tags are inherited correctly. Once https://github.com/rspec/rspec/issues/333 is fixed we can + # collapse these back to `:ingests_json_data`. + RSpec.shared_context "ElasticGraph GraphQL acceptance support", :factories, :json_ingestion_schema_definition, :uses_datastore, :builds_indexer, :capture_logs, :builds_admin do include GraphQLSupport include PreventSearchesFromUsingWriteRequests @@ -93,9 +98,6 @@ def self.with_both_casing_forms(&block) derived_type_name_formats: derived_type_name_formats, type_name_overrides: {Cursor: "String"}, 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/acceptance/schema_evolution_spec.rb b/elasticgraph-graphql/spec/acceptance/schema_evolution_spec.rb index 05d18f5ab..122dc8a44 100644 --- a/elasticgraph-graphql/spec/acceptance/schema_evolution_spec.rb +++ b/elasticgraph-graphql/spec/acceptance/schema_evolution_spec.rb @@ -8,12 +8,11 @@ require "elastic_graph/graphql" require "elastic_graph/indexer" -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/schema_definition/rake_tasks" require "support/graphql" module ElasticGraph - RSpec.describe "Querying an evolving schema", :uses_datastore, :factories, :capture_logs, :in_temp_dir, :rake_task do + RSpec.describe "Querying an evolving schema", :ingests_json_data, :factories, :capture_logs, :in_temp_dir, :rake_task do include GraphQLSupport let(:path_to_schema) { "config/schema.rb" } @@ -66,7 +65,7 @@ def dump_schema_artifacts(json_schema_version:, team_extras: "") run_rake "schema_artifacts:dump" do |output| SchemaDefinition::RakeTasks.new( schema_element_name_form: :snake_case, - extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], + extension_modules: json_ingestion_schema_definition_extension_modules, index_document_sizes: true, path_to_schema: path_to_schema, schema_artifacts_directory: "config/schema/artifacts", diff --git a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/datastore_query_integration_support.rb b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/datastore_query_integration_support.rb index 47c8880f0..1ead5c1af 100644 --- a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/datastore_query_integration_support.rb +++ b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/datastore_query/datastore_query_integration_support.rb @@ -18,7 +18,14 @@ class GraphQL # triggered a `null_pointer_exception` in one shard, which caused the datastore to # return a partial response that our router handled by logging a warning. The # `:capture_logs` here caused the tests to fail until we fixed that issue. - RSpec.shared_context "DatastoreQueryIntegrationSupport", :uses_datastore, :factories, :capture_logs do + # + # We list the `:ingests_json_data` component tags (`:json_ingestion_schema_definition`, + # `:uses_datastore`, `:builds_indexer`) individually rather than using `:ingests_json_data` + # itself. Applying the derived `:ingests_json_data` tag to a `shared_context` and then combining + # it with `include_examples` crashes RSpec (`undefined method 'key?' for nil`), whereas literal + # tags are inherited correctly. Once https://github.com/rspec/rspec/issues/333 is fixed we can + # collapse these back to `:ingests_json_data`. + RSpec.shared_context "DatastoreQueryIntegrationSupport", :json_ingestion_schema_definition, :uses_datastore, :builds_indexer, :factories, :capture_logs do include AggregationsHelpers include SortSupport 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 9a22a9f3f..cba1b2f07 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,7 +9,6 @@ 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 @@ -86,7 +85,7 @@ def msearch(body:, **args, &block) context "when the indexed type has nested `default_sort_fields`" do let(:graphql) do - build_graphql(schema_definition_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| + build_graphql(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/indexed_type_root_fields_resolver_spec.rb b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/indexed_type_root_fields_resolver_spec.rb index 4842514aa..d6c68cd24 100644 --- a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/indexed_type_root_fields_resolver_spec.rb +++ b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/indexed_type_root_fields_resolver_spec.rb @@ -11,7 +11,7 @@ module ElasticGraph class GraphQL module Resolvers - RSpec.describe IndexedTypeRootFieldsResolver, :factories, :uses_datastore, :resolver do + RSpec.describe IndexedTypeRootFieldsResolver, :factories, :ingests_json_data, :resolver do context "when the field being resolved is a relay connection field" do let(:graphql) { build_graphql } diff --git a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_source_spec.rb b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_source_spec.rb index 08ca5c005..8687e7470 100644 --- a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_source_spec.rb +++ b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/nested_relationships_source_spec.rb @@ -15,7 +15,7 @@ module ElasticGraph class GraphQL module Resolvers - RSpec.describe NestedRelationshipsSource, :factories, :uses_datastore, :capture_logs do + RSpec.describe NestedRelationshipsSource, :factories, :ingests_json_data, :capture_logs do include AggregationsHelpers let(:merged_queries_message_type) { "NestedRelationshipsMergedQueries" } 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 2657de370..618b55a08 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,17 +7,16 @@ # frozen_string_literal: true require "elastic_graph/graphql/resolvers/nested_relationships" -require "elastic_graph/json_ingestion/schema_definition/api_extension" module ElasticGraph class GraphQL module Resolvers - RSpec.describe NestedRelationships, :factories, :uses_datastore, :capture_logs, :resolver do + RSpec.describe NestedRelationships, :factories, :ingests_json_data, :capture_logs, :resolver do # :expect_search_routing because the relation we use here uses an outbaund foreign key, which # 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_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| + build_graphql(schema_definition: lambda do |schema| schema.object_type "Component" do |t| t.field "id", "ID!" t.field "name", "String!" @@ -63,7 +62,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_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| + build_graphql(schema_definition: lambda do |schema| schema.object_type "Money" do |t| t.field "currency", "String!" t.field "amount_cents", "Int" @@ -242,7 +241,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_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| + build_graphql(schema_definition: lambda do |schema| schema.object_type "ElectricalPart" do |t| t.field "id", "ID!" t.field "name", "String!" @@ -382,7 +381,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_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| + build_graphql(schema_definition: lambda do |schema| schema.object_type "Component" do |t| t.field "id", "ID!" t.field "name", "String!" @@ -574,7 +573,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_extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], schema_definition: lambda do |schema| + build_graphql(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/integration/elastic_graph/graphql/resolvers/query_source_spec.rb b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/query_source_spec.rb index 06eac069a..450a0446e 100644 --- a/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/query_source_spec.rb +++ b/elasticgraph-graphql/spec/integration/elastic_graph/graphql/resolvers/query_source_spec.rb @@ -13,7 +13,7 @@ module ElasticGraph class GraphQL module Resolvers - RSpec.describe QuerySource, :factories, :uses_datastore do + RSpec.describe QuerySource, :factories, :ingests_json_data do let(:graphql) { build_graphql } it "batches up multiple queries, sending an msearch request containing them all to the datastore" do diff --git a/elasticgraph-health_check/elasticgraph-health_check.gemspec b/elasticgraph-health_check/elasticgraph-health_check.gemspec index 028b7efb7..02f188c52 100644 --- a/elasticgraph-health_check/elasticgraph-health_check.gemspec +++ b/elasticgraph-health_check/elasticgraph-health_check.gemspec @@ -48,6 +48,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "elasticgraph-admin", ElasticGraph::VERSION spec.add_development_dependency "elasticgraph-elasticsearch", ElasticGraph::VERSION spec.add_development_dependency "elasticgraph-indexer", ElasticGraph::VERSION + spec.add_development_dependency "elasticgraph-json_ingestion", ElasticGraph::VERSION spec.add_development_dependency "elasticgraph-opensearch", ElasticGraph::VERSION spec.add_development_dependency "elasticgraph-schema_definition", ElasticGraph::VERSION end diff --git a/elasticgraph-health_check/spec/acceptance/health_checker_spec.rb b/elasticgraph-health_check/spec/acceptance/health_checker_spec.rb index 2f412b0b9..0bb7fb8af 100644 --- a/elasticgraph-health_check/spec/acceptance/health_checker_spec.rb +++ b/elasticgraph-health_check/spec/acceptance/health_checker_spec.rb @@ -13,7 +13,7 @@ module ElasticGraph module HealthCheck - RSpec.describe "HealthChecker", :uses_datastore, :factories, :builds_graphql, :capture_logs do + RSpec.describe "HealthChecker", :ingests_json_data, :factories, :builds_graphql, :capture_logs do let(:now) { ::Time.iso8601("2022-02-14T12:30:00Z") } let(:graphql) { build_graphql } let(:health_checker) { HealthChecker.build_from(graphql) } 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 806dc7357..f1ee584da 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,7 +467,6 @@ 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 b4f1c4782..acd06b7b7 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(extension_modules: []) do |schema| + schema_artifacts = generate_schema_artifacts 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-indexer/spec/acceptance/derived_indexing_types_spec.rb b/elasticgraph-indexer/spec/acceptance/derived_indexing_types_spec.rb index 1df07c316..8a7202e4a 100644 --- a/elasticgraph-indexer/spec/acceptance/derived_indexing_types_spec.rb +++ b/elasticgraph-indexer/spec/acceptance/derived_indexing_types_spec.rb @@ -7,7 +7,7 @@ # frozen_string_literal: true module ElasticGraph - RSpec.describe "A derived indexing type", :uses_datastore, :factories, :capture_logs do + RSpec.describe "A derived indexing type", :ingests_json_data, :factories, :capture_logs do let(:indexer) { build_indexer } it "maintains derived fields, handling nested source and destination fields as needed" do diff --git a/elasticgraph-indexer/spec/acceptance/list_fields_spec.rb b/elasticgraph-indexer/spec/acceptance/list_fields_spec.rb index 1111266b8..f38103d94 100644 --- a/elasticgraph-indexer/spec/acceptance/list_fields_spec.rb +++ b/elasticgraph-indexer/spec/acceptance/list_fields_spec.rb @@ -7,7 +7,7 @@ # frozen_string_literal: true module ElasticGraph - RSpec.describe "Indexing into list fields", :uses_datastore, :factories, :capture_logs do + RSpec.describe "Indexing into list fields", :ingests_json_data, :factories, :capture_logs do let(:indexer) { build_indexer } it "indexes counts of any list fields so we can later use it for filtering" do diff --git a/elasticgraph-indexer/spec/acceptance/nested_multi_source_indexing_spec.rb b/elasticgraph-indexer/spec/acceptance/nested_multi_source_indexing_spec.rb index 4a585817c..cfe2d99e8 100644 --- a/elasticgraph-indexer/spec/acceptance/nested_multi_source_indexing_spec.rb +++ b/elasticgraph-indexer/spec/acceptance/nested_multi_source_indexing_spec.rb @@ -7,7 +7,7 @@ # frozen_string_literal: true module ElasticGraph - RSpec.describe "Nested multi-source indexing", :uses_datastore, :factories, :capture_logs do + RSpec.describe "Nested multi-source indexing", :ingests_json_data, :factories, :capture_logs do let(:indexer) { build_indexer } let(:league) { "NBA" } let(:formed_on) { "2019-04-23" } diff --git a/elasticgraph-indexer/spec/acceptance/schema_evolution_spec.rb b/elasticgraph-indexer/spec/acceptance/schema_evolution_spec.rb index baa04ec86..314dc4eea 100644 --- a/elasticgraph-indexer/spec/acceptance/schema_evolution_spec.rb +++ b/elasticgraph-indexer/spec/acceptance/schema_evolution_spec.rb @@ -7,11 +7,10 @@ # frozen_string_literal: true require "elastic_graph/indexer" -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/schema_definition/rake_tasks" module ElasticGraph - RSpec.describe "Indexing schema evolution", :uses_datastore, :factories, :capture_logs, :in_temp_dir, :rake_task do + RSpec.describe "Indexing schema evolution", :ingests_json_data, :factories, :capture_logs, :in_temp_dir, :rake_task do let(:path_to_schema) { "config/schema.rb" } before do @@ -402,7 +401,7 @@ def dump_artifacts run_rake "schema_artifacts:dump" do |output| SchemaDefinition::RakeTasks.new( schema_element_name_form: :snake_case, - extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], + extension_modules: json_ingestion_schema_definition_extension_modules, index_document_sizes: true, path_to_schema: path_to_schema, schema_artifacts_directory: "config/schema/artifacts", diff --git a/elasticgraph-indexer/spec/acceptance/top_level_multi_source_indexing_spec.rb b/elasticgraph-indexer/spec/acceptance/top_level_multi_source_indexing_spec.rb index 27b573cbd..4a235f3e7 100644 --- a/elasticgraph-indexer/spec/acceptance/top_level_multi_source_indexing_spec.rb +++ b/elasticgraph-indexer/spec/acceptance/top_level_multi_source_indexing_spec.rb @@ -7,7 +7,7 @@ # frozen_string_literal: true module ElasticGraph - RSpec.describe "Top-level multi-source indexing", :uses_datastore, :factories, :capture_logs do + RSpec.describe "Top-level multi-source indexing", :ingests_json_data, :factories, :capture_logs do let(:indexer) { build_indexer } it "ingests data from multiple source types into a single document, regardless of the ingestion order" do diff --git a/elasticgraph-indexer/spec/integration/elastic_graph/indexer/datastore_indexing_router_spec.rb b/elasticgraph-indexer/spec/integration/elastic_graph/indexer/datastore_indexing_router_spec.rb index cb69c9996..072fd0648 100644 --- a/elasticgraph-indexer/spec/integration/elastic_graph/indexer/datastore_indexing_router_spec.rb +++ b/elasticgraph-indexer/spec/integration/elastic_graph/indexer/datastore_indexing_router_spec.rb @@ -11,7 +11,7 @@ module ElasticGraph class Indexer - RSpec.describe DatastoreIndexingRouter, :uses_datastore, :capture_logs do + RSpec.describe DatastoreIndexingRouter, :ingests_json_data, :capture_logs do describe "#source_event_versions_in_index", :factories do let(:indexer) { build_indexer } let(:router) { indexer.datastore_router } diff --git a/elasticgraph-indexer/spec/integration/elastic_graph/indexer/processor_spec.rb b/elasticgraph-indexer/spec/integration/elastic_graph/indexer/processor_spec.rb index b9d118c34..4587f5e5b 100644 --- a/elasticgraph-indexer/spec/integration/elastic_graph/indexer/processor_spec.rb +++ b/elasticgraph-indexer/spec/integration/elastic_graph/indexer/processor_spec.rb @@ -10,7 +10,7 @@ module ElasticGraph class Indexer - RSpec.describe Processor, :uses_datastore, :factories, :capture_logs do + RSpec.describe Processor, :ingests_json_data, :factories, :capture_logs do let(:indexer) { build_indexer } context "process non-rollover upsert events" do diff --git a/elasticgraph-indexer/spec/support/indexing_preparer.rb b/elasticgraph-indexer/spec/support/indexing_preparer.rb index 0f5730a54..df85ca869 100644 --- a/elasticgraph-indexer/spec/support/indexing_preparer.rb +++ b/elasticgraph-indexer/spec/support/indexing_preparer.rb @@ -12,7 +12,7 @@ # For example, to see your indexing preparer being used, this requires that it is registered # on your scalar type correctly, whereas if this directly called your indexing preparer # it wouldn't require it to be correctly registered. -RSpec.shared_context "indexing preparer support" do |scalar_type| +RSpec.shared_context "indexing preparer support", :json_ingestion_schema_definition do |scalar_type| before(:context) do @record_preparer = build_indexer(clients_by_name: {}, schema_definition: lambda do |schema| schema.object_type "MyType" do |t| diff --git a/elasticgraph-indexer/spec/support/multiple_version_support.rb b/elasticgraph-indexer/spec/support/multiple_version_support.rb index 9831a2174..e3b044aab 100644 --- a/elasticgraph-indexer/spec/support/multiple_version_support.rb +++ b/elasticgraph-indexer/spec/support/multiple_version_support.rb @@ -6,22 +6,16 @@ # # frozen_string_literal: true -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/spec_support/schema_definition_helpers" module ElasticGraph class Indexer - ::RSpec.shared_context "MultipleVersionSupport" do + ::RSpec.shared_context "MultipleVersionSupport", :json_ingestion_schema_definition do include_context "SchemaDefinitionHelpers" 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, - extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], - &prior_def - ) + results = define_schema(schema_element_name_form: :snake_case, json_schema_version: json_schema_version, &prior_def) [json_schema_version, results] end diff --git a/elasticgraph-indexer/spec/unit/elastic_graph/indexer/datastore_indexing_router_spec.rb b/elasticgraph-indexer/spec/unit/elastic_graph/indexer/datastore_indexing_router_spec.rb index 1baad1cd5..7702a195a 100644 --- a/elasticgraph-indexer/spec/unit/elastic_graph/indexer/datastore_indexing_router_spec.rb +++ b/elasticgraph-indexer/spec/unit/elastic_graph/indexer/datastore_indexing_router_spec.rb @@ -15,7 +15,7 @@ module ElasticGraph class Indexer - RSpec.describe DatastoreIndexingRouter, :capture_logs do + RSpec.describe DatastoreIndexingRouter, :json_ingestion_schema_definition, :capture_logs do include SpecSupport::BuildsIndexerOperation let(:main_datastore_client) { instance_spy(Elasticsearch::Client, cluster_name: "main") } diff --git a/elasticgraph-indexer/spec/unit/elastic_graph/indexer/operation/update_spec.rb b/elasticgraph-indexer/spec/unit/elastic_graph/indexer/operation/update_spec.rb index 7e56d9e25..686611ceb 100644 --- a/elasticgraph-indexer/spec/unit/elastic_graph/indexer/operation/update_spec.rb +++ b/elasticgraph-indexer/spec/unit/elastic_graph/indexer/operation/update_spec.rb @@ -15,7 +15,7 @@ module ElasticGraph class Indexer module Operation - RSpec.describe Update do + RSpec.describe Update, :json_ingestion_schema_definition do include SchemaArtifacts::RuntimeMetadata::RuntimeMetadataSupport let(:indexer) { build_indexer } 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 bfea42d70..c179e10eb 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,13 +7,12 @@ # 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" module ElasticGraph class Indexer - RSpec.describe RecordPreparer::Factory do + RSpec.describe RecordPreparer::Factory, :json_ingestion_schema_definition do include_context "MultipleVersionSupport" let(:factory_with_multiple_versions) do @@ -55,7 +54,7 @@ class Indexer end end - RSpec.describe RecordPreparer do + RSpec.describe RecordPreparer, :json_ingestion_schema_definition do describe "#prepare_for_index" do it "tolerates a `nil` value where an object would usually be" do preparer = build_preparer do |s| @@ -495,7 +494,7 @@ class Indexer end end - context "when working with events for an old JSON schema version" do + context "when working with events for an old JSON schema version", :json_ingestion_schema_definition do include_context "SchemaDefinitionHelpers" it "handles events for old versions before a field was deleted" do @@ -579,7 +578,7 @@ def build_preparer_for_old_json_schema_version(v1_def:, v2_def:) def define_schema(&schema_definition) super( schema_element_name_form: "snake_case", - extension_modules: [JSONIngestion::SchemaDefinition::APIExtension], + extension_modules: json_ingestion_schema_definition_extension_modules, &schema_definition ) end diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb index 93f2ae09e..2bff39624 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/factory_extension.rb @@ -90,7 +90,7 @@ def new_scalar_type(name) extended_type.json_schema(**options) end - yield extended_type if block_given? + yield extended_type extended_type.finalize_json_schema_configuration! end end @@ -104,7 +104,7 @@ def new_scalar_indexing_field_type(scalar_type:) def new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) super(schema_kind, name, wrapping_type: wrapping_type, field_factory: field_factory) do |type| extended_type = type.extend(SchemaElements::TypeWithSubfieldsExtension) # : ::ElasticGraph::SchemaDefinition::SchemaElements::TypeWithSubfields & SchemaElements::TypeWithSubfieldsExtension - yield extended_type if block_given? + yield extended_type end end diff --git a/elasticgraph-json_ingestion/spec/integration/elastic_graph/json_ingestion/schema_definition/schema_artifact_manager_extension_spec.rb b/elasticgraph-json_ingestion/spec/integration/elastic_graph/json_ingestion/schema_definition/schema_artifact_manager_extension_spec.rb index 079f348b4..3bb973ef0 100644 --- a/elasticgraph-json_ingestion/spec/integration/elastic_graph/json_ingestion/schema_definition/schema_artifact_manager_extension_spec.rb +++ b/elasticgraph-json_ingestion/spec/integration/elastic_graph/json_ingestion/schema_definition/schema_artifact_manager_extension_spec.rb @@ -7,7 +7,6 @@ # frozen_string_literal: true require "elastic_graph/constants" -require "elastic_graph/json_ingestion/schema_definition/api_extension" require "elastic_graph/schema_definition/rake_tasks" require "yaml" @@ -620,7 +619,7 @@ def as_active_instance index_document_sizes: true, path_to_schema: "schema.rb", schema_artifacts_directory: "config/schema/artifacts", - extension_modules: [APIExtension, extension_module], + extension_modules: json_ingestion_schema_definition_extension_modules([extension_module]), output: output ) end diff --git a/elasticgraph-json_ingestion/spec/spec_helper.rb b/elasticgraph-json_ingestion/spec/spec_helper.rb index 138d7e9ca..1bbf10366 100644 --- a/elasticgraph-json_ingestion/spec/spec_helper.rb +++ b/elasticgraph-json_ingestion/spec/spec_helper.rb @@ -8,3 +8,14 @@ # This file contains RSpec configuration for `elasticgraph-json_ingestion`. # It is loaded by the shared spec helper at `spec_support/spec_helper.rb`. + +require "elastic_graph/json_ingestion/schema_definition/api_extension" +require "elastic_graph/spec_support/schema_definition_helpers" + +RSpec.configure do |config| + config.define_derived_metadata(absolute_file_path: %r{/elasticgraph-json_ingestion/}) do |meta| + meta[:json_ingestion_schema_definition] = true + end + + config.include_context "SchemaDefinitionHelpers", :json_ingestion_schema_definition +end 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 deleted file mode 100644 index 1cae87905..000000000 --- a/elasticgraph-json_ingestion/spec/support/json_ingestion_schema_definition_helpers.rb +++ /dev/null @@ -1,28 +0,0 @@ -# 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/deprecated_element_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb index b4db4690b..425bb4667 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb @@ -6,14 +6,10 @@ # # frozen_string_literal: true -require "support/json_ingestion_schema_definition_helpers" - module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe DeprecatedElement do - include_context "JSONIngestionSchemaDefinitionHelpers" - it "records `deleted_type`, `deleted_field`, and `renamed_from` calls so that schema artifact tooling can consume them" do state = define_schema(schema_element_name_form: "snake_case") do |schema| schema.deleted_type "OldType" diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/factory_extension_spec.rb b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/factory_extension_spec.rb index 01b554acd..c7226e845 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/factory_extension_spec.rb +++ b/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/factory_extension_spec.rb @@ -6,15 +6,10 @@ # # frozen_string_literal: true -require "elastic_graph/json_ingestion/schema_definition/api_extension" -require "elastic_graph/spec_support/schema_definition_helpers" - module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe FactoryExtension do - include_context "SchemaDefinitionHelpers" - it "extends enum types created without customization blocks" do api = build_api api.enum_type "EmptyEnum" @@ -42,7 +37,7 @@ def build_api ::ElasticGraph::SchemaDefinition::API.new( schema_elements, true, - extension_modules: [APIExtension], + extension_modules: json_ingestion_schema_definition_extension_modules, output: log_device ) 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 2d699583a..e1f824426 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 @@ -7,15 +7,12 @@ # frozen_string_literal: true require "elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata" -require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion module SchemaDefinition module Indexing ::RSpec.describe JSONSchemaWithMetadata do - 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| schema.json_schema_version 1 @@ -1054,9 +1051,10 @@ def metadata_for(json_schema, type, field) json_schema.dig("$defs", type, "properties", field, "ElasticGraph") end - def define_schema(&schema_definition) + def define_schema(**options, &schema_definition) super( schema_element_name_form: "snake_case", + **options, &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 95dc5ae4d..b2ba8404e 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 @@ -11,15 +11,12 @@ 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 "support/json_ingestion_schema_definition_helpers" require "support/json_schema_matcher" module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe "JSON schema indexing wrappers" do - 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 # resolves) through the public schema-definition API. 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 382bb67b7..260135a11 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 @@ -7,14 +7,11 @@ # frozen_string_literal: true require "elastic_graph/json_ingestion/schema_definition/indexing/json_schema_field_metadata" -require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe "JSON schema field metadata generation" do - include_context "JSONIngestionSchemaDefinitionHelpers" - it "generates no field metadata for built-in scalar and enum types" do metadata_by_type_and_field_name = dump_metadata @@ -141,9 +138,10 @@ def dump_metadata(&schema_definition) define_schema(&schema_definition).json_schema_field_metadata_by_type_and_field_name end - def define_schema(&schema_definition) + def define_schema(**options, &schema_definition) super( schema_element_name_form: "snake_case", + **options, &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 b23acfb8c..dd8ff56b5 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 @@ -8,14 +8,11 @@ require "elastic_graph/constants" require "elastic_graph/json_ingestion/schema_definition/json_schema_pruner" -require "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe JSONSchemaPruner, :capture_logs do - include_context "JSONIngestionSchemaDefinitionHelpers" - describe ".prune" do subject { described_class.prune(schema) } 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 46819c9c1..00ca08d2b 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,12 @@ require "elastic_graph/constants" require "elastic_graph/errors" -require "support/json_ingestion_schema_definition_helpers" require "support/json_schema_matcher" module ElasticGraph module JSONIngestion module SchemaDefinition RSpec.describe "JSON schema generation" do - 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"} 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 8497479c6..0360610dd 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,17 +8,13 @@ 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 "support/json_ingestion_schema_definition_helpers" module ElasticGraph module JSONIngestion module SchemaDefinition module SchemaElements RSpec.describe ScalarTypeExtension do - include_context "JSONIngestionSchemaDefinitionHelpers" - it "requires custom scalar types to declare their JSON schema representation" do expect { define_schema(schema_element_name_form: "snake_case") do |schema| @@ -29,174 +25,145 @@ module SchemaElements }.to raise_error Errors::SchemaError, a_string_including("BigInt", "lacks `json_schema`") end - it "extends schema elements created without customization blocks" do - api = build_api - api.enum_type "EmptyEnum" - api.interface_type "EmptyInterface" - direct_type_with_subfields = api.factory.new_type_with_subfields( - :object, - "DirectObject", - wrapping_type: nil, - field_factory: api.factory.method(:new_field) - ) - - # An enum's derived GraphQL types are built from a derived scalar twin, which can only be - # built if `EnumTypeExtension` configured the twin's `json_schema`; otherwise building it - # raises a "lacks `json_schema`" error. - expect { - api.state.enum_types_by_name.fetch("EmptyEnum").derived_graphql_types - }.not_to raise_error - - # `json_schema` is only available on types extended with `TypeWithSubfieldsExtension`. - interface_type = api.state.object_types_by_name.fetch("EmptyInterface") - interface_type.json_schema minProperties: 1 - expect(interface_type.json_schema_options).to eq({minProperties: 1}) - - direct_type_with_subfields.json_schema minProperties: 2 - expect(direct_type_with_subfields.json_schema_options).to eq({minProperties: 2}) - - expect { - build_api.scalar_type "BigInt" - }.to raise_error Errors::SchemaError, a_string_including("BigInt", "lacks `json_schema`") - end + describe "#grouping_missing_value_placeholder" do + it "infers a numeric missing-value placeholder for JSON-safe unsigned_long scalars with custom coercion" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "unsigned_long", + type: "integer", + maximum: JSON_SAFE_LONG_MAX + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "infers a numeric missing-value placeholder for JSON-safe unsigned_long scalars with custom coercion" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "unsigned_long", - type: "integer", - maximum: JSON_SAFE_LONG_MAX - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) end - expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) - end + it "does not infer a numeric missing-value placeholder for unsigned_long scalars outside the JSON-safe range" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "unsigned_long", + type: "integer", + maximum: JSON_SAFE_LONG_MAX + 1 + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "does not infer a numeric missing-value placeholder for unsigned_long scalars outside the JSON-safe range" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "unsigned_long", - type: "integer", - maximum: JSON_SAFE_LONG_MAX + 1 - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(nil) end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "does not infer a placeholder for JSON-safe unsigned_long scalars with the default coercion adapter (which would not coerce floats back to integers)" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "unsigned_long", + type: "integer", + maximum: JSON_SAFE_LONG_MAX + ) - it "does not infer a placeholder for JSON-safe unsigned_long scalars with the default coercion adapter (which would not coerce floats back to integers)" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "unsigned_long", - type: "integer", - maximum: JSON_SAFE_LONG_MAX - ) + expect(grouping_missing_value_placeholder).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "does not infer a placeholder for unsigned_long scalars when no maximum is specified" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("unsigned_long", type: "integer") do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "does not infer a placeholder for unsigned_long scalars when no maximum is specified" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("unsigned_long", type: "integer") do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(nil) end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "infers a numeric missing-value placeholder for long scalars exactly at the JSON-safe boundaries with custom coercion" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN, + maximum: JSON_SAFE_LONG_MAX + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "infers a numeric missing-value placeholder for long scalars exactly at the JSON-safe boundaries with custom coercion" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN, - maximum: JSON_SAFE_LONG_MAX - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) end - expect(grouping_missing_value_placeholder).to eq(MISSING_NUMERIC_PLACEHOLDER) - end + it "does not infer a placeholder for JSON-safe long scalars with the default coercion adapter" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN, + maximum: JSON_SAFE_LONG_MAX + ) - it "does not infer a placeholder for JSON-safe long scalars with the default coercion adapter" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN, - maximum: JSON_SAFE_LONG_MAX - ) + expect(grouping_missing_value_placeholder).to eq(nil) + end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "does not infer a placeholder for long scalars when the minimum is one below the JSON-safe range" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN - 1, + maximum: JSON_SAFE_LONG_MAX + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "does not infer a placeholder for long scalars when the minimum is one below the JSON-safe range" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN - 1, - maximum: JSON_SAFE_LONG_MAX - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(nil) end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "does not infer a placeholder for long scalars when the maximum is one above the JSON-safe range" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( + "long", + type: "integer", + minimum: JSON_SAFE_LONG_MIN, + maximum: JSON_SAFE_LONG_MAX + 1 + ) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "does not infer a placeholder for long scalars when the maximum is one above the JSON-safe range" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for( - "long", - type: "integer", - minimum: JSON_SAFE_LONG_MIN, - maximum: JSON_SAFE_LONG_MAX + 1 - ) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(nil) end - expect(grouping_missing_value_placeholder).to eq(nil) - end + it "does not infer a placeholder for long scalars when only one bound is specified (the other defaults to the LongString range)" do + only_min = grouping_missing_value_placeholder_for("long", type: "integer", minimum: 0) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "does not infer a placeholder for long scalars when only one bound is specified (the other defaults to the LongString range)" do - only_min = grouping_missing_value_placeholder_for("long", type: "integer", minimum: 0) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path - end + only_max = grouping_missing_value_placeholder_for("long", type: "integer", maximum: 1000) do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - only_max = grouping_missing_value_placeholder_for("long", type: "integer", maximum: 1000) do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(only_min).to eq(nil) + expect(only_max).to eq(nil) end - expect(only_min).to eq(nil) - expect(only_max).to eq(nil) - end + it "does not infer a placeholder for long scalars when no bounds are specified" do + grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("long", type: "integer") do |type| + type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + end - it "does not infer a placeholder for long scalars when no bounds are specified" do - grouping_missing_value_placeholder = grouping_missing_value_placeholder_for("long", type: "integer") do |type| - type.coerce_with "ExampleScalarCoercionAdapter", defined_at: scalar_coercion_adapter_path + expect(grouping_missing_value_placeholder).to eq(nil) end - expect(grouping_missing_value_placeholder).to eq(nil) - 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| } + built_in_scalars = results.state.scalar_types_by_name.keys + scalar_types_by_name = results.runtime_metadata.scalar_types_by_name - 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| } - built_in_scalars = results.state.scalar_types_by_name.keys - scalar_types_by_name = results.runtime_metadata.scalar_types_by_name + placeholders_by_scalar_type = built_in_scalars.to_h do |scalar_type| + [scalar_type, scalar_types_by_name.fetch(scalar_type).grouping_missing_value_placeholder] + end - placeholders_by_scalar_type = built_in_scalars.to_h do |scalar_type| - [scalar_type, scalar_types_by_name.fetch(scalar_type).grouping_missing_value_placeholder] + expect(placeholders_by_scalar_type).to eq({ + "Boolean" => nil, + "Cursor" => MISSING_STRING_PLACEHOLDER, + "Date" => nil, + "DateTime" => nil, + "Float" => MISSING_NUMERIC_PLACEHOLDER, + "ID" => MISSING_STRING_PLACEHOLDER, + "Int" => MISSING_NUMERIC_PLACEHOLDER, # GraphQL automatically coerces Int values + "JsonSafeLong" => MISSING_NUMERIC_PLACEHOLDER, # custom coercion adapter coerces floats back to integers + "LocalTime" => nil, + "LongString" => nil, # outside of the JSON safe range. + "String" => MISSING_STRING_PLACEHOLDER, + "TimeZone" => MISSING_STRING_PLACEHOLDER, + "Untyped" => MISSING_STRING_PLACEHOLDER + }) end - - expect(placeholders_by_scalar_type).to eq({ - "Boolean" => nil, - "Cursor" => MISSING_STRING_PLACEHOLDER, - "Date" => nil, - "DateTime" => nil, - "Float" => MISSING_NUMERIC_PLACEHOLDER, - "ID" => MISSING_STRING_PLACEHOLDER, - "Int" => MISSING_NUMERIC_PLACEHOLDER, # GraphQL automatically coerces Int values - "JsonSafeLong" => MISSING_NUMERIC_PLACEHOLDER, # custom coercion adapter coerces floats back to integers - "LocalTime" => nil, - "LongString" => nil, # outside of the JSON safe range. - "String" => MISSING_STRING_PLACEHOLDER, - "TimeZone" => MISSING_STRING_PLACEHOLDER, - "Untyped" => MISSING_STRING_PLACEHOLDER - }) end def grouping_missing_value_placeholder_for(mapping_type, **json_schema_options) @@ -216,16 +183,6 @@ def scalar_coercion_adapter_path # when one worker runs multiple suites. "elastic_graph/spec_support/example_extensions/scalar_coercion_adapter" end - - def build_api - schema_elements = SchemaArtifacts::RuntimeMetadata::SchemaElementNames.new(form: "snake_case") - ::ElasticGraph::SchemaDefinition::API.new( - schema_elements, - true, - extension_modules: [APIExtension], - output: log_device - ) - end end end end 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 cf61aa6ba..8407e57c4 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(extension_modules: []) do |schema| + super do |schema| yield schema # Ensure there's at least one indexed type defined to avoid GraphQL validation errors. 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 33ae7fbf3..23fa72092 100644 --- a/spec_support/lib/elastic_graph/spec_support/builds_indexer.rb +++ b/spec_support/lib/elastic_graph/spec_support/builds_indexer.rb @@ -21,18 +21,11 @@ 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, + datastore_core: datastore_core || build_datastore_core(**datastore_core_options, &customize_datastore_config), 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 @@ -42,17 +35,6 @@ 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/ingests_json_data.rb b/spec_support/lib/elastic_graph/spec_support/ingests_json_data.rb new file mode 100644 index 000000000..49ea13157 --- /dev/null +++ b/spec_support/lib/elastic_graph/spec_support/ingests_json_data.rb @@ -0,0 +1,11 @@ +# 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/spec_support/builds_indexer" +require "elastic_graph/spec_support/json_ingestion_schema_definition" +require "elastic_graph/spec_support/uses_datastore" diff --git a/spec_support/lib/elastic_graph/spec_support/json_ingestion_schema_definition.rb b/spec_support/lib/elastic_graph/spec_support/json_ingestion_schema_definition.rb new file mode 100644 index 000000000..98c1ec051 --- /dev/null +++ b/spec_support/lib/elastic_graph/spec_support/json_ingestion_schema_definition.rb @@ -0,0 +1,48 @@ +# 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" + +module ElasticGraph + module JSONIngestionSchemaDefinition + def define_schema(extension_modules: [], **options, &block) + super( + extension_modules: json_ingestion_schema_definition_extension_modules(extension_modules), + **options, + &block + ) + end + + def define_schema_with_schema_elements(schema_elements, extension_modules: [], **options, &block) + super( + schema_elements, + extension_modules: json_ingestion_schema_definition_extension_modules(extension_modules), + **options, + &block + ) + end + + def generate_schema_artifacts(extension_modules: [], **options, &block) + super( + extension_modules: json_ingestion_schema_definition_extension_modules(extension_modules), + **options, + &block + ) + end + + private + + def json_ingestion_schema_definition_extension_modules(extension_modules = []) + [JSONIngestion::SchemaDefinition::APIExtension] | Array(extension_modules) + end + end + + RSpec.configure do |config| + config.prepend JSONIngestionSchemaDefinition, :json_ingestion_schema_definition + end +end diff --git a/spec_support/lib/elastic_graph/spec_support/uses_datastore.rb b/spec_support/lib/elastic_graph/spec_support/uses_datastore.rb index bc3fd38a0..a2f0ba795 100644 --- a/spec_support/lib/elastic_graph/spec_support/uses_datastore.rb +++ b/spec_support/lib/elastic_graph/spec_support/uses_datastore.rb @@ -549,6 +549,5 @@ def build_datastore_core(**options) # Note: we MUST consider the `body` when matching requests, because the body is # part of the core identity of requests to the datastore. meta[:vcr] = {match_requests_on: [:method, :uri, :body]} unless meta.key?(:vcr) - meta[:builds_indexer] = true end end diff --git a/spec_support/spec_helper.rb b/spec_support/spec_helper.rb index f76e05b0d..faa22b72b 100644 --- a/spec_support/spec_helper.rb +++ b/spec_support/spec_helper.rb @@ -210,7 +210,9 @@ config.raise_on_warning = true config.raise_errors_for_deprecations! + config.when_first_matching_example_defined(:json_ingestion_schema_definition) { require "elastic_graph/spec_support/json_ingestion_schema_definition" } config.when_first_matching_example_defined(:uses_datastore) { require "elastic_graph/spec_support/uses_datastore" } + config.when_first_matching_example_defined(:ingests_json_data) { require "elastic_graph/spec_support/ingests_json_data" } config.when_first_matching_example_defined(:builds_admin) { require "elastic_graph/spec_support/builds_admin" } config.when_first_matching_example_defined(:builds_datastore_core) { require "elastic_graph/spec_support/builds_datastore_core" } config.when_first_matching_example_defined(:builds_indexer) { require "elastic_graph/spec_support/builds_indexer" } @@ -442,6 +444,12 @@ def with_env(overrides) end RSpec.configure do |c| + c.define_derived_metadata(:ingests_json_data) do |m| + m[:json_ingestion_schema_definition] = true + m[:uses_datastore] = true + m[:builds_indexer] = true + end + c.define_derived_metadata(type: :unit) { |m| m[:stub_datastore_client] = true unless m.key?(:stub_datastore_client) } c.include ElasticGraph::CommonSpecHelpers end