forked from loafle/openapi-generator-original
Better support of inline allOf/anyOf/oneOf schemas (#14887)
* better support of inilne allof schemas * add test file * better handling of anyof, oneof inline schemas * update samples * add tests for nested anyof * better code format
This commit is contained in:
parent
70faa6b15c
commit
e38ea578f8
@ -224,7 +224,7 @@ public class InlineModelResolver {
|
||||
}
|
||||
|
||||
if (m.getAllOf() != null && !m.getAllOf().isEmpty()) {
|
||||
// check to ensure at least of the allOf item is model
|
||||
// check to ensure at least one of the allOf item is model
|
||||
for (Schema inner : m.getAllOf()) {
|
||||
if (isModelNeeded(ModelUtils.getReferencedSchema(openAPI, inner), visitedSchemas)) {
|
||||
return true;
|
||||
@ -385,6 +385,7 @@ public class InlineModelResolver {
|
||||
}
|
||||
}
|
||||
m.setAnyOf(newAnyOf);
|
||||
|
||||
}
|
||||
if (m.getOneOf() != null) {
|
||||
List<Schema> newOneOf = new ArrayList<Schema>();
|
||||
@ -577,6 +578,8 @@ public class InlineModelResolver {
|
||||
// instead of inline.
|
||||
String innerModelName = resolveModelName(component.getTitle(), key);
|
||||
Schema innerModel = modelFromProperty(openAPI, component, innerModelName);
|
||||
// Recurse to create $refs for inner models
|
||||
gatherInlineModels(innerModel, innerModelName);
|
||||
String existing = matchGenerated(innerModel);
|
||||
if (existing == null) {
|
||||
innerModelName = addSchemas(innerModelName, innerModel);
|
||||
@ -604,13 +607,17 @@ public class InlineModelResolver {
|
||||
List<String> modelNames = new ArrayList<String>(models.keySet());
|
||||
for (String modelName : modelNames) {
|
||||
Schema model = models.get(modelName);
|
||||
if (ModelUtils.isComposedSchema(model)) {
|
||||
if (ModelUtils.isAnyOf(model)) { // contains anyOf only
|
||||
gatherInlineModels(model, modelName);
|
||||
} else if (ModelUtils.isOneOf(model)) { // contains oneOf only
|
||||
gatherInlineModels(model, modelName);
|
||||
} else if (ModelUtils.isComposedSchema(model)) {
|
||||
ComposedSchema m = (ComposedSchema) model;
|
||||
// inline child schemas
|
||||
flattenComposedChildren(modelName + "_allOf", m.getAllOf());
|
||||
flattenComposedChildren(modelName + "_anyOf", m.getAnyOf());
|
||||
flattenComposedChildren(modelName + "_oneOf", m.getOneOf());
|
||||
} else if (model instanceof Schema) {
|
||||
} else {
|
||||
gatherInlineModels(model, modelName);
|
||||
}
|
||||
}
|
||||
|
@ -1885,6 +1885,70 @@ public class ModelUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the schema contains oneOf but
|
||||
* no properties/allOf/anyOf defined.
|
||||
*
|
||||
* @param schema the schema
|
||||
* @return true if the schema contains oneOf but no properties/allOf/anyOf defined.
|
||||
*/
|
||||
public static boolean isOneOf(Schema schema) {
|
||||
if (hasOneOf(schema) && (schema.getProperties() == null || schema.getProperties().isEmpty()) &&
|
||||
(schema.getAllOf() == null || schema.getAllOf().isEmpty()) &&
|
||||
(schema.getAnyOf() == null || schema.getAnyOf().isEmpty())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the schema contains oneOf and may or may not have
|
||||
* properties/allOf/anyOf defined.
|
||||
*
|
||||
* @param schema the schema
|
||||
* @return true if allOf is not empty
|
||||
*/
|
||||
public static boolean hasOneOf(Schema schema) {
|
||||
if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the schema contains anyOf but
|
||||
* no properties/allOf/anyOf defined.
|
||||
*
|
||||
* @param schema the schema
|
||||
* @return true if the schema contains oneOf but no properties/allOf/anyOf defined.
|
||||
*/
|
||||
public static boolean isAnyOf(Schema schema) {
|
||||
if (hasAnyOf(schema) && (schema.getProperties() == null || schema.getProperties().isEmpty()) &&
|
||||
(schema.getAllOf() == null || schema.getAllOf().isEmpty()) &&
|
||||
(schema.getOneOf() == null || schema.getOneOf().isEmpty())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the schema contains anyOf and may or may not have
|
||||
* properties/allOf/oneOf defined.
|
||||
*
|
||||
* @param schema the schema
|
||||
* @return true if anyOf is not empty
|
||||
*/
|
||||
public static boolean hasAnyOf(Schema schema) {
|
||||
if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any of the common attributes of the schema (e.g. readOnly, default, maximum, etc) is defined.
|
||||
*
|
||||
|
@ -1103,4 +1103,29 @@ public class InlineModelResolverTest {
|
||||
//RequestBody referencedRequestBody = ModelUtils.getReferencedRequestBody(openAPI, requestBodyReference);
|
||||
//assertTrue(referencedRequestBody.getRequired());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineSchemaAllOfPropertyOfOneOf() {
|
||||
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/inline_model_allof_propertyof_oneof.yaml");
|
||||
InlineModelResolver resolver = new InlineModelResolver();
|
||||
resolver.flatten(openAPI);
|
||||
|
||||
Schema schema = openAPI.getComponents().getSchemas().get("Order_allOf_inline_oneof");
|
||||
assertEquals(((Schema) schema.getOneOf().get(0)).get$ref(), "#/components/schemas/Tag");
|
||||
assertEquals(((Schema) schema.getOneOf().get(1)).get$ref(), "#/components/schemas/Filter");
|
||||
|
||||
Schema schema2 = openAPI.getComponents().getSchemas().get("Order_allOf_inline_model");
|
||||
assertTrue(schema2.getProperties().get("something") instanceof StringSchema);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedAnyOf() {
|
||||
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/nested_anyof.yaml");
|
||||
InlineModelResolver resolver = new InlineModelResolver();
|
||||
resolver.flatten(openAPI);
|
||||
|
||||
Schema schema = openAPI.getComponents().getSchemas().get("SomeData_anyOf");
|
||||
assertTrue((Schema) schema.getAnyOf().get(0) instanceof StringSchema);
|
||||
assertTrue((Schema) schema.getAnyOf().get(1) instanceof IntegerSchema);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: ping test
|
||||
version: '1.0'
|
||||
servers:
|
||||
- url: 'http://localhost:8000/'
|
||||
paths:
|
||||
/ping:
|
||||
get:
|
||||
operationId: pingGet
|
||||
responses:
|
||||
'201':
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
Order:
|
||||
title: Pet Order
|
||||
description: An order for a pets from the pet store
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/Tag"
|
||||
- type: object
|
||||
properties:
|
||||
length:
|
||||
type: integer
|
||||
format: int64
|
||||
shipDate:
|
||||
type: string
|
||||
format: date-time
|
||||
inline_oneof:
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/Tag"
|
||||
- $ref: "#/components/schemas/Filter"
|
||||
inline_model:
|
||||
properties:
|
||||
something:
|
||||
type: string
|
||||
Tag:
|
||||
title: Pet Tag
|
||||
description: A tag for a pet
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
Filter:
|
||||
title: Pet Tag
|
||||
description: A tag for a pet
|
||||
type: object
|
||||
properties:
|
||||
fid:
|
||||
type: integer
|
||||
format: int64
|
||||
fname:
|
||||
type: string
|
@ -0,0 +1,16 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
version: '1.0.0'
|
||||
title: 'Problem example with nested anyOf'
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
NullOne:
|
||||
enum:
|
||||
- null
|
||||
SomeData:
|
||||
anyOf:
|
||||
- anyOf:
|
||||
- type: string
|
||||
- type: integer
|
||||
- $ref: '#/components/schemas/NullOne'
|
@ -9,14 +9,12 @@ docs/AnotherXmlInner.md
|
||||
docs/AnotherXmlObject.md
|
||||
docs/AnyOfGet202Response.md
|
||||
docs/AnyOfObject.md
|
||||
docs/AnyOfObjectAnyOf.md
|
||||
docs/AnyOfProperty.md
|
||||
docs/DuplicateXmlObject.md
|
||||
docs/EnumWithStarObject.md
|
||||
docs/Err.md
|
||||
docs/Error.md
|
||||
docs/Model12345AnyOfObject.md
|
||||
docs/Model12345AnyOfObjectAnyOf.md
|
||||
docs/MultigetGet201Response.md
|
||||
docs/MyId.md
|
||||
docs/MyIdList.md
|
||||
|
@ -155,14 +155,12 @@ Method | HTTP request | Description
|
||||
- [AnotherXmlObject](docs/AnotherXmlObject.md)
|
||||
- [AnyOfGet202Response](docs/AnyOfGet202Response.md)
|
||||
- [AnyOfObject](docs/AnyOfObject.md)
|
||||
- [AnyOfObjectAnyOf](docs/AnyOfObjectAnyOf.md)
|
||||
- [AnyOfProperty](docs/AnyOfProperty.md)
|
||||
- [DuplicateXmlObject](docs/DuplicateXmlObject.md)
|
||||
- [EnumWithStarObject](docs/EnumWithStarObject.md)
|
||||
- [Err](docs/Err.md)
|
||||
- [Error](docs/Error.md)
|
||||
- [Model12345AnyOfObject](docs/Model12345AnyOfObject.md)
|
||||
- [Model12345AnyOfObjectAnyOf](docs/Model12345AnyOfObjectAnyOf.md)
|
||||
- [MultigetGet201Response](docs/MultigetGet201Response.md)
|
||||
- [MyId](docs/MyId.md)
|
||||
- [MyIdList](docs/MyIdList.md)
|
||||
|
@ -478,13 +478,20 @@ components:
|
||||
- requiredAnyOf
|
||||
AnyOfObject:
|
||||
anyOf:
|
||||
- $ref: '#/components/schemas/AnyOfObject_anyOf'
|
||||
- enum:
|
||||
- FOO
|
||||
- BAR
|
||||
type: string
|
||||
- description: Alternate option
|
||||
type: string
|
||||
description: Test a model containing an anyOf
|
||||
"12345AnyOfObject":
|
||||
anyOf:
|
||||
- $ref: '#/components/schemas/_12345AnyOfObject_anyOf'
|
||||
- enum:
|
||||
- FOO
|
||||
- BAR
|
||||
- '*'
|
||||
type: string
|
||||
- description: Alternate option
|
||||
type: string
|
||||
description: Test a model containing an anyOf that starts with a number
|
||||
@ -678,19 +685,6 @@ components:
|
||||
anyOf:
|
||||
- $ref: '#/components/schemas/StringObject'
|
||||
- $ref: '#/components/schemas/UuidObject'
|
||||
AnyOfObject_anyOf:
|
||||
enum:
|
||||
- FOO
|
||||
- BAR
|
||||
type: string
|
||||
example: null
|
||||
_12345AnyOfObject_anyOf:
|
||||
enum:
|
||||
- FOO
|
||||
- BAR
|
||||
- '*'
|
||||
type: string
|
||||
example: null
|
||||
securitySchemes:
|
||||
authScheme:
|
||||
flows:
|
||||
|
@ -638,50 +638,6 @@ impl AnyOfObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of values.
|
||||
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
|
||||
/// which helps with FFI.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]
|
||||
pub enum AnyOfObjectAnyOf {
|
||||
#[serde(rename = "FOO")]
|
||||
Foo,
|
||||
#[serde(rename = "BAR")]
|
||||
Bar,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AnyOfObjectAnyOf {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
AnyOfObjectAnyOf::Foo => write!(f, "FOO"),
|
||||
AnyOfObjectAnyOf::Bar => write!(f, "BAR"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for AnyOfObjectAnyOf {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
match s {
|
||||
"FOO" => std::result::Result::Ok(AnyOfObjectAnyOf::Foo),
|
||||
"BAR" => std::result::Result::Ok(AnyOfObjectAnyOf::Bar),
|
||||
_ => std::result::Result::Err(format!("Value not valid: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyOfObjectAnyOf {
|
||||
/// Helper function to allow us to convert this model to an XML string.
|
||||
/// Will panic if serialisation fails.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn as_xml(&self) -> String {
|
||||
serde_xml_rs::to_string(&self).expect("impossible to fail to serialize")
|
||||
}
|
||||
}
|
||||
|
||||
/// Test containing an anyOf object
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
|
||||
@ -1242,54 +1198,6 @@ impl Model12345AnyOfObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of values.
|
||||
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
|
||||
/// which helps with FFI.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]
|
||||
pub enum Model12345AnyOfObjectAnyOf {
|
||||
#[serde(rename = "FOO")]
|
||||
Foo,
|
||||
#[serde(rename = "BAR")]
|
||||
Bar,
|
||||
#[serde(rename = "*")]
|
||||
Star,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Model12345AnyOfObjectAnyOf {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
Model12345AnyOfObjectAnyOf::Foo => write!(f, "FOO"),
|
||||
Model12345AnyOfObjectAnyOf::Bar => write!(f, "BAR"),
|
||||
Model12345AnyOfObjectAnyOf::Star => write!(f, "*"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Model12345AnyOfObjectAnyOf {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
match s {
|
||||
"FOO" => std::result::Result::Ok(Model12345AnyOfObjectAnyOf::Foo),
|
||||
"BAR" => std::result::Result::Ok(Model12345AnyOfObjectAnyOf::Bar),
|
||||
"*" => std::result::Result::Ok(Model12345AnyOfObjectAnyOf::Star),
|
||||
_ => std::result::Result::Err(format!("Value not valid: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Model12345AnyOfObjectAnyOf {
|
||||
/// Helper function to allow us to convert this model to an XML string.
|
||||
/// Will panic if serialisation fails.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn as_xml(&self) -> String {
|
||||
serde_xml_rs::to_string(&self).expect("impossible to fail to serialize")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
|
||||
pub struct MultigetGet201Response {
|
||||
|
Loading…
x
Reference in New Issue
Block a user