-
Notifications
You must be signed in to change notification settings - Fork 40
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
Generated client for ExternalIpCreate
feels clunky
#1501
Comments
I think this is due to interaction with the generator, either because we're using an externally-tagged type or how the generator consumes them. The native type definition is indeed what you were hoping to write. The tag-name Also, I recall some discussion a while ago about generating enum representations in a way that feels at least passable in a number of languages. All I can find at the moment is #573, but @ahl might have a better memory for that. |
Progenitor usually does a good job of reconstructing enums. This is a case I hadn't considered: an enum with a single variant. Here's what the OpenAPI JSON looks like: "ExternalIpCreate": {
"description": "Parameters for creating an external IP address for instances.",
"oneOf": [
{
"description": "An IP address providing both inbound and outbound access. The address is automatically-assigned from the provided IP Pool, or all available pools if not specified.",
"type": "object",
"properties": {
"pool_name": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/Name"
}
]
},
"type": {
"type": "string",
"enum": [
"ephemeral"
]
}
},
"required": [
"type"
]
}
]
}, For quite awhile, typify has made the assumption that a lone subschema can just be treated as a wrapper around the subordinate schema. This is fairly common construction... we even see it above with the We can fix this by first seeing if the type fits one of our more attractive enum types (i.e. not Note however that these two enums emit the same JSON schema: #[derive(JsonSchema)]
#[serde(tag = "type")] // internal tagging
pub enum ExternalIpCreate {
Ephemeral { pool_name: Option<Name> },
}
#[derive(JsonSchema)]
#[serde(tag = "type", content = "pool_name")] // adjacent tagging
pub enum ExternalIpCreate {
Ephemeral(String),
} Typify needs to prefer one or the other when interpreting the JSON schema, and there's a clear choice because adjacent tagging is a strict subset of internal tagging! Consider this enum: #[derive(JsonSchema)]
#[serde(tag = "who", content = "what")]
pub enum Friend {
Bert(String),
Ernie(String),
} If we were to prefer to interpret this with internal tagging, we'd reconstruct this: #[derive(JsonSchema)]
#[serde(tag = "who")]
Bert { what: String },
Ernie { what: String },
} ... which would be gross. This fix for this needs to go into typify and then percolate to progenitor and then omicron. |
Here's the original: /// Parameters for creating an external IP address for instances.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ExternalIpCreate {
/// An IP address providing both inbound and outbound access. The address is
/// automatically-assigned from the provided IP Pool, or all available pools
/// if not specified.
Ephemeral { pool_name: Option<Name> },
// TODO: Add floating IPs: https://github.com/oxidecomputer/omicron/issues/1334
} With the fixes applied, here's how it comes out: ///Parameters for creating an external IP address for instances.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum ExternalIpCreate {
///An IP address providing both inbound and outbound access. The
/// address is automatically-assigned from the provided IP Pool, or all
/// available pools if not specified.
#[serde(rename = "ephemeral")]
Ephemeral {
#[serde(default, skip_serializing_if = "Option::is_none")]
pool_name: Option<Name>,
},
} Everyone happy? |
Before expending any effort, I should make clear that this enum will soon have more than one variant. I can add it now, and have the server error out, if it makes the generated client better in the meantime.
… On 26 Jul 2022, at 20:37, Adam Leventhal ***@***.***> wrote:
Progenitor usually does a good job of reconstructing enums. This is a case I hadn't considered: an enum with a single variant. Here's what the OpenAPI JSON looks like:
"ExternalIpCreate": {
"description": "Parameters for creating an external IP address for instances.",
"oneOf": [
{
"description": "An IP address providing both inbound and outbound access. The address is automatically-assigned from the provided IP Pool, or all available pools if not specified.",
"type": "object",
"properties": {
"pool_name": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/Name"
}
]
},
"type": {
"type": "string",
"enum": [
"ephemeral"
]
}
},
"required": [
"type"
]
}
]
},
For quite awhile, typify has made the assumption that a lone subschema can just be treated as a wrapper around the subordinate schema. This is fairly common construction... we even see it above with the $ref for Name!
We can fix this by first seeing if the type fits one of our more attractive enum types (i.e. not untagged) and only failing that, seeing if there's a singleton subschema.
Note however that these two enums emit the same JSON schema:
#[derive(JsonSchema)]
#[serde(tag = "type")] // internal tagging
pub enum ExternalIpCreate {
Ephemeral { pool_name: Option<Name> },
}
#[derive(JsonSchema)]
#[serde(tag = "type", content = "pool_name")] // adjacent tagging
pub enum ExternalIpCreate {
Ephemeral(String),
}
Typify needs to prefer one or the other when interpreting the JSON schema, and there's a clear choice because adjacent tagging is a strict subset of internal tagging! Consider this enum:
#[derive(JsonSchema)]
#[serde(tag = "who", content = "what")]
pub enum Friend {
Bert(String),
Ernie(String),
}
If we were to prefer to interpret this with internal tagging, we'd reconstruct this:
#[derive(JsonSchema)]
#[serde(tag = "who")]
Bert { what: String },
Ernie { what: String },
}
... which would be gross.
This fix for this needs to go into typify and then percolate to progenitor and then omicron.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.
|
Meh... turns out Digest had this problem too: before pub struct Digest {
#[serde(rename = "type")]
pub type_: DigestType,
pub value: String,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub enum DigestType {
#[serde(rename = "sha256")]
Sha256,
} after #[serde(tag = "type", content = "value")]
pub enum Digest {
#[serde(rename = "sha256")]
Sha256(String),
} |
Fair enough! Glad it was neither a lot of work, nor a one-off for this type. It seems like we'll resolve this with the merger of #1502, correct? Thanks Adam. |
Crucible changes Add test and fix for replay race condition (#1519) Fix clippy warnings (#1517) Add edition to `crucible-workspace-hack` (#1516) Split out Downstairs-specific stats (#1511) Move remaining `GuestWork` functionality into `Downstairs` (#1510) Track both jobs and bytes in each IO state (#1507) Fix new `rustc` and `clippy` warnings (#1509) Remove IOP/BW limits (for now) (#1506) Move `GuestBlockRes` into `DownstairsIO` (#1502) Update actions/checkout digest to eef6144 (#1499) Update Rust crate hyper-staticfile to 0.10.1 (#1411) Turn off test-up-2region-encrypted.sh (#1504) Add `IOop::Barrier` (#1494) Fix IPv6 addresses in `crutest` (#1503) Add region set options to more tests. (#1496) Simplify `CompleteJobs` (#1493) Removed ignored CI jobs (#1497) Minor cleanups to `print_last_completed` (#1501) Remove remaining `Arc<Volume>` instances (#1500) Add `VolumeBuilder` type (#1492) remove old unused scripts (#1495) More multiple region support. (#1484) Simplify matches (#1490) Move complete job tracker to a helper object (#1489) Expand summary and add documentation references to the README. (#1486) Remove `GuestWorkId` (2/2) (#1482) Remove `JobId` from `DownstairsIO` (1/2) (#1481) Remove unused `#[derive(..)]` (#1483) Update more tests to use dsc (#1480) Crutest now Volume only (#1479) Propolis changes manually impl Deserialize for PciPath for validation purposes (#801) phd: gate OS-specific tests, make others more OS-agnostic (#799) lib: log vCPU diagnostics on triple fault and for some unhandled exit types (#795) add marker trait to help check safety of guest memory reads (#794) clippy fixes for 1.82 (#796) lib: move cpuid::Set to cpuid_utils; prevent semantic subleaf conflicts (#782) PHD: write efivars in one go (#786) PHD: support guest-initiated reboot (#785) server: accept CPUID values in instance specs and plumb them to bhyve (#780) PHD: allow patched Crucible dependencies (#778) server: add a first-class error type to machine init (#777) PciPath to Bdf conversion is infallible; prove it and refactor (#774) instance spec rework: flatten InstanceSpecV0 (#767) Make PUT /instance/state 503 when waiting to init Less anxiety-inducing `Vm::{get, state_watcher}`
Crucible changes Add test and fix for replay race condition (#1519) Fix clippy warnings (#1517) Add edition to `crucible-workspace-hack` (#1516) Split out Downstairs-specific stats (#1511) Move remaining `GuestWork` functionality into `Downstairs` (#1510) Track both jobs and bytes in each IO state (#1507) Fix new `rustc` and `clippy` warnings (#1509) Remove IOP/BW limits (for now) (#1506) Move `GuestBlockRes` into `DownstairsIO` (#1502) Update actions/checkout digest to eef6144 (#1499) Update Rust crate hyper-staticfile to 0.10.1 (#1411) Turn off test-up-2region-encrypted.sh (#1504) Add `IOop::Barrier` (#1494) Fix IPv6 addresses in `crutest` (#1503) Add region set options to more tests. (#1496) Simplify `CompleteJobs` (#1493) Removed ignored CI jobs (#1497) Minor cleanups to `print_last_completed` (#1501) Remove remaining `Arc<Volume>` instances (#1500) Add `VolumeBuilder` type (#1492) remove old unused scripts (#1495) More multiple region support. (#1484) Simplify matches (#1490) Move complete job tracker to a helper object (#1489) Expand summary and add documentation references to the README. (#1486) Remove `GuestWorkId` (2/2) (#1482) Remove `JobId` from `DownstairsIO` (1/2) (#1481) Remove unused `#[derive(..)]` (#1483) Update more tests to use dsc (#1480) Crutest now Volume only (#1479) Propolis changes manually impl Deserialize for PciPath for validation purposes (#801) phd: gate OS-specific tests, make others more OS-agnostic (#799) lib: log vCPU diagnostics on triple fault and for some unhandled exit types (#795) add marker trait to help check safety of guest memory reads (#794) clippy fixes for 1.82 (#796) lib: move cpuid::Set to cpuid_utils; prevent semantic subleaf conflicts (#782) PHD: write efivars in one go (#786) PHD: support guest-initiated reboot (#785) server: accept CPUID values in instance specs and plumb them to bhyve (#780) PHD: allow patched Crucible dependencies (#778) server: add a first-class error type to machine init (#777) PciPath to Bdf conversion is infallible; prove it and refactor (#774) instance spec rework: flatten InstanceSpecV0 (#767) Make PUT /instance/state 503 when waiting to init Less anxiety-inducing `Vm::{get, state_watcher}` --------- Co-authored-by: Alan Hanson <alan@oxide.computer>
type_
is awkward here. What I would expect is to be able to write something likeExternalIpCreate::Ephemeral { pool_name: None }
; is the pool name relevant for all future kinds of external IP creation objects?If we want to leave the shape the same I would recommend renaming type to kind.
cc @bnaecker
The text was updated successfully, but these errors were encountered: