Class: ElasticGraph::ProtoIngestion::SchemaDefinition::FieldNumberMappings
- Inherits:
-
Object
- Object
- ElasticGraph::ProtoIngestion::SchemaDefinition::FieldNumberMappings
- Defined in:
- elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb
Overview
Registry of the protobuf field and enum value numbers assigned to an ElasticGraph schema.
Parses and validates the numbers stored in the proto_field_numbers.yaml artifact, hands
out the next available numbers for new fields and enum values, and serializes the updated
mappings for the next artifact dump so that numbers stay stable over time.
Constant Summary collapse
- MAX_FIELD_NUMBER =
The largest field number protobuf allows (2^29 - 1), per https://protobuf.dev/programming-guides/proto3/#assigning.
536_870_911- RESERVED_FIELD_NUMBER_RANGE =
Field numbers protobuf reserves for its own implementation; they may not be used as field tags, per https://protobuf.dev/programming-guides/proto3/#assigning.
19_000..19_999
- MAX_ENUM_VALUE_NUMBER =
The largest enum value number protobuf allows (the int32 maximum), per https://protobuf.dev/programming-guides/proto3/#enum.
2_147_483_647- JSON_SCHEMA =
JSON schema for the
proto_field_numbers.yamlartifact. { "$schema" => "http://json-schema.org/draft-07/schema#", "definitions" => { "field_number" => field_number_schema, "next_field_number" => field_number_schema.merge({"maximum" => MAX_FIELD_NUMBER + 1}), "enum_value_number" => enum_value_number_schema, "next_enum_value_number" => enum_value_number_schema.merge({"maximum" => MAX_ENUM_VALUE_NUMBER + 1}) }, "type" => "object", "properties" => { "messages" => { "type" => "object", "additionalProperties" => { "type" => "object", "properties" => { "fields" => { "type" => "object", "additionalProperties" => {"$ref" => "#/definitions/field_number"} }, "next_number" => {"$ref" => "#/definitions/next_field_number"} }, "required" => ["fields", "next_number"], "additionalProperties" => false } }, "enums" => { "type" => "object", "additionalProperties" => { "type" => "object", "properties" => { "values" => { "type" => "object", "additionalProperties" => {"$ref" => "#/definitions/enum_value_number"} }, "next_number" => {"$ref" => "#/definitions/next_enum_value_number"} }, "required" => ["values", "next_number"], "additionalProperties" => false } } }, "additionalProperties" => false }
Class Method Summary collapse
-
.from_parsed_yaml(parsed_yaml) ⇒ FieldNumberMappings
Builds an instance from parsed
proto_field_numbers.yaml, validating its structure and every mapped number.
Instance Method Summary collapse
-
#enum_value_numbers_for(enum_name, value_names) ⇒ Hash<String, Integer>
Returns the stable protobuf numbers for an enum's values, assigning the next available numbers to values that have no stored mapping.
-
#field_number_for(message_name:, public_field_name:, previous_field_names:) ⇒ Integer
Returns the stable protobuf number for a message field, assigning the message's stored
next_numberif the field has no mapping. -
#initialize(message_mappings_by_name:, enum_mappings_by_name:) ⇒ FieldNumberMappings
constructor
private
A new instance of FieldNumberMappings.
-
#next_enum_value_number_for(enum_name) ⇒ Integer
Returns the next value number that will be assigned for the given enum.
-
#next_field_number_for(message_name) ⇒ Integer
Returns the next field number that will be assigned for the given message.
-
#reserved_enum_value_numbers_for(enum_name, active_value_names) ⇒ Hash<String, Integer>
Returns value names and numbers retained in the mappings but absent from the enum.
-
#reserved_field_numbers_for(message_name, active_field_names) ⇒ Hash<String, Integer>
Returns field names and numbers retained in the mappings but absent from the message.
-
#to_dumpable_hash ⇒ Hash<String, Object>
Serializes the mappings back to the
proto_field_numbers.yamlartifact format, with messages and enums sorted by name and their fields and values sorted by number.
Constructor Details
#initialize(message_mappings_by_name:, enum_mappings_by_name:) ⇒ FieldNumberMappings
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of FieldNumberMappings.
144 145 146 147 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 144 def initialize(message_mappings_by_name:, enum_mappings_by_name:) @message_mappings_by_name = @enum_mappings_by_name = enum_mappings_by_name end |
Class Method Details
.from_parsed_yaml(parsed_yaml) ⇒ FieldNumberMappings
Builds an instance from parsed proto_field_numbers.yaml, validating its structure and
every mapped number.
127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 127 def self.from_parsed_yaml(parsed_yaml) parsed_yaml ||= {} # : ::Hash[::String, untyped] if (validation_error = VALIDATOR.(parsed_yaml)) raise Errors::SchemaError, "Invalid protobuf field-number mappings:\n\n#{validation_error}" end empty_section = {} # : ::Hash[::String, untyped] new( message_mappings_by_name: (parsed_yaml.fetch("messages", empty_section)), enum_mappings_by_name: parse_enums(parsed_yaml.fetch("enums", empty_section)) ) end |
Instance Method Details
#enum_value_numbers_for(enum_name, value_names) ⇒ Hash<String, Integer>
Returns the stable protobuf numbers for an enum's values, assigning the next available numbers to values that have no stored mapping.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 211 def enum_value_numbers_for(enum_name, value_names) enum_mapping = enum_mapping_for(enum_name) value_numbers = enum_mapping.value_numbers_by_name new_value_names = value_names - value_numbers.keys next_available_number = enum_mapping.next_number next_available_number_after_allocations = next_available_number + new_value_names.size if next_available_number_after_allocations > MAX_ENUM_VALUE_NUMBER + 1 raise Errors::SchemaError, "Cannot allocate another protobuf enum value number for enum `#{enum_name}`: " \ "the maximum enum value number (#{MAX_ENUM_VALUE_NUMBER}) has been reached." end new_value_numbers = new_value_names.each_with_index.to_h do |value_name, index| [value_name, next_available_number + index] end updated_value_numbers = value_numbers.merge(new_value_numbers) updated_mapping = enum_mapping.with( value_numbers_by_name: updated_value_numbers, next_number: next_available_number_after_allocations ) @enum_mappings_by_name = @enum_mappings_by_name.merge(enum_name => updated_mapping) value_names.to_h do |value_name| [value_name, updated_value_numbers.fetch(value_name)] end end |
#field_number_for(message_name:, public_field_name:, previous_field_names:) ⇒ Integer
Returns the stable protobuf number for a message field, assigning the message's stored
next_number if the field has no mapping. When the field was renamed, the mapping
stored under one of its previous_field_names (and its number) carries over.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 157 def field_number_for(message_name:, public_field_name:, previous_field_names:) = () field_numbers = .field_numbers_by_name return field_numbers.fetch(public_field_name) if field_numbers.key?(public_field_name) old_field_names = previous_field_names.intersection(field_numbers.keys) if old_field_names.size > 1 formatted_old_field_names = old_field_names.sort.map { |name| "`#{name}`" }.join(" and ") raise Errors::SchemaError, "Cannot preserve a protobuf field number for `#{}.#{public_field_name}`: " \ "multiple previous field names have mappings (#{formatted_old_field_names}). A field can preserve only one " \ "protobuf number; use `renamed_from` for the name whose number should carry over and `deleted_field` for the others." end old_field_name = old_field_names.first updated_mapping = if old_field_name .with( field_numbers_by_name: field_numbers .except(old_field_name) .merge(public_field_name => field_numbers.fetch(old_field_name)) ) else allocate_field_number(, public_field_name, ) end @message_mappings_by_name = @message_mappings_by_name.merge( => updated_mapping) updated_mapping.field_numbers_by_name.fetch(public_field_name) end |
#next_enum_value_number_for(enum_name) ⇒ Integer
Returns the next value number that will be assigned for the given enum.
242 243 244 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 242 def next_enum_value_number_for(enum_name) enum_mapping_for(enum_name).next_number end |
#next_field_number_for(message_name) ⇒ Integer
Returns the next field number that will be assigned for the given message.
191 192 193 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 191 def next_field_number_for() ().next_number end |
#reserved_enum_value_numbers_for(enum_name, active_value_names) ⇒ Hash<String, Integer>
Returns value names and numbers retained in the mappings but absent from the enum.
251 252 253 254 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 251 def reserved_enum_value_numbers_for(enum_name, active_value_names) value_numbers = enum_mapping_for(enum_name).value_numbers_by_name reserved_numbers_by_name(value_numbers, active_value_names) end |
#reserved_field_numbers_for(message_name, active_field_names) ⇒ Hash<String, Integer>
Returns field names and numbers retained in the mappings but absent from the message.
200 201 202 203 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 200 def reserved_field_numbers_for(, active_field_names) field_numbers = ().field_numbers_by_name reserved_numbers_by_name(field_numbers, active_field_names) end |
#to_dumpable_hash ⇒ Hash<String, Object>
Serializes the mappings back to the proto_field_numbers.yaml artifact format, with
messages and enums sorted by name and their fields and values sorted by number.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/field_number_mappings.rb', line 260 def to_dumpable_hash { "messages" => @message_mappings_by_name .sort_by(&:first) .to_h do |, | [, { "fields" => .field_numbers_by_name.sort_by { |field_name, number| [number, field_name] }.to_h, "next_number" => .next_number }] end, "enums" => @enum_mappings_by_name .sort_by(&:first) .to_h do |enum_name, enum_mapping| [enum_name, { "values" => enum_mapping.value_numbers_by_name.sort_by { |value_name, number| [number, value_name] }.to_h, "next_number" => enum_mapping.next_number }] end } end |