From 72ed5776c55d63157f737125ebecb6eaa5ebc62c Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Sat, 4 Jul 2026 09:13:08 -0500 Subject: [PATCH 1/3] Restore schema-evolution declarations to elasticgraph-schema_definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves `field.renamed_from`, `type.renamed_from`, `type.deleted_field`, and `schema.deleted_type` — along with the state registries and `DeprecatedElement` record they populate — from `elasticgraph-json_ingestion` back into the core schema definition gem, partially reverting the placement chosen in #1259. These declarations record serializer-neutral facts about how a schema has evolved, and we now have a second consumer: `elasticgraph-proto_ingestion` (#1080) needs rename metadata to keep protobuf field numbers stable across renames (with `reserved` field numbers for deletions as a natural follow-up). Keeping the markers inside one serializer forced other consumers to duck-type against state that may or may not be present; with the declarations in core, every consumer reads typed `State` members directly. This also addresses the concern raised in review of #1259 about `elasticgraph-json_ingestion` bolting 9 fields onto `::ElasticGraph::SchemaDefinition::State`: its `StateExtension` now adds only the 5 genuinely JSON-specific fields, and the four deprecation registries are ordinary typed `State` members that need no `State & StateExtension` casts. All JSON-specific behavior — schema version enforcement, merge reporting, pruning, and the warnings that flag no-longer-needed declarations — stays in `elasticgraph-json_ingestion`. Co-Authored-By: Claude Fable 5 --- .../schema_definition/api_extension.rb | 23 -------- .../indexing/json_schema_with_metadata.rb | 9 ++-- .../schema_elements/field_extension.rb | 33 ------------ .../type_with_subfields_extension.rb | 54 ------------------- .../schema_definition/state_extension.rb | 51 +----------------- .../schema_definition/api_extension.rbs | 1 - .../indexing/json_schema_with_metadata.rbs | 20 +++---- .../json_schema_merge_reporter.rbs | 4 +- .../schema_definition/results_extension.rbs | 2 +- .../schema_elements/field_extension.rbs | 5 -- .../type_with_subfields_extension.rbs | 6 --- .../schema_definition/state_extension.rbs | 8 --- .../elastic_graph/schema_definition/api.rb | 23 ++++++++ .../schema_elements}/deprecated_element.rb | 6 +-- .../schema_elements/field.rb | 29 ++++++++++ .../schema_elements/type_with_subfields.rb | 48 +++++++++++++++++ .../elastic_graph/schema_definition/state.rb | 51 +++++++++++++++++- .../elastic_graph/schema_definition/api.rbs | 1 + .../schema_elements}/deprecated_element.rbs | 8 +-- .../schema_elements/field.rbs | 1 + .../schema_elements/type_with_subfields.rbs | 2 + .../elastic_graph/schema_definition/state.rbs | 12 +++++ .../deprecated_element_spec.rb | 21 ++++++-- 23 files changed, 208 insertions(+), 210 deletions(-) rename {elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition => elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements}/deprecated_element.rb (76%) rename {elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition => elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements}/deprecated_element.rbs (64%) rename {elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition => elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements}/deprecated_element_spec.rb (72%) diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/api_extension.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/api_extension.rb index fa95173d7..a8b8ef645 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/api_extension.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/api_extension.rb @@ -7,7 +7,6 @@ # frozen_string_literal: true require "elastic_graph/constants" -require "elastic_graph/json_ingestion/schema_definition/deprecated_element" require "elastic_graph/json_ingestion/schema_definition/factory_extension" require "elastic_graph/json_ingestion/schema_definition/state_extension" @@ -143,28 +142,6 @@ def json_schema_strictness(allow_omitted_fields: false, allow_extra_fields: true nil end - # Registers the name of a type that existed in a prior JSON schema version but has been deleted. - # - # @note In situations where this API applies, ElasticGraph will give you an error message indicating that you need to use this API - # or {SchemaElements::TypeWithSubfieldsExtension#renamed_from}. Likewise, when ElasticGraph no longer needs to know about this, - # it'll give you a warning indicating the call to this method can be removed. - # - # @param name [String] name of type that used to exist but has been deleted - # @return [void] - # - # @example Indicate that `Widget` has been deleted - # ElasticGraph.define_schema do |schema| - # schema.deleted_type "Widget" - # end - def deleted_type(name) - json_ingestion_state.register_deleted_type( - name, - defined_at: caller_locations(1, 1).to_a.first, # : ::Thread::Backtrace::Location - defined_via: %(schema.deleted_type "#{name}") - ) - nil - end - private # Returns the API's `state` narrowed to include this gem's `StateExtension`. Centralizes diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb index ba9d3e5b1..272739aa9 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb @@ -52,12 +52,11 @@ class Merger attr_reader :unused_deprecated_elements def initialize(schema_def_results) - json_ingestion_state = schema_def_results.state # : ::ElasticGraph::SchemaDefinition::State & SchemaDefinition::StateExtension @field_metadata_by_type_and_field_name = schema_def_results.json_schema_field_metadata_by_type_and_field_name - @renamed_types_by_old_name = json_ingestion_state.renamed_types_by_old_name - @deleted_types_by_old_name = json_ingestion_state.deleted_types_by_old_name - @renamed_fields_by_type_name_and_old_field_name = json_ingestion_state.renamed_fields_by_type_name_and_old_field_name - @deleted_fields_by_type_name_and_old_field_name = json_ingestion_state.deleted_fields_by_type_name_and_old_field_name + @renamed_types_by_old_name = schema_def_results.state.renamed_types_by_old_name + @deleted_types_by_old_name = schema_def_results.state.deleted_types_by_old_name + @renamed_fields_by_type_name_and_old_field_name = schema_def_results.state.renamed_fields_by_type_name_and_old_field_name + @deleted_fields_by_type_name_and_old_field_name = schema_def_results.state.deleted_fields_by_type_name_and_old_field_name @state = schema_def_results.state @derived_indexing_type_names = schema_def_results.derived_indexing_type_names diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rb index 1edaa29d1..918bd13d0 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rb @@ -90,33 +90,6 @@ def json_schema(nullable: nil, **options) super(**options) end - # Registers an old name that this field used to have in a prior JSON schema version. - # - # @note In situations where this API applies, ElasticGraph will give you an error message indicating that you need to use this API - # or {TypeWithSubfieldsExtension#deleted_field}. Likewise, when ElasticGraph no longer needs to know about this, it'll give you a warning - # indicating the call to this method can be removed. - # - # @param old_name [String] old name this field used to have in a prior version of the schema - # @return [void] - # - # @example Indicate that `Widget.description` used to be called `Widget.notes`. - # ElasticGraph.define_schema do |schema| - # schema.object_type "Widget" do |t| - # t.field "description", "String" do |f| - # f.renamed_from "notes" - # end - # end - # end - def renamed_from(old_name) - json_ingestion_state.register_renamed_field( - parent_type.name, - from: old_name, - to: name, - defined_at: caller_locations(1, 1).to_a.first, # : ::Thread::Backtrace::Location - defined_via: %(field.renamed_from "#{old_name}") - ) - end - # @private def to_indexing_field_reference reference = super @@ -131,12 +104,6 @@ def to_indexing_field_reference doc_comment: doc_comment ) end - - private - - def json_ingestion_state - schema_def_state # : ::ElasticGraph::SchemaDefinition::State & SchemaDefinition::StateExtension - end end end end diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rb index bcfc856d8..78085fb66 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rb @@ -16,54 +16,6 @@ module SchemaElements module TypeWithSubfieldsExtension include HasJSONSchema - # Registers the name of a field that existed in a prior JSON schema version but has been deleted. - # - # @note In situations where this API applies, ElasticGraph will give you an error message indicating that you need to use this API - # or {FieldExtension#renamed_from}. Likewise, when ElasticGraph no longer needs to know about this, it'll give you a warning - # indicating the call to this method can be removed. - # - # @param field_name [String] name of field that used to exist but has been deleted - # @return [void] - # - # @example Indicate that `Widget.description` has been deleted - # ElasticGraph.define_schema do |schema| - # schema.object_type "Widget" do |t| - # t.deleted_field "description" - # end - # end - def deleted_field(field_name) - json_ingestion_state.register_deleted_field( - name, - field_name, - defined_at: caller_locations(2, 1).to_a.first, # : ::Thread::Backtrace::Location - defined_via: %(type.deleted_field "#{field_name}") - ) - end - - # Registers an old name that this type used to have in a prior JSON schema version. - # - # @note In situations where this API applies, ElasticGraph will give you an error message indicating that you need to use this API - # or {APIExtension#deleted_type}. Likewise, when ElasticGraph no longer needs to know about this, it'll give you a warning - # indicating the call to this method can be removed. - # - # @param old_name [String] old name this type used to have in a prior version of the schema - # @return [void] - # - # @example Indicate that `Widget` used to be called `Component`. - # ElasticGraph.define_schema do |schema| - # schema.object_type "Widget" do |t| - # t.renamed_from "Component" - # end - # end - def renamed_from(old_name) - json_ingestion_state.register_renamed_type( - name, - from: old_name, - defined_at: caller_locations(2, 1).to_a.first, # : ::Thread::Backtrace::Location - defined_via: %(type.renamed_from "#{old_name}") - ) - end - # @private def to_indexing_field_type field_type = super # : Indexing::FieldType::Object @@ -71,12 +23,6 @@ def to_indexing_field_type field_type.doc_comment = doc_comment field_type end - - private - - def json_ingestion_state - schema_def_state # : ::ElasticGraph::SchemaDefinition::State & SchemaDefinition::StateExtension - end end end end diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/state_extension.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/state_extension.rb index a9fb58168..d072bb02d 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/state_extension.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/state_extension.rb @@ -6,8 +6,6 @@ # # frozen_string_literal: true -require "elastic_graph/json_ingestion/schema_definition/deprecated_element" - module ElasticGraph module JSONIngestion module SchemaDefinition @@ -20,12 +18,7 @@ module StateExtension # @dynamic enforce_json_schema_version, enforce_json_schema_version= # @dynamic allow_omitted_json_schema_fields, allow_omitted_json_schema_fields= # @dynamic allow_extra_json_schema_fields, allow_extra_json_schema_fields= - # @dynamic renamed_types_by_old_name, renamed_types_by_old_name= - # @dynamic deleted_types_by_old_name, deleted_types_by_old_name= - # @dynamic renamed_fields_by_type_name_and_old_field_name, renamed_fields_by_type_name_and_old_field_name= - # @dynamic deleted_fields_by_type_name_and_old_field_name, deleted_fields_by_type_name_and_old_field_name= - attr_accessor :json_schema_version, :json_schema_version_setter_location, :enforce_json_schema_version, :allow_omitted_json_schema_fields, :allow_extra_json_schema_fields, - :renamed_types_by_old_name, :deleted_types_by_old_name, :renamed_fields_by_type_name_and_old_field_name, :deleted_fields_by_type_name_and_old_field_name + attr_accessor :json_schema_version, :json_schema_version_setter_location, :enforce_json_schema_version, :allow_omitted_json_schema_fields, :allow_extra_json_schema_fields def self.extended(state) state.json_schema_version = nil @@ -33,48 +26,6 @@ def self.extended(state) state.enforce_json_schema_version = true state.allow_omitted_json_schema_fields = false state.allow_extra_json_schema_fields = true - state.renamed_types_by_old_name = {} - state.deleted_types_by_old_name = {} - state.renamed_fields_by_type_name_and_old_field_name = ::Hash.new { |h, k| h[k] = {} } - state.deleted_fields_by_type_name_and_old_field_name = ::Hash.new { |h, k| h[k] = {} } - end - - def register_renamed_type(type_name, from:, defined_at:, defined_via:) - renamed_types_by_old_name[from] = DeprecatedElement.new( - schema_def_state: self, - name: type_name, - defined_at: defined_at, - defined_via: defined_via - ) - end - - def register_deleted_type(type_name, defined_at:, defined_via:) - deleted_types_by_old_name[type_name] = DeprecatedElement.new( - schema_def_state: self, - name: type_name, - defined_at: defined_at, - defined_via: defined_via - ) - end - - def register_renamed_field(type_name, from:, to:, defined_at:, defined_via:) - renamed_fields_by_old_field_name = renamed_fields_by_type_name_and_old_field_name[type_name] - renamed_fields_by_old_field_name[from] = DeprecatedElement.new( - schema_def_state: self, - name: to, - defined_at: defined_at, - defined_via: defined_via - ) - end - - def register_deleted_field(type_name, field_name, defined_at:, defined_via:) - deleted_fields_by_old_field_name = deleted_fields_by_type_name_and_old_field_name[type_name] - deleted_fields_by_old_field_name[field_name] = DeprecatedElement.new( - schema_def_state: self, - name: field_name, - defined_at: defined_at, - defined_via: defined_via - ) end end end diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/api_extension.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/api_extension.rbs index 40496a4d5..4a2bc4756 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/api_extension.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/api_extension.rbs @@ -5,7 +5,6 @@ module ElasticGraph def json_schema_version: (::Integer) -> void def enforce_json_schema_version: (bool) -> void def json_schema_strictness: (?allow_omitted_fields: bool, ?allow_extra_fields: bool) -> void - def deleted_type: (::String) -> void def self.extended: (::ElasticGraph::SchemaDefinition::API & APIExtension) -> void diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rbs index a76cc94a8..4ddde82f6 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rbs @@ -6,14 +6,14 @@ module ElasticGraph attr_reader json_schema: ::Hash[::String, untyped] attr_reader missing_fields: ::Set[::String] attr_reader missing_types: ::Set[::String] - attr_reader definition_conflicts: ::Set[ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] + attr_reader definition_conflicts: ::Set[ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] attr_reader missing_necessary_fields: ::Array[JSONSchemaWithMetadata::MissingNecessaryField] def initialize: ( json_schema: ::Hash[::String, untyped], missing_fields: ::Set[::String], missing_types: ::Set[::String], - definition_conflicts: ::Set[ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement], + definition_conflicts: ::Set[ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement], missing_necessary_fields: ::Array[JSONSchemaWithMetadata::MissingNecessaryField] ) -> void @@ -21,7 +21,7 @@ module ElasticGraph ?json_schema: ::Hash[::String, untyped], ?missing_fields: ::Set[::String], ?missing_types: ::Set[::String], - ?definition_conflicts: ::Set[ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement], + ?definition_conflicts: ::Set[ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement], ?missing_necessary_fields: ::Array[JSONSchemaWithMetadata::MissingNecessaryField] ) -> instance end @@ -31,14 +31,14 @@ module ElasticGraph class Merger @field_metadata_by_type_and_field_name: ::Hash[::String, ::Hash[::String, JSONSchemaFieldMetadata]] - @renamed_types_by_old_name: ::Hash[::String, ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] - @deleted_types_by_old_name: ::Hash[::String, ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] - @renamed_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement]] - @deleted_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement]] + @renamed_types_by_old_name: ::Hash[::String, ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] + @deleted_types_by_old_name: ::Hash[::String, ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] + @renamed_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement]] + @deleted_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement]] @state: ElasticGraph::SchemaDefinition::State @derived_indexing_type_names: ::Set[::String] - attr_reader unused_deprecated_elements: ::Set[ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] + attr_reader unused_deprecated_elements: ::Set[ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] def initialize: ((ElasticGraph::SchemaDefinition::Results & ResultsExtension)) -> void def merge_metadata_into: (::Hash[::String, untyped]) -> JSONSchemaWithMetadata @@ -48,14 +48,14 @@ module ElasticGraph def determine_current_type_name: ( ::String, missing_types: ::Set[::String], - definition_conflicts: ::Set[ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] + definition_conflicts: ::Set[ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] ) -> ::String? def field_metadata_for: ( ::String, ::String, missing_fields: ::Set[::String], - definition_conflicts: ::Set[ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] + definition_conflicts: ::Set[ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] ) -> JSONSchemaFieldMetadata? def identify_missing_necessary_fields: ( diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/json_schema_merge_reporter.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/json_schema_merge_reporter.rbs index 5e4e9b7a8..4b4f6f0f6 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/json_schema_merge_reporter.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/json_schema_merge_reporter.rbs @@ -5,13 +5,13 @@ module ElasticGraph def initialize: (io) -> void def report_errors: (::Array[Indexing::JSONSchemaWithMetadata]) -> void - def report_warnings: (::Set[::ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement]) -> void + def report_warnings: (::Set[::ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement]) -> void private @output: io - def format_deprecated_elements: (::Enumerable[::ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement]) -> ::String + def format_deprecated_elements: (::Enumerable[::ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement]) -> ::String def missing_field_error_for: (::String, ::Array[::Integer]) -> ::String def missing_type_error_for: (::String, ::Array[::Integer]) -> ::String def missing_necessary_field_error_for: (Indexing::JSONSchemaWithMetadata::MissingNecessaryField, ::Array[::Integer]) -> ::String diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/results_extension.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/results_extension.rbs index 67fcdd87d..c6c33eb4a 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/results_extension.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/results_extension.rbs @@ -9,7 +9,7 @@ module ElasticGraph def json_schema_field_metadata_by_type_and_field_name: () -> ::Hash[::String, ::Hash[::String, Indexing::JSONSchemaFieldMetadata]] def current_public_json_schema: () -> ::Hash[::String, untyped] def merge_field_metadata_into_json_schema: (::Hash[::String, untyped]) -> Indexing::JSONSchemaWithMetadata - def unused_deprecated_elements: () -> ::Set[::ElasticGraph::JSONIngestion::SchemaDefinition::DeprecatedElement] + def unused_deprecated_elements: () -> ::Set[::ElasticGraph::SchemaDefinition::SchemaElements::DeprecatedElement] private diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rbs index 097c8bb95..5803b2919 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/field_extension.rbs @@ -9,12 +9,7 @@ module ElasticGraph def non_nullable_in_json_schema?: () -> bool def json_schema: (?nullable: bool?, **untyped) -> void - def renamed_from: (::String) -> void def to_indexing_field_reference: () -> Indexing::FieldReference? - - private - - def json_ingestion_state: () -> (::ElasticGraph::SchemaDefinition::State & SchemaDefinition::StateExtension) end end end diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rbs index 01c7cbf89..9eded4420 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/schema_elements/type_with_subfields_extension.rbs @@ -5,13 +5,7 @@ module ElasticGraph module TypeWithSubfieldsExtension: ::ElasticGraph::SchemaDefinition::SchemaElements::TypeWithSubfields include HasJSONSchema - def deleted_field: (::String) -> void - def renamed_from: (::String) -> void def to_indexing_field_type: () -> Indexing::FieldType::Object - - private - - def json_ingestion_state: () -> (::ElasticGraph::SchemaDefinition::State & SchemaDefinition::StateExtension) end end end diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/state_extension.rbs b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/state_extension.rbs index a1bf56493..06f930cbb 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/state_extension.rbs +++ b/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/state_extension.rbs @@ -7,16 +7,8 @@ module ElasticGraph attr_accessor enforce_json_schema_version: bool attr_accessor allow_omitted_json_schema_fields: bool attr_accessor allow_extra_json_schema_fields: bool - attr_accessor renamed_types_by_old_name: ::Hash[::String, DeprecatedElement] - attr_accessor deleted_types_by_old_name: ::Hash[::String, DeprecatedElement] - attr_accessor renamed_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, DeprecatedElement]] - attr_accessor deleted_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, DeprecatedElement]] def self.extended: (::ElasticGraph::SchemaDefinition::State & StateExtension) -> void - def register_renamed_type: (::String, from: ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void - def register_deleted_type: (::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void - def register_renamed_field: (::String, from: ::String, to: ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void - def register_deleted_field: (::String, ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void end end end diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb index 01d04387f..10d05492e 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb @@ -301,6 +301,29 @@ def scalar_type(name, &block) nil end + # Registers the name of a type that existed in a prior version of the schema but has been deleted. + # + # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # indicating that you need to use this API or {SchemaElements::TypeWithSubfields#renamed_from}. Likewise, when + # ElasticGraph no longer needs to know about this, it'll give you a warning indicating the call to this method can + # be removed. + # + # @param name [String] name of type that used to exist but has been deleted + # @return [void] + # + # @example Indicate that `Widget` has been deleted + # ElasticGraph.define_schema do |schema| + # schema.deleted_type "Widget" + # end + def deleted_type(name) + @state.register_deleted_type( + name, + defined_at: caller_locations(1, 1).to_a.first, # : ::Thread::Backtrace::Location + defined_via: %(schema.deleted_type "#{name}") + ) + nil + end + # Registers a GraphQL extension module that will be loaded and used by `elasticgraph-graphql`. While such # extension modules can also be configured in a settings YAML file, it can be useful to register it here # when you want to ensure that the extension is used in all environments. For example, an extension library diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/deprecated_element.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/deprecated_element.rb similarity index 76% rename from elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/deprecated_element.rb rename to elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/deprecated_element.rb index 62b17864c..229b0ccb6 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/deprecated_element.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/deprecated_element.rb @@ -7,9 +7,9 @@ # frozen_string_literal: true module ElasticGraph - module JSONIngestion - module SchemaDefinition - # Records a schema element that existed in a prior JSON schema version. + module SchemaDefinition + module SchemaElements + # Records a schema element (such as a type or a field) that existed in a prior version of the schema. # # @private DeprecatedElement = ::Data.define(:schema_def_state, :name, :defined_at, :defined_via) do diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb index 75664619a..98e4ab045 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb @@ -563,6 +563,35 @@ def resolve_with(resolver_name, **config) self.resolver = resolver_name&.then { SchemaArtifacts::RuntimeMetadata::ConfiguredGraphQLResolver.new(it, config) } end + # Registers an old name that this field used to have in a prior version of the schema. Extensions + # use this to deal with schema evolution — for example, `elasticgraph-json_ingestion` uses it to + # migrate data ingested under the old JSON schema version. + # + # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # indicating that you need to use this API or `deleted_field`. Likewise, when ElasticGraph no longer needs to know + # about this, it'll give you a warning indicating the call to this method can be removed. + # + # @param old_name [String] old name this field used to have in a prior version of the schema + # @return [void] + # + # @example Indicate that `Widget.description` used to be called `Widget.notes`. + # ElasticGraph.define_schema do |schema| + # schema.object_type "Widget" do |t| + # t.field "description", "String" do |f| + # f.renamed_from "notes" + # end + # end + # end + def renamed_from(old_name) + schema_def_state.register_renamed_field( + parent_type.name, + from: old_name, + to: name, + defined_at: caller_locations(1, 1).to_a.first, # : ::Thread::Backtrace::Location + defined_via: %(field.renamed_from "#{old_name}") + ) + end + # @private def runtime_script(script) self.runtime_field_script = script diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb index 2a8ce3d8f..0570b821a 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb @@ -419,6 +419,54 @@ def relates_to_many(field_name, type, via:, dir:, singular: nil, indexing_only: end end + # Registers the name of a field that existed in a prior version of the schema but has been deleted. + # + # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # indicating that you need to use this API or {Field#renamed_from}. Likewise, when ElasticGraph no longer needs to + # know about this, it'll give you a warning indicating the call to this method can be removed. + # + # @param field_name [String] name of field that used to exist but has been deleted + # @return [void] + # + # @example Indicate that `Widget.description` has been deleted + # ElasticGraph.define_schema do |schema| + # schema.object_type "Widget" do |t| + # t.deleted_field "description" + # end + # end + def deleted_field(field_name) + schema_def_state.register_deleted_field( + name, + field_name, + defined_at: caller_locations(2, 1).to_a.first, # : ::Thread::Backtrace::Location + defined_via: %(type.deleted_field "#{field_name}") + ) + end + + # Registers an old name that this type used to have in a prior version of the schema. + # + # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # indicating that you need to use this API or {API#deleted_type}. Likewise, when ElasticGraph no longer needs to + # know about this, it'll give you a warning indicating the call to this method can be removed. + # + # @param old_name [String] old name this type used to have in a prior version of the schema + # @return [void] + # + # @example Indicate that `Widget` used to be called `Component`. + # ElasticGraph.define_schema do |schema| + # schema.object_type "Widget" do |t| + # t.renamed_from "Component" + # end + # end + def renamed_from(old_name) + schema_def_state.register_renamed_type( + name, + from: old_name, + defined_at: caller_locations(2, 1).to_a.first, # : ::Thread::Backtrace::Location + defined_via: %(type.renamed_from "#{old_name}") + ) + end + # Converts the type to GraphQL SDL syntax. # # @private diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb index aceb18ef1..aef5fd4d8 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb @@ -9,6 +9,7 @@ require "elastic_graph/errors" require "elastic_graph/schema_definition/factory" require "elastic_graph/schema_definition/mixins/has_readable_to_s_and_inspect" +require "elastic_graph/schema_definition/schema_elements/deprecated_element" require "elastic_graph/schema_definition/schema_elements/enum_value_namer" require "elastic_graph/schema_definition/schema_elements/field_path" require "elastic_graph/schema_definition/schema_elements/sub_aggregation_path" @@ -49,7 +50,11 @@ class State < Struct.new( :output, :type_namer, :enum_value_namer, - :indexed_types_by_index_name + :indexed_types_by_index_name, + :renamed_types_by_old_name, + :deleted_types_by_old_name, + :renamed_fields_by_type_name_and_old_field_name, + :deleted_fields_by_type_name_and_old_field_name ) include Mixins::HasReadableToSAndInspect.new @@ -95,7 +100,11 @@ def self.with( ), enum_value_namer: SchemaElements::EnumValueNamer.new(enum_value_overrides_by_type), output: output, - indexed_types_by_index_name: {} + indexed_types_by_index_name: {}, + renamed_types_by_old_name: {}, + deleted_types_by_old_name: {}, + renamed_fields_by_type_name_and_old_field_name: ::Hash.new { |h, k| h[k] = {} }, + deleted_fields_by_type_name_and_old_field_name: ::Hash.new { |h, k| h[k] = {} } ) end @@ -136,6 +145,44 @@ def register_user_defined_field(field) user_defined_fields << field end + def register_renamed_type(type_name, from:, defined_at:, defined_via:) + renamed_types_by_old_name[from] = SchemaElements::DeprecatedElement.new( + schema_def_state: self, + name: type_name, + defined_at: defined_at, + defined_via: defined_via + ) + end + + def register_deleted_type(type_name, defined_at:, defined_via:) + deleted_types_by_old_name[type_name] = SchemaElements::DeprecatedElement.new( + schema_def_state: self, + name: type_name, + defined_at: defined_at, + defined_via: defined_via + ) + end + + def register_renamed_field(type_name, from:, to:, defined_at:, defined_via:) + renamed_fields_by_old_field_name = renamed_fields_by_type_name_and_old_field_name[type_name] + renamed_fields_by_old_field_name[from] = SchemaElements::DeprecatedElement.new( + schema_def_state: self, + name: to, + defined_at: defined_at, + defined_via: defined_via + ) + end + + def register_deleted_field(type_name, field_name, defined_at:, defined_via:) + deleted_fields_by_old_field_name = deleted_fields_by_type_name_and_old_field_name[type_name] + deleted_fields_by_old_field_name[field_name] = SchemaElements::DeprecatedElement.new( + schema_def_state: self, + name: field_name, + defined_at: defined_at, + defined_via: defined_via + ) + end + def user_defined_field_references_by_type_name @user_defined_field_references_by_type_name ||= begin unless user_definition_complete diff --git a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/api.rbs b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/api.rbs index 3dbb4e055..199b517aa 100644 --- a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/api.rbs +++ b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/api.rbs @@ -72,6 +72,7 @@ module ElasticGraph def enum_type: (::String) { (SchemaElements::EnumType) -> void } -> void def union_type: (::String) { (SchemaElements::UnionType) -> void } -> void def scalar_type: (::String) { (SchemaElements::ScalarType) -> void } -> void + def deleted_type: (::String) -> void def as_active_instance: { () -> void } -> void @results: Results? def results: () -> Results diff --git a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/deprecated_element.rbs b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/deprecated_element.rbs similarity index 64% rename from elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/deprecated_element.rbs rename to elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/deprecated_element.rbs index 985966712..b2b3d1f9b 100644 --- a/elasticgraph-json_ingestion/sig/elastic_graph/json_ingestion/schema_definition/deprecated_element.rbs +++ b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/deprecated_element.rbs @@ -1,14 +1,14 @@ module ElasticGraph - module JSONIngestion - module SchemaDefinition + module SchemaDefinition + module SchemaElements class DeprecatedElement - attr_reader schema_def_state: ::ElasticGraph::SchemaDefinition::State & StateExtension + attr_reader schema_def_state: State attr_reader name: ::String attr_reader defined_at: ::Thread::Backtrace::Location attr_reader defined_via: ::String def initialize: ( - schema_def_state: ::ElasticGraph::SchemaDefinition::State & StateExtension, + schema_def_state: State, name: ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String diff --git a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/field.rbs b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/field.rbs index 68b09cd35..e1b34cdc6 100644 --- a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/field.rbs +++ b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/field.rbs @@ -79,6 +79,7 @@ module ElasticGraph def to_sdl: (?type_structure_only: bool, ?default_value_sdl: ::String?) ?{ (argument) -> boolish } -> ::String def sourced_from: (::String, ::String) -> void def resolve_with: (::Symbol?, **untyped) -> void + def renamed_from: (::String) -> void def paths_to_lists_for_count_indexing: (?has_list_ancestor: bool) -> ::Array[::String] def index_leaf?: () -> bool diff --git a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/type_with_subfields.rbs b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/type_with_subfields.rbs index 1a6a4f3c1..a5a576f94 100644 --- a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/type_with_subfields.rbs +++ b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/schema_elements/type_with_subfields.rbs @@ -76,6 +76,8 @@ module ElasticGraph def relates_to_one: (::String, ::String, via: ::String, dir: foreignKeyDirection, ?indexing_only: bool) ?{ (Relationship) -> void } -> void def relates_to_many: (::String, ::String, via: ::String, dir: foreignKeyDirection, ?singular: ::String?, ?indexing_only: bool) ?{ (Relationship) -> void } -> void + def deleted_field: (::String) -> void + def renamed_from: (::String) -> void def generate_sdl: (name_section: ::String) ?{ (Field::argument) -> boolish } -> String def current_sources: () -> ::Array[::String] diff --git a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/state.rbs b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/state.rbs index e538c9f70..4673ab4fb 100644 --- a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/state.rbs +++ b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/state.rbs @@ -28,6 +28,10 @@ module ElasticGraph attr_reader enum_value_namer: SchemaElements::EnumValueNamer attr_accessor output: io attr_accessor indexed_types_by_index_name: ::Hash[::String, indexableType] + attr_accessor renamed_types_by_old_name: ::Hash[::String, SchemaElements::DeprecatedElement] + attr_accessor deleted_types_by_old_name: ::Hash[::String, SchemaElements::DeprecatedElement] + attr_accessor renamed_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, SchemaElements::DeprecatedElement]] + attr_accessor deleted_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, SchemaElements::DeprecatedElement]] def initialize: ( api: API, @@ -57,6 +61,10 @@ module ElasticGraph enum_value_namer: SchemaElements::EnumValueNamer, output: io, indexed_types_by_index_name: ::Hash[::String, indexableType], + renamed_types_by_old_name: ::Hash[::String, SchemaElements::DeprecatedElement], + deleted_types_by_old_name: ::Hash[::String, SchemaElements::DeprecatedElement], + renamed_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, SchemaElements::DeprecatedElement]], + deleted_fields_by_type_name_and_old_field_name: ::Hash[::String, ::Hash[::String, SchemaElements::DeprecatedElement]], ) -> void end @@ -80,6 +88,10 @@ module ElasticGraph def register_scalar_type: (SchemaElements::ScalarType) -> void def register_input_type: (SchemaElements::InputType) -> void def register_user_defined_field: (SchemaElements::Field) -> void + def register_renamed_type: (::String, from: ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void + def register_deleted_type: (::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void + def register_renamed_field: (::String, from: ::String, to: ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void + def register_deleted_field: (::String, ::String, defined_at: ::Thread::Backtrace::Location, defined_via: ::String) -> void @factory: Factory? def factory: () -> Factory diff --git a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb similarity index 72% rename from elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb rename to elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb index 425bb4667..09a734484 100644 --- a/elasticgraph-json_ingestion/spec/unit/elastic_graph/json_ingestion/schema_definition/deprecated_element_spec.rb +++ b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb @@ -6,11 +6,15 @@ # # frozen_string_literal: true +require "elastic_graph/spec_support/schema_definition_helpers" + module ElasticGraph - module JSONIngestion - module SchemaDefinition + module SchemaDefinition + module SchemaElements RSpec.describe DeprecatedElement do - it "records `deleted_type`, `deleted_field`, and `renamed_from` calls so that schema artifact tooling can consume them" do + include_context "SchemaDefinitionHelpers" + + it "records `deleted_type`, `deleted_field`, and `renamed_from` calls so that extensions (such as ingestion serializers) can consume them" do state = define_schema(schema_element_name_form: "snake_case") do |schema| schema.deleted_type "OldType" @@ -43,6 +47,17 @@ module SchemaDefinition /\A`field\.renamed_from "old_name"` at .+:\d+\z/ ) end + + it "reports the caller's schema definition location (not ElasticGraph internals) as `defined_at`" do + state = define_schema(schema_element_name_form: "snake_case") do |schema| + schema.object_type "Widget" do |t| + t.renamed_from "OldWidget" + t.field "id", "ID!" + end + end.state + + expect(state.renamed_types_by_old_name.fetch("OldWidget").defined_at.path).to eq __FILE__ + end end end end From 683be9a9612e2561a7b63dbc6dcdc5c4e7907f65 Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Tue, 7 Jul 2026 09:39:10 -0500 Subject: [PATCH 2/3] Address PR feedback: restore `new_deprecated_element` factory method, tighten docs, expand `defined_at` test coverage --- .../indexing/json_schema_with_metadata.rb | 8 ++--- .../elastic_graph/schema_definition/api.rb | 2 +- .../schema_definition/factory.rb | 6 ++++ .../schema_elements/field.rb | 4 +-- .../schema_elements/type_with_subfields.rb | 4 +-- .../elastic_graph/schema_definition/state.rb | 29 +++------------- .../schema_definition/factory.rbs | 7 ++++ .../deprecated_element_spec.rb | 34 ++++++++++++++++++- 8 files changed, 59 insertions(+), 35 deletions(-) diff --git a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb index 272739aa9..b83279c45 100644 --- a/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb +++ b/elasticgraph-json_ingestion/lib/elastic_graph/json_ingestion/schema_definition/indexing/json_schema_with_metadata.rb @@ -53,11 +53,11 @@ class Merger def initialize(schema_def_results) @field_metadata_by_type_and_field_name = schema_def_results.json_schema_field_metadata_by_type_and_field_name - @renamed_types_by_old_name = schema_def_results.state.renamed_types_by_old_name - @deleted_types_by_old_name = schema_def_results.state.deleted_types_by_old_name - @renamed_fields_by_type_name_and_old_field_name = schema_def_results.state.renamed_fields_by_type_name_and_old_field_name - @deleted_fields_by_type_name_and_old_field_name = schema_def_results.state.deleted_fields_by_type_name_and_old_field_name @state = schema_def_results.state + @renamed_types_by_old_name = @state.renamed_types_by_old_name + @deleted_types_by_old_name = @state.deleted_types_by_old_name + @renamed_fields_by_type_name_and_old_field_name = @state.renamed_fields_by_type_name_and_old_field_name + @deleted_fields_by_type_name_and_old_field_name = @state.deleted_fields_by_type_name_and_old_field_name @derived_indexing_type_names = schema_def_results.derived_indexing_type_names @unused_deprecated_elements = ( diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb index 10d05492e..82d64e413 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/api.rb @@ -303,7 +303,7 @@ def scalar_type(name, &block) # Registers the name of a type that existed in a prior version of the schema but has been deleted. # - # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # @note In situations where this API is needed, ElasticGraph will give you an error message # indicating that you need to use this API or {SchemaElements::TypeWithSubfields#renamed_from}. Likewise, when # ElasticGraph no longer needs to know about this, it'll give you a warning indicating the call to this method can # be removed. diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/factory.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/factory.rb index 9d43c43ac..b32611fac 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/factory.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/factory.rb @@ -17,6 +17,7 @@ require "elastic_graph/schema_definition/schema_artifact_manager" require "elastic_graph/schema_definition/schema_elements/argument" require "elastic_graph/schema_definition/schema_elements/built_in_types" +require "elastic_graph/schema_definition/schema_elements/deprecated_element" require "elastic_graph/schema_definition/schema_elements/directive" require "elastic_graph/schema_definition/schema_elements/enum_type" require "elastic_graph/schema_definition/schema_elements/enum_value" @@ -79,6 +80,11 @@ def self.prevent_non_factory_instantiation_of(klass) end end + def new_deprecated_element(name, defined_at:, defined_via:) + @@deprecated_element_new.call(schema_def_state: @state, name: name, defined_at: defined_at, defined_via: defined_via) + end + @@deprecated_element_new = prevent_non_factory_instantiation_of(SchemaElements::DeprecatedElement) + def new_argument(field, name, value_type) @@argument_new.call(@state, field, name, value_type).tap do |argument| yield argument if block_given? diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb index 98e4ab045..66b64a02e 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb @@ -565,9 +565,9 @@ def resolve_with(resolver_name, **config) # Registers an old name that this field used to have in a prior version of the schema. Extensions # use this to deal with schema evolution — for example, `elasticgraph-json_ingestion` uses it to - # migrate data ingested under the old JSON schema version. + # ingest events that contain old field names that are no longer in the current schema. # - # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # @note In situations where this API is needed, ElasticGraph will give you an error message # indicating that you need to use this API or `deleted_field`. Likewise, when ElasticGraph no longer needs to know # about this, it'll give you a warning indicating the call to this method can be removed. # diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb index 0570b821a..7a0dd0ed6 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb @@ -421,7 +421,7 @@ def relates_to_many(field_name, type, via:, dir:, singular: nil, indexing_only: # Registers the name of a field that existed in a prior version of the schema but has been deleted. # - # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # @note In situations where this API is needed, ElasticGraph will give you an error message # indicating that you need to use this API or {Field#renamed_from}. Likewise, when ElasticGraph no longer needs to # know about this, it'll give you a warning indicating the call to this method can be removed. # @@ -445,7 +445,7 @@ def deleted_field(field_name) # Registers an old name that this type used to have in a prior version of the schema. # - # @note When `elasticgraph-json_ingestion` is in use and this API applies, ElasticGraph will give you an error message + # @note In situations where this API is needed, ElasticGraph will give you an error message # indicating that you need to use this API or {API#deleted_type}. Likewise, when ElasticGraph no longer needs to # know about this, it'll give you a warning indicating the call to this method can be removed. # diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb index aef5fd4d8..eee523187 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/state.rb @@ -9,7 +9,6 @@ require "elastic_graph/errors" require "elastic_graph/schema_definition/factory" require "elastic_graph/schema_definition/mixins/has_readable_to_s_and_inspect" -require "elastic_graph/schema_definition/schema_elements/deprecated_element" require "elastic_graph/schema_definition/schema_elements/enum_value_namer" require "elastic_graph/schema_definition/schema_elements/field_path" require "elastic_graph/schema_definition/schema_elements/sub_aggregation_path" @@ -146,41 +145,21 @@ def register_user_defined_field(field) end def register_renamed_type(type_name, from:, defined_at:, defined_via:) - renamed_types_by_old_name[from] = SchemaElements::DeprecatedElement.new( - schema_def_state: self, - name: type_name, - defined_at: defined_at, - defined_via: defined_via - ) + renamed_types_by_old_name[from] = factory.new_deprecated_element(type_name, defined_at: defined_at, defined_via: defined_via) end def register_deleted_type(type_name, defined_at:, defined_via:) - deleted_types_by_old_name[type_name] = SchemaElements::DeprecatedElement.new( - schema_def_state: self, - name: type_name, - defined_at: defined_at, - defined_via: defined_via - ) + deleted_types_by_old_name[type_name] = factory.new_deprecated_element(type_name, defined_at: defined_at, defined_via: defined_via) end def register_renamed_field(type_name, from:, to:, defined_at:, defined_via:) renamed_fields_by_old_field_name = renamed_fields_by_type_name_and_old_field_name[type_name] - renamed_fields_by_old_field_name[from] = SchemaElements::DeprecatedElement.new( - schema_def_state: self, - name: to, - defined_at: defined_at, - defined_via: defined_via - ) + renamed_fields_by_old_field_name[from] = factory.new_deprecated_element(to, defined_at: defined_at, defined_via: defined_via) end def register_deleted_field(type_name, field_name, defined_at:, defined_via:) deleted_fields_by_old_field_name = deleted_fields_by_type_name_and_old_field_name[type_name] - deleted_fields_by_old_field_name[field_name] = SchemaElements::DeprecatedElement.new( - schema_def_state: self, - name: field_name, - defined_at: defined_at, - defined_via: defined_via - ) + deleted_fields_by_old_field_name[field_name] = factory.new_deprecated_element(field_name, defined_at: defined_at, defined_via: defined_via) end def user_defined_field_references_by_type_name diff --git a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/factory.rbs b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/factory.rbs index a7122abef..fafcf4682 100644 --- a/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/factory.rbs +++ b/elasticgraph-schema_definition/sig/elastic_graph/schema_definition/factory.rbs @@ -7,6 +7,13 @@ module ElasticGraph def self.prevent_non_factory_instantiation_of: (::Class) -> ::Method + def new_deprecated_element: ( + ::String, + defined_at: ::Thread::Backtrace::Location, + defined_via: ::String + ) -> SchemaElements::DeprecatedElement + @@deprecated_element_new: ::Method + def new_argument: (SchemaElements::Field, ::String, SchemaElements::TypeReference) ?{ (SchemaElements::Argument) -> void } -> SchemaElements::Argument @@argument_new: ::Method diff --git a/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb index 09a734484..fb5a09c27 100644 --- a/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb +++ b/elasticgraph-schema_definition/spec/unit/elastic_graph/schema_definition/schema_elements/deprecated_element_spec.rb @@ -49,14 +49,46 @@ module SchemaElements end it "reports the caller's schema definition location (not ElasticGraph internals) as `defined_at`" do + deleted_type_callsite = nil + renamed_type_callsite = nil + deleted_field_callsite = nil + renamed_field_callsite = nil + state = define_schema(schema_element_name_form: "snake_case") do |schema| + deleted_type_callsite = __LINE__ + 1 + schema.deleted_type "OldType" + schema.object_type "Widget" do |t| + renamed_type_callsite = __LINE__ + 1 t.renamed_from "OldWidget" + + deleted_field_callsite = __LINE__ + 1 + t.deleted_field "legacy_name" + t.field "id", "ID!" + t.field "name", "String" do |f| + renamed_field_callsite = __LINE__ + 1 + f.renamed_from "old_name" + end end end.state - expect(state.renamed_types_by_old_name.fetch("OldWidget").defined_at.path).to eq __FILE__ + expect(state.deleted_types_by_old_name.fetch("OldType").defined_at).to have_attributes( + path: __FILE__, + lineno: deleted_type_callsite + ) + expect(state.renamed_types_by_old_name.fetch("OldWidget").defined_at).to have_attributes( + path: __FILE__, + lineno: renamed_type_callsite + ) + expect(state.deleted_fields_by_type_name_and_old_field_name.fetch("Widget").fetch("legacy_name").defined_at).to have_attributes( + path: __FILE__, + lineno: deleted_field_callsite + ) + expect(state.renamed_fields_by_type_name_and_old_field_name.fetch("Widget").fetch("old_name").defined_at).to have_attributes( + path: __FILE__, + lineno: renamed_field_callsite + ) end end end From eab6512b0c62bfe3fde42a86b7286f401e72bbea Mon Sep 17 00:00:00 2001 From: Josh Wilson Date: Tue, 7 Jul 2026 10:45:14 -0500 Subject: [PATCH 3/3] Return `nil` from schema evolution declaration methods For parity with `schema.deleted_type` (and the other public DSL methods on `API`), make `type.deleted_field`, `type.renamed_from`, and `field.renamed_from` return `nil` instead of leaking the internal `DeprecatedElement` returned by the `register_*` state methods. All three already documented `@return [void]`. --- .../elastic_graph/schema_definition/schema_elements/field.rb | 1 + .../schema_definition/schema_elements/type_with_subfields.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb index 66b64a02e..7f66179d6 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/field.rb @@ -590,6 +590,7 @@ def renamed_from(old_name) defined_at: caller_locations(1, 1).to_a.first, # : ::Thread::Backtrace::Location defined_via: %(field.renamed_from "#{old_name}") ) + nil end # @private diff --git a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb index 7a0dd0ed6..dfc7f5e90 100644 --- a/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb +++ b/elasticgraph-schema_definition/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb @@ -441,6 +441,7 @@ def deleted_field(field_name) defined_at: caller_locations(2, 1).to_a.first, # : ::Thread::Backtrace::Location defined_via: %(type.deleted_field "#{field_name}") ) + nil end # Registers an old name that this type used to have in a prior version of the schema. @@ -465,6 +466,7 @@ def renamed_from(old_name) defined_at: caller_locations(2, 1).to_a.first, # : ::Thread::Backtrace::Location defined_via: %(type.renamed_from "#{old_name}") ) + nil end # Converts the type to GraphQL SDL syntax.