-
Notifications
You must be signed in to change notification settings - Fork 332
fix(rust): preserve inherited fields in union variants #17297
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
base: main
Are you sure you want to change the base?
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 |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| pub use crate::prelude::*; | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] | ||
| #[serde(tag = "event")] | ||
| #[non_exhaustive] | ||
| pub enum PlantEvent { | ||
| #[serde(rename = "sprouted")] | ||
| #[non_exhaustive] | ||
| Sprouted { | ||
| #[serde(flatten)] | ||
| data: SproutedEvent, | ||
| }, | ||
|
|
||
| #[serde(rename = "watered")] | ||
| #[non_exhaustive] | ||
| Watered { | ||
| #[serde(flatten)] | ||
| data: WateredEvent, | ||
| }, | ||
|
|
||
| /// Catch-all variant for unrecognized discriminant values. | ||
| /// If the server sends a discriminant not recognized by the current SDK | ||
| /// version, the raw payload is captured here so callers can still inspect it. | ||
| #[serde(untagged)] | ||
| __Unknown(serde_json::Value), | ||
| } | ||
|
|
||
| impl PlantEvent { | ||
| pub fn sprouted(data: SproutedEvent) -> Self { | ||
| Self::Sprouted { data } | ||
| } | ||
|
|
||
| pub fn watered(data: WateredEvent) -> Self { | ||
| Self::Watered { data } | ||
| } | ||
|
|
||
| pub fn unknown(value: serde_json::Value) -> Self { | ||
| Self::__Unknown(value) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| pub use crate::prelude::*; | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] | ||
| pub struct PlantEventBase { | ||
| #[serde(default)] | ||
| pub occurred_at: String, | ||
| } | ||
|
|
||
| impl PlantEventBase { | ||
| pub fn builder() -> PlantEventBaseBuilder { | ||
| <PlantEventBaseBuilder as Default>::default() | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, PartialEq, Default, Debug)] | ||
| #[non_exhaustive] | ||
| pub struct PlantEventBaseBuilder { | ||
| occurred_at: Option<String>, | ||
| } | ||
|
|
||
| impl PlantEventBaseBuilder { | ||
| pub fn occurred_at(mut self, value: impl Into<String>) -> Self { | ||
| self.occurred_at = Some(value.into()); | ||
| self | ||
| } | ||
|
|
||
| /// Consumes the builder and constructs a [`PlantEventBase`]. | ||
| /// This method will fail if any of the following fields are not set: | ||
| /// - [`occurred_at`](PlantEventBaseBuilder::occurred_at) | ||
| pub fn build(self) -> Result<PlantEventBase, BuildError> { | ||
| Ok(PlantEventBase { | ||
| occurred_at: self.occurred_at.ok_or_else(|| BuildError::missing_field("occurred_at"))?, | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| pub use crate::prelude::*; | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] | ||
| pub struct SproutedEvent { | ||
| #[serde(flatten)] | ||
| pub plant_event_base_fields: PlantEventBase, | ||
| } | ||
|
|
||
| impl SproutedEvent { | ||
| pub fn builder() -> SproutedEventBuilder { | ||
| <SproutedEventBuilder as Default>::default() | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, PartialEq, Default, Debug)] | ||
| #[non_exhaustive] | ||
| pub struct SproutedEventBuilder { | ||
| plant_event_base_fields: Option<PlantEventBase>, | ||
| } | ||
|
|
||
| impl SproutedEventBuilder { | ||
| pub fn plant_event_base_fields(mut self, value: PlantEventBase) -> Self { | ||
| self.plant_event_base_fields = Some(value); | ||
| self | ||
| } | ||
|
|
||
| /// Consumes the builder and constructs a [`SproutedEvent`]. | ||
| /// This method will fail if any of the following fields are not set: | ||
| /// - [`plant_event_base_fields`](SproutedEventBuilder::plant_event_base_fields) | ||
| pub fn build(self) -> Result<SproutedEvent, BuildError> { | ||
| Ok(SproutedEvent { | ||
| plant_event_base_fields: self.plant_event_base_fields.ok_or_else(|| BuildError::missing_field("plant_event_base_fields"))?, | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| pub use crate::prelude::*; | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] | ||
| pub struct WateredEvent { | ||
| #[serde(flatten)] | ||
| pub plant_event_base_fields: PlantEventBase, | ||
| #[serde(default)] | ||
| #[serde(with = "crate::core::number_serializers")] | ||
| pub liters: f64, | ||
| } | ||
|
|
||
| impl WateredEvent { | ||
| pub fn builder() -> WateredEventBuilder { | ||
| <WateredEventBuilder as Default>::default() | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, PartialEq, Default, Debug)] | ||
| #[non_exhaustive] | ||
| pub struct WateredEventBuilder { | ||
| plant_event_base_fields: Option<PlantEventBase>, | ||
| liters: Option<f64>, | ||
| } | ||
|
|
||
| impl WateredEventBuilder { | ||
| pub fn plant_event_base_fields(mut self, value: PlantEventBase) -> Self { | ||
| self.plant_event_base_fields = Some(value); | ||
| self | ||
| } | ||
|
|
||
| pub fn liters(mut self, value: f64) -> Self { | ||
| self.liters = Some(value); | ||
| self | ||
| } | ||
|
|
||
| /// Consumes the builder and constructs a [`WateredEvent`]. | ||
| /// This method will fail if any of the following fields are not set: | ||
| /// - [`plant_event_base_fields`](WateredEventBuilder::plant_event_base_fields) | ||
| /// - [`liters`](WateredEventBuilder::liters) | ||
| pub fn build(self) -> Result<WateredEvent, BuildError> { | ||
| Ok(WateredEvent { | ||
| plant_event_base_fields: self.plant_event_base_fields.ok_or_else(|| BuildError::missing_field("plant_event_base_fields"))?, | ||
| liters: self.liters.ok_or_else(|| BuildError::missing_field("liters"))?, | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json | ||
|
|
||
| - summary: | | ||
| Fix discriminated union variants silently dropping inherited properties. When a variant's | ||
| referenced object type got all of its properties from `extends`, the variant was inlined to | ||
| an empty struct, so the payload deserialized successfully but every field was discarded. | ||
| Such types are no longer inlined and keep their `#[serde(flatten)]` wrapper. | ||
| type: fix | ||
|
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. 🔵 suggestion Two things:
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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.
🔵 suggestion
files.find(...)can returnundefined, and the?.then handsundefinedtotoContain, producing a confusing matcher error rather than "file not generated". Assert the files exist first, and while here consider asserting the inherited field onWateredEventtoo (that's the variant most likely to regress silently since it has an own property).