Comments (7)
Precedence for geneating types using schema composition
Background
- https://spec.openapis.org/oas/v3.0.3#schema-object
- https://json-schema.org/understanding-json-schema/reference/combining.html#anyof
Inputs
I decided to use the following snippet of OpenAPI YAML for this research:
components:
schemas:
Badger:
type: object
properties:
b:
type: boolean
Mushroom:
type: object
properties:
m:
type: integer
Snake:
type: object
properties:
s:
type: string
MyAnyOf:
anyOf: &schemas
- $ref: '#/components/schemas/Badger'
- $ref: '#/components/schemas/Mushroom'
- $ref: '#/components/schemas/Snake'
- type: string
- type: string
pattern: '\d'
- type: string
enum:
- foo
- type: integer
- type: object
- type: object
properties:
foo:
type: string
- type: array
items:
type: integer
MyAllOf:
allOf: *schemas
MyOneOf:
oneOf: *schemas
Generators
I'll use the following set of generators initially:
openapi-generator -g swift
openapi-generator -g go
openapi-generator -g rust
openapi-typescript
openapi-typescript-codegen
Outputs
I've tried to capture just enough of the output for each generator to draw themes about the question at hand.
openapi-generator -g swift
public struct MyAnyOf: Codable, JSONEncodable, Hashable {
public var b: Bool?
public var m: Int?
public var s: String?
public var foo: String?
}
public struct MyAllOf: Codable, JSONEncodable, Hashable {
public var b: Bool?
public var m: Int?
public var s: String?
public var foo: String?
}
public enum MyOneOf: Codable, JSONEncodable, Hashable {
case typeAnyCodable(AnyCodable)
case typeBadger(Badger)
case typeInt(Int)
case typeMushroom(Mushroom)
case typeMyAnyOfAnyOf(MyAnyOfAnyOf)
case typeSnake(Snake)
case typeString(String)
case type[Int]([Int])
}
openapi-generator -g go
type MyAnyOf struct {
Badger *Badger
Mushroom *Mushroom
MyAnyOfAnyOf *MyAnyOfAnyOf
Snake *Snake
[]int32 *[]int32
int32 *int32
map[string]interface{} *map[string]interface{}
string *string
}
type MyAllOf struct {
B *bool `json:"b,omitempty"`
M *int32 `json:"m,omitempty"`
S *string `json:"s,omitempty"`
Foo *string `json:"foo,omitempty"`
}
type MyOneOf struct {
Badger *Badger
Mushroom *Mushroom
MyAnyOfAnyOf *MyAnyOfAnyOf
Snake *Snake
ArrayOfInt32 *[]int32
Int32 *int32
MapmapOfStringinterface{} *map[string]interface{}
String *string
}
openapi-generator -g rust
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MyAnyOf {
#[serde(rename = "b", skip_serializing_if = "Option::is_none")]
pub b: Option<bool>,
#[serde(rename = "m", skip_serializing_if = "Option::is_none")]
pub m: Option<i32>,
#[serde(rename = "s", skip_serializing_if = "Option::is_none")]
pub s: Option<String>,
#[serde(rename = "foo", skip_serializing_if = "Option::is_none")]
pub foo: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MyAllOf {
#[serde(rename = "b", skip_serializing_if = "Option::is_none")]
pub b: Option<bool>,
#[serde(rename = "m", skip_serializing_if = "Option::is_none")]
pub m: Option<i32>,
#[serde(rename = "s", skip_serializing_if = "Option::is_none")]
pub s: Option<String>,
#[serde(rename = "foo", skip_serializing_if = "Option::is_none")]
pub foo: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MyOneOf {
#[serde(rename = "b", skip_serializing_if = "Option::is_none")]
pub b: Option<bool>,
#[serde(rename = "m", skip_serializing_if = "Option::is_none")]
pub m: Option<i32>,
#[serde(rename = "s", skip_serializing_if = "Option::is_none")]
pub s: Option<String>,
#[serde(rename = "foo", skip_serializing_if = "Option::is_none")]
pub foo: Option<String>,
}
openapi-typescript
export interface components {
schemas: {
Badger: {
b?: boolean;
};
Mushroom: {
m?: number;
};
Snake: {
s?: string;
};
MyAnyOf: components["schemas"]["Badger"] | components["schemas"]["Mushroom"] | components["schemas"]["Snake"] | string | "foo" | number | Record<string, never> | {
foo?: string;
} | number[];
MyAllOf: components["schemas"]["Badger"] & components["schemas"]["Mushroom"] & components["schemas"]["Snake"] & string & string & "foo" & number & Record<string, never> & {
foo?: string;
} & number[];
MyOneOf: components["schemas"]["Badger"] | components["schemas"]["Mushroom"] | components["schemas"]["Snake"] | string | "foo" | number | Record<string, never> | {
foo?: string;
} | number[];
};
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
openapi-typescript-codegen
export type MyAnyOf = (Badger | Mushroom | Snake | string | 'foo' | number | Record<string, any> | {
foo?: string;
} | Array<number>);
export type MyAllOf = (Badger & Mushroom & Snake & string & 'foo' & number & Record<string, any> & {
foo?: string;
} & Array<number>);
export type MyOneOf = (Badger | Mushroom | Snake | string | 'foo' | number | Record<string, any> | {
foo?: string;
} | Array<number>);
Learnings
The above outputs show different strategies for handling schema composition,
including flattened types, union types, variant types, etc.
However, none of them seem to need to make use of an index-based naming, e.g.
case1
, case2
, etc.
from swift-openapi-generator.
@simonjbeaumont to gather some data.
from swift-openapi-generator.
Yeah if constrained to just references to objects as subschemas, those can be handled pretty well, because they have a global name.
But all of these aggregate types (except for oneOfs with a discriminator) can have any schema as a subschemas, and those types can be inlined (in which case you don't have a global name) and don't even have to be unique (you can have two string schemas that only differ by their regex constraint).
Curious to see the output for the following additional subschemas:
- type: string
- type: string
pattern: '\d'
- type: string
enum:
- foo
- type: integer
- type: object
- type: object
properties:
foo:
type: string
- type: array
- type: array
items:
type: integer
I also suspect that variadic generics in Swift could help us here, but all this was designed before they were available.
from swift-openapi-generator.
Those set of subschemas won't work for all of the schema composition methods, because they cannot be used in conjunction in some cases in an OAS-compliant way. However, I threw them into the generators and updated the comment with the findings.
Still no index-based type names.
from swift-openapi-generator.
Sure, in the case of allOf and anyOf, there are some mutually incompatible schemas, but the oneOf should be able to support all 10 schemas. From the above, it seems none of the generators were able to generate more than 8, and some stopped at 7.
And getting all the way to 10 is where they'd have to choose either an order-based name, or create a unique name from the contents, such as string_with_pattern_backslash_d_minItems_10_...
etc. The order-based name is a lot simpler to implement, but the content-based name would also work.
Would love to see a generator that can actually handle all 10 cases, and which method of naming they chose.
from swift-openapi-generator.
but the oneOf should be able to support all 10 schemas.
I don't think that's true. For example you suggested these:
- type: string
- type: string
pattern: '\d'
- type: string
enum:
- foo
which cannot work because it needs to satisfy (exactly one)1. But the first is always satisfied, which is why I think some of the generators skipped it (there was some diagnostic output to this effect too).
Footnotes
from swift-openapi-generator.
Good catch, let me amend to:
- type: string
pattern: '\s'
- type: string
pattern: '\d'
from swift-openapi-generator.
Related Issues (20)
- Empty arrays are omitted from query params HOT 5
- Support for `multipart/mixed`? HOT 1
- When a delete path do not have responses content an error generated HOT 2
- Add ability to attach protocols to generated models HOT 9
- Protocol where each `Output` conforms to HOT 3
- Make the generator usable on very big specs HOT 3
- Old and new examples HOT 3
- visionOS can't use HOT 4
- How to convert the binary obtained from the server into a playable Data type? HOT 2
- HTTPTypes linker error if multiple targets use open API generator HOT 2
- Getting "any has no effect on concrete type 'Error'" HOT 6
- Need to be able to support multiple OpenAPI specs HOT 2
- Tutorial doc text color issue on light theme HOT 1
- Check for updated spec before generating HOT 2
- Warnings due to `@frozen` attribute on non-public enums with Xcode 16 Beta 3 HOT 5
- Support multiple contentTypes in encoding multipart/form-data HOT 7
- multipart/form-data request body schemas with additionalProperties are a bit confusing to use, also doesn't compile HOT 7
- Multipart generated enum case naming is inconsistent (other vs additionalProperties) HOT 3
- Add `consuming` Modifier to Middleware Intercept Method Parameters HOT 8
- Hyphen in path variable name results in static path variable name in url HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from swift-openapi-generator.