-
Notifications
You must be signed in to change notification settings - Fork 78
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
any of being used where it should just be an enum of strings #126
Comments
I'm not sure if I understand this precisely, but I think we're talking about types such as this one: /**
* State of a Disk (primarily: attached or not)
*/
#[derive(JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum DiskState {
/** Disk is being initialized */
Creating,
/** Disk is ready but detached from any Instance */
Detached,
/** Disk is being attached to the given Instance */
Attaching(Uuid), /* attached Instance id */
/** Disk is attached to the given Instance */
Attached(Uuid), /* attached Instance id */
/** Disk is being detached from the given Instance */
Detaching(Uuid), /* attached Instance id */
/** Disk has been destroyed */
Destroyed,
/** Disk is unavailable */
Faulted,
} Which {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DiskState",
"description": "State of a Disk (primarily: attached or not)",
"anyOf": [
{
"type": "string",
"enum": [
"creating",
"detached",
"destroyed",
"faulted"
]
},
{
"description": "Disk is being attached to the given Instance",
"type": "object",
"required": [
"attaching"
],
"properties": {
"attaching": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
{
"description": "Disk is attached to the given Instance",
"type": "object",
"required": [
"attached"
],
"properties": {
"attached": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
{
"description": "Disk is being detached from the given Instance",
"type": "object",
"required": [
"detaching"
],
"properties": {
"detaching": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
}
]
} In this case, the FWIW I once found some odd output from |
Yeah I’m mostly saying the open api spec can denote this as an enumeration which makes this a lot easier on all the client generators going forward. We can fork schemers and fix it
… On Aug 17, 2021, at 8:27 PM, Adam Leventhal ***@***.***> wrote:
I'm not sure if I understand this precisely, but I think we're talking about types such as this one:
/**
* State of a Disk (primarily: attached or not)
*/
#[derive(JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum DiskState {
/** Disk is being initialized */
Creating,
/** Disk is ready but detached from any Instance */
Detached,
/** Disk is being attached to the given Instance */
Attaching(Uuid), /* attached Instance id */
/** Disk is attached to the given Instance */
Attached(Uuid), /* attached Instance id */
/** Disk is being detached from the given Instance */
Detaching(Uuid), /* attached Instance id */
/** Disk has been destroyed */
Destroyed,
/** Disk is unavailable */
Faulted,
}
Which schemars turns into this:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DiskState",
"description": "State of a Disk (primarily: attached or not)",
"anyOf": [
{
"type": "string",
"enum": [
"creating",
"detached",
"destroyed",
"faulted"
]
},
{
"description": "Disk is being attached to the given Instance",
"type": "object",
"required": [
"attaching"
],
"properties": {
"attaching": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
{
"description": "Disk is attached to the given Instance",
"type": "object",
"required": [
"attached"
],
"properties": {
"attached": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
{
"description": "Disk is being detached from the given Instance",
"type": "object",
"required": [
"detaching"
],
"properties": {
"detaching": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
}
]
}
In this case, the anyOf seems important in that different enum values have associated types (i.e. the Uuids), and some of the enum values have additional descriptions (although it does seem like a bug that schemars omits those descriptions for simple values. Do you have a proposal for how we could simplify this type? Or are you talking about a different sort of type?
FWIW I once found some odd output from schemars only to discover it was a shortcoming of the spec: GREsau/schemars#42
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
It’s one of those things that will piss off people who want to do everything by spec but like if we keep this the way it is these will become overly complex enums of objects in not only my client generators but golang was well, sure I can hack my generator to account for this weird oddity but it’s a bad smell to me I’ve not seen this weird ass behavior elsewhere in the wild. Like why make our lives harder if we don’t have to
… On Aug 17, 2021, at 8:58 PM, Jess Frazelle ***@***.***> wrote:
Yeah I’m mostly saying the open api spec can denote this as an enumeration which makes this a lot easier on all the client generators going forward. We can fork schemers and fix it
> On Aug 17, 2021, at 8:27 PM, Adam Leventhal ***@***.***> wrote:
>
>
> I'm not sure if I understand this precisely, but I think we're talking about types such as this one:
>
> /**
> * State of a Disk (primarily: attached or not)
> */
> #[derive(JsonSchema)]
> #[serde(rename_all = "lowercase")]
> pub enum DiskState {
> /** Disk is being initialized */
> Creating,
> /** Disk is ready but detached from any Instance */
> Detached,
> /** Disk is being attached to the given Instance */
> Attaching(Uuid), /* attached Instance id */
> /** Disk is attached to the given Instance */
> Attached(Uuid), /* attached Instance id */
> /** Disk is being detached from the given Instance */
> Detaching(Uuid), /* attached Instance id */
> /** Disk has been destroyed */
> Destroyed,
> /** Disk is unavailable */
> Faulted,
> }
> Which schemars turns into this:
>
> {
> "$schema": "http://json-schema.org/draft-07/schema#",
> "title": "DiskState",
> "description": "State of a Disk (primarily: attached or not)",
> "anyOf": [
> {
> "type": "string",
> "enum": [
> "creating",
> "detached",
> "destroyed",
> "faulted"
> ]
> },
> {
> "description": "Disk is being attached to the given Instance",
> "type": "object",
> "required": [
> "attaching"
> ],
> "properties": {
> "attaching": {
> "type": "string",
> "format": "uuid"
> }
> },
> "additionalProperties": false
> },
> {
> "description": "Disk is attached to the given Instance",
> "type": "object",
> "required": [
> "attached"
> ],
> "properties": {
> "attached": {
> "type": "string",
> "format": "uuid"
> }
> },
> "additionalProperties": false
> },
> {
> "description": "Disk is being detached from the given Instance",
> "type": "object",
> "required": [
> "detaching"
> ],
> "properties": {
> "detaching": {
> "type": "string",
> "format": "uuid"
> }
> },
> "additionalProperties": false
> }
> ]
> }
> In this case, the anyOf seems important in that different enum values have associated types (i.e. the Uuids), and some of the enum values have additional descriptions (although it does seem like a bug that schemars omits those descriptions for simple values. Do you have a proposal for how we could simplify this type? Or are you talking about a different sort of type?
>
> FWIW I once found some odd output from schemars only to discover it was a shortcoming of the spec: GREsau/schemars#42
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub, or unsubscribe.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
I haven't looked at that many cases, but I have found that cases that can be denoted strictly as an enum seem to be represented that way already (at least with |
I thought it was funny you knew it was disk state as that’s the one that’s failing for me :) I do know it can be represented just fine in the open api spec as an enum so maybe it’s just doing a custom conversion for that case. The guy mentioned conforming to the json schema spec but we don’t really care about that we just care about open api
… On Aug 17, 2021, at 9:09 PM, Adam Leventhal ***@***.***> wrote:
I haven't looked at that many cases, but I have found that cases that can be denoted strictly as an enum seem to be represented that way already (at least with schemars 0.8.3). Is DiskState above one of the types you'd like to see emitted without the anyOf? How would we represent it?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
Well, https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schemaObject And they've gotten even closer in 3.1 (not that that matters yet / ever) https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schemaObject I don't see how |
Yeah what I have seen other specs do is a string enum then the strings are
references and they store the data in the reference. Or even simpler you
throw all the docs into the main enum doc string:
https://docs.rs/octorust/0.1.25/octorust/types/enum.Filter.html
uuid is just a string type. schmars is kinda making it more complex since
its a unit struct in rust but really in openapi land its just a string with
format uuid. making these all their own objects with a single property
would make the conversion really gross.
```
pub enum ThingAnyOf {
OurActualEnumOfStrings
SomeObjectThatsReallyJustAString
AnotherObjectThatsReallyJustAString
}
pub enum OurActualEnumOfStrings {
String1
String2
String3
}
pub struct SomeObjectThatsReallyJustAString {
our_actual_string: string,
}
pub struct AnotherObjectThatsReallyJustAString {
our_actual_string: string,
}
```
Like that's what my library interprets your spec as because that's what the
spec is telling it
…On Wed, Aug 18, 2021 at 9:45 AM Adam Leventhal ***@***.***> wrote:
Well, schemars just does JSON Schema. But OpenAPI hands off most of the
definition of their schema object to JSON Schema:
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schemaObject
And they've gotten even closer in 3.1 (not that that matters yet / ever)
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schemaObject
I don't see how enum can be used to address these enum variants that
include additional data, but I'd love to see it simplify of course!
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#126 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALA23HAU4KLRJJZFL577R3T5PPRRANCNFSM5CLCGHQA>
.
|
And I have no way of knowing from the spec that these should be untagged, otherwise the API when called will never match on anything. And if I set them all as untagged enums well that would fuck up all the other specs I generate, and sure I can make a special case for oxide but I just feel like this is going to lead us to a world of pain with every other language we want to generate as well. |
Honestly just getting rid of the weird object types with a single property would help a lot because then at least this would parse as strings. |
hmm it might work because I do have anyof and allof as untagged but its just gross. and they need titles for the objects or else I have to generate one and it comes out like |
mostly unless you have better ideas its nearly impossible for me to go from that schema back to the original unless I do a bunch of weirdo logic to make sure all these objects only have one value... |
let me try it and see if that breaks others, in theory it shouldnt |
Would it help us to constrain the enum types which could be exposed over this interface? That might limit the number of cases a potential generator would need to handle. Rust enums can be both named and unnamed: enum MyEnum {
A, // Unit variant
B(i32), // Unnamed field
C { value: i32 }, // Named field
} I care pretty strongly that we are able to associate data with a single variant, but there is definitely some flexibility in how we go about doing so. |
@smklein this might be a good one to discuss. Without any serde annotations related to enum tags, the enum above would serialize like this: {
"my_enum1": "a",
"my_enum2": { "b": 123 },
"my_enum3": { "c": { "value": 123 } }
} (This is referred to in the serde docs as "externally tagged") I'll argue that that's kind of janky in json and in other, non-rust, languages. In particular, I'd suggest that a given value have a single valid data type whereas I think that serde adjacent tagging provides a better interface: {
"my_enum1": { "tag": "a" },
"my_enum2": { "tag": "b", "content": 123 },
"my_enum3": { "tag": "c", "content": { "value": 123 } }
} I think that's a nicer interface. In the case of A related topic that Jess and I discussed was an openapi-lint crate that we could invoke from tests. The only two lints that I've got written down are both to validate type consistency:
|
Yeah, definitely agreed - the distinction of name / data is much more explicit with the tagging, and adding this tagging would be non-invasive for our Rust backend, so it's pretty much "free".
We... can. If there aren't any good solutions in this space, I'd relent, but this comes with a cost. In the case of Consider another enum (full caveat, I made this one up): pub enum InstanceState {
Creating(Progress)
Starting,
Running(RunState),
Stopping,
Failed(Reason)
...
} In this case, would we need |
Cool. I imagine this lint tool would catch this and could advise that adjacent tagging is a good solution.
Totally fair. I don't feel strongly that one way is clearly always correct. We might just keep an eye out for situations that make clients awkward to write. For example, when the console displays information about a disk, will the associated instance (via a UUID, link, or some other info) be a field that's always visible or will it be part of the status. |
The new lint is now enforcing that the arms of an
With |
@david-crespo apologies for the delayed response on this. Agreed that that's the case. I would have hoped that I could imagine a few fixes:
Number 1 is my preferred choice. I think I'd prefer 5 to 2 i.e. if we're going to maintain our own thing I'd prefer to maintain the thing that's closer to what we actually want and avoid the conversion from If you have preferences or other options please let me know. |
I agree that 1 is best! 2 and 5 sound like a lot of work. 3 and 4 seem about equal to me — if it's a 20 line function to post-process the generated schema, it doesn't seem too important to me whether it goes in Dropshot or Nexus, though it does seem pretty useful to users of Dropshot to have |
|
this is fixed in schemars 0.8.6 |
So I plan on fixing this.
But I've generated a bunch of API clients now and seen a lot of specs and as a matter of making our future life easier we need to fix how we are generating these. We are using
AnyOf
when what we really want is just a simple enum of strings. We can still have descriptions we can still make thingsuuids
, but now we won't have the weird "objects" with one property in it. That's just noise and its going to be hard to make any nice client from it.Expect a PR.
The text was updated successfully, but these errors were encountered: