-
Notifications
You must be signed in to change notification settings - Fork 35
Source enum values from external proto enums and reference external enum types #1286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -218,6 +218,108 @@ Each call to `protobuf` replaces the full protobuf configuration. The override a | |||||||||||||||||
| `import:`, so `schema.proto` no longer imports `google/protobuf/timestamp.proto`. An override that | ||||||||||||||||||
| omits `field_comment:` likewise drops the built-in comment. | ||||||||||||||||||
|
|
||||||||||||||||||
| ### Sourcing Enum Values From Existing Protobuf Enums | ||||||||||||||||||
|
|
||||||||||||||||||
| If your project already has canonical protobuf enum definitions, you can source an enum's | ||||||||||||||||||
| generated proto values from them instead of maintaining the value list in two places. Call | ||||||||||||||||||
| `external_proto_enum` on the enum type with a proto enum class (anything exposing `.enums`). | ||||||||||||||||||
| In the examples below, this stand-in plays the role of your app's generated proto enum class: | ||||||||||||||||||
|
|
||||||||||||||||||
| ```ruby | ||||||||||||||||||
| # in config/schema/app_protos.rb | ||||||||||||||||||
|
|
||||||||||||||||||
| # Stands in for a proto enum class generated by your app's protobuf tooling. | ||||||||||||||||||
| module MyApp | ||||||||||||||||||
| module Protos | ||||||||||||||||||
| EnumEntry = ::Data.define(:name, :number) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Its value names carry a prefix and include a sentinel, so it needs a transform and an | ||||||||||||||||||
| # exclusion to line up with an ElasticGraph enum. | ||||||||||||||||||
| class Currency | ||||||||||||||||||
| def self.enums | ||||||||||||||||||
| [ | ||||||||||||||||||
| EnumEntry.new(name: :CURRENCY_UNKNOWN_DO_NOT_USE, number: 0), | ||||||||||||||||||
| EnumEntry.new(name: :CURRENCY_USD, number: 1), | ||||||||||||||||||
| EnumEntry.new(name: :CURRENCY_CAD, number: 2) | ||||||||||||||||||
| ] | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| # Its value names already match the ElasticGraph enum exactly, so it can be referenced | ||||||||||||||||||
| # directly rather than regenerated locally. | ||||||||||||||||||
| class CurrencyCode | ||||||||||||||||||
| def self.enums | ||||||||||||||||||
| [ | ||||||||||||||||||
| EnumEntry.new(name: :USD, number: 1), | ||||||||||||||||||
| EnumEntry.new(name: :CAD, number: 2) | ||||||||||||||||||
| ] | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| Optional per-source options curate and transform the sourced values: | ||||||||||||||||||
|
|
||||||||||||||||||
| ```ruby | ||||||||||||||||||
| # in config/schema/currency.rb | ||||||||||||||||||
|
|
||||||||||||||||||
| ElasticGraph.define_schema do |schema| | ||||||||||||||||||
| schema.enum_type "Currency" do |t| | ||||||||||||||||||
| t.values "USD", "CAD" | ||||||||||||||||||
| t.external_proto_enum MyApp::Protos::Currency, | ||||||||||||||||||
| # Proto values to omit from the generated enum. | ||||||||||||||||||
| exclusions: [:UNKNOWN_DO_NOT_USE], | ||||||||||||||||||
| # Values expected in the generated enum that the proto enum lacks. | ||||||||||||||||||
| expected_extras: [:LEGACY], | ||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||
| # Optional transform applied to each proto value name. | ||||||||||||||||||
| name_transform: ->(name) { name.delete_prefix("CURRENCY_") } | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| `name_transform` runs first, and `exclusions` and `expected_extras` then apply to the | ||||||||||||||||||
| transformed names. In the example above the exclusion is therefore `UNKNOWN_DO_NOT_USE`, the | ||||||||||||||||||
| name left after the transform strips `CURRENCY_`, rather than `CURRENCY_UNKNOWN_DO_NOT_USE`. | ||||||||||||||||||
|
|
||||||||||||||||||
| When an enum has one or more external sources, `elasticgraph-proto_ingestion` uses them | ||||||||||||||||||
| as the source of the generated enum's values. When multiple sources are registered for the | ||||||||||||||||||
| same enum, they must all resolve to the same value set. | ||||||||||||||||||
|
|
||||||||||||||||||
| ### Referencing Existing Protobuf Types | ||||||||||||||||||
|
|
||||||||||||||||||
| For enums that exactly match a canonical proto enum, you can go further and reference the | ||||||||||||||||||
| existing proto type instead of generating a duplicate local enum. Pass `proto:` and `import:` | ||||||||||||||||||
| to `external_proto_enum`, and `schema.proto` will import the named file and use the external | ||||||||||||||||||
| type name directly: | ||||||||||||||||||
|
|
||||||||||||||||||
| ```ruby | ||||||||||||||||||
| # in config/schema/currency.rb | ||||||||||||||||||
|
|
||||||||||||||||||
| ElasticGraph.define_schema do |schema| | ||||||||||||||||||
| schema.enum_type "CurrencyCode" do |t| | ||||||||||||||||||
| t.values "USD", "CAD" | ||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is |
||||||||||||||||||
| t.external_proto_enum MyApp::Protos::CurrencyCode, | ||||||||||||||||||
| proto: "myapp.types.CurrencyCode", | ||||||||||||||||||
| import: "myapp/types/currency_code.proto" | ||||||||||||||||||
| end | ||||||||||||||||||
| end | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| A referenced enum must have exactly one `external_proto_enum` call that passes no | ||||||||||||||||||
| `exclusions:`, `expected_extras:`, or `name_transform:`, whose values match the | ||||||||||||||||||
| ElasticGraph enum's values; transformed, curated, or multi-source enums stay generated | ||||||||||||||||||
| locally. Note that `MyApp::Protos::Currency` from the | ||||||||||||||||||
| previous section cannot be referenced this way: its `CURRENCY_`-prefixed names only match | ||||||||||||||||||
| after a transform, and referenced enums allow no transform. | ||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand this last sentence. Also, I thought that EG natively generates enum values with a prefix like Lines 67 to 74 in b98f719
Given that, why is the |
||||||||||||||||||
|
|
||||||||||||||||||
| The source's enum entries must also expose `.number`. Those numbers are recorded in | ||||||||||||||||||
| `proto_field_numbers.yaml`, and must agree with any numbers already pinned there — otherwise | ||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're referencing an existing proto enum, then we should treat it as the canonical source of truth for the enum value numbers. Recording them in |
||||||||||||||||||
| switching to the external type would silently reinterpret existing wire data. Recording them | ||||||||||||||||||
| also means that dropping `proto:`/`import:` later regenerates the enum locally with its | ||||||||||||||||||
| original numbers rather than renumbering it. No referenced value may use number 0, which is | ||||||||||||||||||
| reserved for the zero value this gem generates for a local enum. | ||||||||||||||||||
|
|
||||||||||||||||||
| ## Type Mappings | ||||||||||||||||||
|
|
||||||||||||||||||
| The generated `schema.proto` uses these built-in scalar mappings: | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above you say:
Based on that, I thought the point of this was to not need to define the values--instead, it would get them from the externally defined proto.
Am I misunderstanding what this is for? Or should the
t.valuesbe removed here?