Fix #12412 Use enum type for discriminator (#13846)

* Fix #12412 Use enum type for discriminator

* Rename variable

* Fix required property in test resource

* Add example of enum mapping to samples

* Generate samples

* Generate samples after merging master

* Regenerate samples

* Fix raw use of parameterized class

* Add test showing serialization and deserialization of classes with enum discriminator

* Remove old generated files

* Generate samples

* Generate samples
This commit is contained in:
bernie-schelberg-mywave 2023-07-05 01:25:39 +10:00 committed by GitHub
parent b465d88880
commit a42f90b0e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1156 additions and 7 deletions

View File

@ -3526,8 +3526,8 @@ public class DefaultCodegen implements CodegenConfig {
return null;
}
CodegenDiscriminator discriminator = new CodegenDiscriminator();
String discPropName = sourceDiscriminator.getPropertyName();
discriminator.setPropertyName(toVarName(discPropName));
String discriminatorPropertyName = sourceDiscriminator.getPropertyName();
discriminator.setPropertyName(toVarName(discriminatorPropertyName));
discriminator.setPropertyBaseName(sourceDiscriminator.getPropertyName());
discriminator.setPropertyGetter(toGetter(discriminator.getPropertyName()));
@ -3535,13 +3535,21 @@ public class DefaultCodegen implements CodegenConfig {
discriminator.setVendorExtensions(sourceDiscriminator.getExtensions());
}
// FIXME: for now, we assume that the discriminator property is String
discriminator.setPropertyType(typeMapping.get("string"));
// FIXME: there are other ways to define the type of the discriminator property (inline
// for example). Handling those scenarios is too complicated for me, I'm leaving it for
// the future..
String propertyType =
Optional.ofNullable(schema.getProperties())
.map(p -> (Schema<?>) p.get(discriminatorPropertyName))
.map(Schema::get$ref)
.map(ModelUtils::getSimpleRef)
.orElseGet(() -> typeMapping.get("string"));
discriminator.setPropertyType(propertyType);
// check to see if the discriminator property is an enum string
if (schema.getProperties() != null &&
schema.getProperties().get(discPropName) instanceof StringSchema) {
StringSchema s = (StringSchema) schema.getProperties().get(discPropName);
schema.getProperties().get(discriminatorPropertyName) instanceof StringSchema) {
StringSchema s = (StringSchema) schema.getProperties().get(discriminatorPropertyName);
if (s.getEnum() != null && !s.getEnum().isEmpty()) { // it's an enum string
discriminator.setIsEnum(true);
}
@ -3586,7 +3594,7 @@ public class DefaultCodegen implements CodegenConfig {
}
// if there are composed oneOf/anyOf schemas, add them to this discriminator
if (ModelUtils.isComposedSchema(schema) && !this.getLegacyDiscriminatorBehavior()) {
List<MappedModel> otherDescendants = getOneOfAnyOfDescendants(schemaName, discPropName, (ComposedSchema) schema, openAPI);
List<MappedModel> otherDescendants = getOneOfAnyOfDescendants(schemaName, discriminatorPropertyName, (ComposedSchema) schema, openAPI);
for (MappedModel otherDescendant : otherDescendants) {
if (!uniqueDescendants.contains(otherDescendant)) {
uniqueDescendants.add(otherDescendant);

View File

@ -56,6 +56,7 @@ import java.util.*;
import java.util.stream.Collectors;
import static junit.framework.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.*;
public class DefaultCodegenTest {
@ -1509,6 +1510,12 @@ public class DefaultCodegenTest {
hs.add(new CodegenDiscriminator.MappedModel(mn, mn));
Assert.assertEquals(cm.discriminator.getMappedModels(), hs);
// ref oneOf models with enum property discriminator
modelName = "FruitOneOfEnumMappingDisc";
sc = openAPI.getComponents().getSchemas().get(modelName);
cm = codegen.fromModel(modelName, sc);
assertThat(cm.discriminator.getPropertyType()).isEqualTo("FruitTypeEnum");
// ref oneOf models with discriminator in the grandparent schemas of those oneof models
modelName = "FruitGrandparentDisc";
sc = openAPI.getComponents().getSchemas().get(modelName);

View File

@ -1538,6 +1538,55 @@ public class SpringCodegenTest {
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/ChildWithoutMappingBDTO.java"), "@JsonTypeName");
}
@Test
void testOneOfWithEnumDiscriminator() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/3_0/oneOfDiscriminator.yaml", null, new ParseOptions()).getOpenAPI();
SpringCodegen codegen = new SpringCodegen();
codegen.setOutputDir(output.getAbsolutePath());
codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true");
codegen.setUseOneOfInterfaces(true);
ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);
DefaultGenerator generator = new DefaultGenerator();
codegen.setHateoas(true);
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
//generator.setGeneratorPropertyDefault(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "false");
codegen.setUseOneOfInterfaces(true);
codegen.setLegacyDiscriminatorBehavior(false);
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
generator.opts(input).generate();
assertFileContains(
Paths.get(outputPath + "/src/main/java/org/openapitools/model/FruitOneOfEnumMappingDisc.java"),
"public FruitTypeEnum getFruitType();"
);
assertFileContains(
Paths.get(outputPath + "/src/main/java/org/openapitools/model/AppleOneOfEnumMappingDisc.java"),
"private FruitTypeEnum fruitType;",
"public FruitTypeEnum getFruitType() {"
);
assertFileContains(
Paths.get(outputPath + "/src/main/java/org/openapitools/model/BananaOneOfEnumMappingDisc.java"),
"private FruitTypeEnum fruitType;",
"public FruitTypeEnum getFruitType() {"
);
}
@Test
public void testTypeMappings() {
final SpringCodegen codegen = new SpringCodegen();

View File

@ -170,6 +170,38 @@ components:
type: integer
oneOf:
- $ref: '#/components/schemas/FruitType'
FruitTypeEnum:
type: string
enum: [APPLE, BANANA]
FruitOneOfEnumMappingDisc:
type: object
properties:
fruitType:
$ref: "#/components/schemas/FruitTypeEnum"
required:
- fruitType
oneOf:
- $ref: '#/components/schemas/AppleOneOfEnumMappingDisc'
- $ref: '#/components/schemas/BananaOneOfEnumMappingDisc'
discriminator:
propertyName: fruitType
mapping:
APPLE: '#/components/schemas/AppleOneOfEnumMappingDisc'
BANANA: '#/components/schemas/BananaOneOfEnumMappingDisc'
AppleOneOfEnumMappingDisc:
type: object
required:
- seeds
properties:
seeds:
type: integer
BananaOneOfEnumMappingDisc:
type: object
required:
- length
properties:
length:
type: integer
FruitGrandparentDisc:
oneOf:
- $ref: '#/components/schemas/AppleGrandparentDisc'

View File

@ -177,6 +177,38 @@ components:
type: string
allOf:
- $ref: '#/components/schemas/Pizza'
FruitType:
type: string
enum: [APPLE, BANANA]
Fruit:
type: object
properties:
fruitType:
$ref: "#/components/schemas/FruitType"
required:
- fruitType
oneOf:
- $ref: '#/components/schemas/Apple'
- $ref: '#/components/schemas/Banana'
discriminator:
propertyName: fruitType
mapping:
APPLE: '#/components/schemas/Apple'
BANANA: '#/components/schemas/Banana'
Apple:
type: object
required:
- seeds
properties:
seeds:
type: integer
Banana:
type: object
required:
- length
properties:
length:
type: integer
requestBodies:
Foo:

View File

@ -2,6 +2,8 @@
README.md
analysis_options.yaml
doc/Addressable.md
doc/Apple.md
doc/Banana.md
doc/Bar.md
doc/BarApi.md
doc/BarCreate.md
@ -14,6 +16,8 @@ doc/Foo.md
doc/FooApi.md
doc/FooRef.md
doc/FooRefOrValue.md
doc/Fruit.md
doc/FruitType.md
doc/Pasta.md
doc/Pizza.md
doc/PizzaSpeziale.md
@ -29,6 +33,8 @@ lib/src/auth/bearer_auth.dart
lib/src/auth/oauth.dart
lib/src/date_serializer.dart
lib/src/model/addressable.dart
lib/src/model/apple.dart
lib/src/model/banana.dart
lib/src/model/bar.dart
lib/src/model/bar_create.dart
lib/src/model/bar_ref.dart
@ -40,6 +46,8 @@ lib/src/model/extensible.dart
lib/src/model/foo.dart
lib/src/model/foo_ref.dart
lib/src/model/foo_ref_or_value.dart
lib/src/model/fruit.dart
lib/src/model/fruit_type.dart
lib/src/model/pasta.dart
lib/src/model/pizza.dart
lib/src/model/pizza_speziale.dart

View File

@ -73,6 +73,8 @@ Class | Method | HTTP request | Description
## Documentation For Models
- [Addressable](doc/Addressable.md)
- [Apple](doc/Apple.md)
- [Banana](doc/Banana.md)
- [Bar](doc/Bar.md)
- [BarCreate](doc/BarCreate.md)
- [BarRef](doc/BarRef.md)
@ -83,6 +85,8 @@ Class | Method | HTTP request | Description
- [Foo](doc/Foo.md)
- [FooRef](doc/FooRef.md)
- [FooRefOrValue](doc/FooRefOrValue.md)
- [Fruit](doc/Fruit.md)
- [FruitType](doc/FruitType.md)
- [Pasta](doc/Pasta.md)
- [Pizza](doc/Pizza.md)
- [PizzaSpeziale](doc/PizzaSpeziale.md)

View File

@ -0,0 +1,15 @@
# openapi.model.Apple
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**seeds** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -0,0 +1,15 @@
# openapi.model.Banana
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**length** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -0,0 +1,17 @@
# openapi.model.Fruit
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**fruitType** | [**FruitType**](FruitType.md) | |
**seeds** | **int** | |
**length** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -0,0 +1,14 @@
# openapi.model.FruitType
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -13,6 +13,8 @@ export 'package:openapi/src/api/bar_api.dart';
export 'package:openapi/src/api/foo_api.dart';
export 'package:openapi/src/model/addressable.dart';
export 'package:openapi/src/model/apple.dart';
export 'package:openapi/src/model/banana.dart';
export 'package:openapi/src/model/bar.dart';
export 'package:openapi/src/model/bar_create.dart';
export 'package:openapi/src/model/bar_ref.dart';
@ -23,6 +25,8 @@ export 'package:openapi/src/model/extensible.dart';
export 'package:openapi/src/model/foo.dart';
export 'package:openapi/src/model/foo_ref.dart';
export 'package:openapi/src/model/foo_ref_or_value.dart';
export 'package:openapi/src/model/fruit.dart';
export 'package:openapi/src/model/fruit_type.dart';
export 'package:openapi/src/model/pasta.dart';
export 'package:openapi/src/model/pizza.dart';
export 'package:openapi/src/model/pizza_speziale.dart';

View File

@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// ignore_for_file: unused_element
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'apple.g.dart';
/// Apple
///
/// Properties:
/// * [seeds]
@BuiltValue()
abstract class Apple implements Built<Apple, AppleBuilder> {
@BuiltValueField(wireName: r'seeds')
int get seeds;
Apple._();
factory Apple([void updates(AppleBuilder b)]) = _$Apple;
@BuiltValueHook(initializeBuilder: true)
static void _defaults(AppleBuilder b) => b;
@BuiltValueSerializer(custom: true)
static Serializer<Apple> get serializer => _$AppleSerializer();
}
class _$AppleSerializer implements PrimitiveSerializer<Apple> {
@override
final Iterable<Type> types = const [Apple, _$Apple];
@override
final String wireName = r'Apple';
Iterable<Object?> _serializeProperties(
Serializers serializers,
Apple object, {
FullType specifiedType = FullType.unspecified,
}) sync* {
yield r'seeds';
yield serializers.serialize(
object.seeds,
specifiedType: const FullType(int),
);
}
@override
Object serialize(
Serializers serializers,
Apple object, {
FullType specifiedType = FullType.unspecified,
}) {
return _serializeProperties(serializers, object, specifiedType: specifiedType).toList();
}
void _deserializeProperties(
Serializers serializers,
Object serialized, {
FullType specifiedType = FullType.unspecified,
required List<Object?> serializedList,
required AppleBuilder result,
required List<Object?> unhandled,
}) {
for (var i = 0; i < serializedList.length; i += 2) {
final key = serializedList[i] as String;
final value = serializedList[i + 1];
switch (key) {
case r'seeds':
final valueDes = serializers.deserialize(
value,
specifiedType: const FullType(int),
) as int;
result.seeds = valueDes;
break;
default:
unhandled.add(key);
unhandled.add(value);
break;
}
}
}
@override
Apple deserialize(
Serializers serializers,
Object serialized, {
FullType specifiedType = FullType.unspecified,
}) {
final result = AppleBuilder();
final serializedList = (serialized as Iterable<Object?>).toList();
final unhandled = <Object?>[];
_deserializeProperties(
serializers,
serialized,
specifiedType: specifiedType,
serializedList: serializedList,
unhandled: unhandled,
result: result,
);
return result.build();
}
}

View File

@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// ignore_for_file: unused_element
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'banana.g.dart';
/// Banana
///
/// Properties:
/// * [length]
@BuiltValue()
abstract class Banana implements Built<Banana, BananaBuilder> {
@BuiltValueField(wireName: r'length')
int get length;
Banana._();
factory Banana([void updates(BananaBuilder b)]) = _$Banana;
@BuiltValueHook(initializeBuilder: true)
static void _defaults(BananaBuilder b) => b;
@BuiltValueSerializer(custom: true)
static Serializer<Banana> get serializer => _$BananaSerializer();
}
class _$BananaSerializer implements PrimitiveSerializer<Banana> {
@override
final Iterable<Type> types = const [Banana, _$Banana];
@override
final String wireName = r'Banana';
Iterable<Object?> _serializeProperties(
Serializers serializers,
Banana object, {
FullType specifiedType = FullType.unspecified,
}) sync* {
yield r'length';
yield serializers.serialize(
object.length,
specifiedType: const FullType(int),
);
}
@override
Object serialize(
Serializers serializers,
Banana object, {
FullType specifiedType = FullType.unspecified,
}) {
return _serializeProperties(serializers, object, specifiedType: specifiedType).toList();
}
void _deserializeProperties(
Serializers serializers,
Object serialized, {
FullType specifiedType = FullType.unspecified,
required List<Object?> serializedList,
required BananaBuilder result,
required List<Object?> unhandled,
}) {
for (var i = 0; i < serializedList.length; i += 2) {
final key = serializedList[i] as String;
final value = serializedList[i + 1];
switch (key) {
case r'length':
final valueDes = serializers.deserialize(
value,
specifiedType: const FullType(int),
) as int;
result.length = valueDes;
break;
default:
unhandled.add(key);
unhandled.add(value);
break;
}
}
}
@override
Banana deserialize(
Serializers serializers,
Object serialized, {
FullType specifiedType = FullType.unspecified,
}) {
final result = BananaBuilder();
final serializedList = (serialized as Iterable<Object?>).toList();
final unhandled = <Object?>[];
_deserializeProperties(
serializers,
serialized,
specifiedType: specifiedType,
serializedList: serializedList,
unhandled: unhandled,
result: result,
);
return result.build();
}
}

View File

@ -0,0 +1,166 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// ignore_for_file: unused_element
import 'package:openapi/src/model/apple.dart';
import 'package:openapi/src/model/banana.dart';
import 'package:openapi/src/model/fruit_type.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:one_of/one_of.dart';
part 'fruit.g.dart';
/// Fruit
///
/// Properties:
/// * [fruitType]
/// * [seeds]
/// * [length]
@BuiltValue()
abstract class Fruit implements Built<Fruit, FruitBuilder> {
@BuiltValueField(wireName: r'fruitType')
FruitType get fruitType;
// enum fruitTypeEnum { APPLE, BANANA, };
/// One Of [Apple], [Banana]
OneOf get oneOf;
static const String discriminatorFieldName = r'fruitType';
static const Map<String, Type> discriminatorMapping = {
r'APPLE': Apple,
r'BANANA': Banana,
};
Fruit._();
factory Fruit([void updates(FruitBuilder b)]) = _$Fruit;
@BuiltValueHook(initializeBuilder: true)
static void _defaults(FruitBuilder b) => b;
@BuiltValueSerializer(custom: true)
static Serializer<Fruit> get serializer => _$FruitSerializer();
}
extension FruitDiscriminatorExt on Fruit {
String? get discriminatorValue {
if (this is Apple) {
return r'APPLE';
}
if (this is Banana) {
return r'BANANA';
}
return null;
}
}
extension FruitBuilderDiscriminatorExt on FruitBuilder {
String? get discriminatorValue {
if (this is AppleBuilder) {
return r'APPLE';
}
if (this is BananaBuilder) {
return r'BANANA';
}
return null;
}
}
class _$FruitSerializer implements PrimitiveSerializer<Fruit> {
@override
final Iterable<Type> types = const [Fruit, _$Fruit];
@override
final String wireName = r'Fruit';
Iterable<Object?> _serializeProperties(
Serializers serializers,
Fruit object, {
FullType specifiedType = FullType.unspecified,
}) sync* {
yield r'fruitType';
yield serializers.serialize(
object.fruitType,
specifiedType: const FullType(FruitType),
);
}
@override
Object serialize(
Serializers serializers,
Fruit object, {
FullType specifiedType = FullType.unspecified,
}) {
final oneOf = object.oneOf;
final result = _serializeProperties(serializers, object, specifiedType: specifiedType).toList();
result.addAll(serializers.serialize(oneOf.value, specifiedType: FullType(oneOf.valueType)) as Iterable<Object?>);
return result;
}
void _deserializeProperties(
Serializers serializers,
Object serialized, {
FullType specifiedType = FullType.unspecified,
required List<Object?> serializedList,
required FruitBuilder result,
required List<Object?> unhandled,
}) {
for (var i = 0; i < serializedList.length; i += 2) {
final key = serializedList[i] as String;
final value = serializedList[i + 1];
switch (key) {
case r'fruitType':
final valueDes = serializers.deserialize(
value,
specifiedType: const FullType(FruitType),
) as FruitType;
result.fruitType = valueDes;
break;
default:
unhandled.add(key);
unhandled.add(value);
break;
}
}
}
@override
Fruit deserialize(
Serializers serializers,
Object serialized, {
FullType specifiedType = FullType.unspecified,
}) {
final result = FruitBuilder();
Object? oneOfDataSrc;
final serializedList = (serialized as Iterable<Object?>).toList();
final discIndex = serializedList.indexOf(Fruit.discriminatorFieldName) + 1;
final discValue = serializers.deserialize(serializedList[discIndex], specifiedType: FullType(String)) as String;
oneOfDataSrc = serialized;
final oneOfTypes = [Apple, Banana, ];
Object oneOfResult;
Type oneOfType;
switch (discValue) {
case r'APPLE':
oneOfResult = serializers.deserialize(
oneOfDataSrc,
specifiedType: FullType(Apple),
) as Apple;
oneOfType = Apple;
break;
case r'BANANA':
oneOfResult = serializers.deserialize(
oneOfDataSrc,
specifiedType: FullType(Banana),
) as Banana;
oneOfType = Banana;
break;
default:
throw UnsupportedError("Couldn't deserialize oneOf for the discriminator value: ${discValue}");
}
result.oneOf = OneOfDynamic(typeIndex: oneOfTypes.indexOf(oneOfType), types: oneOfTypes, value: oneOfResult);
return result.build();
}
}

View File

@ -0,0 +1,36 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// ignore_for_file: unused_element
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'fruit_type.g.dart';
class FruitType extends EnumClass {
@BuiltValueEnumConst(wireName: r'APPLE')
static const FruitType APPLE = _$APPLE;
@BuiltValueEnumConst(wireName: r'BANANA')
static const FruitType BANANA = _$BANANA;
@BuiltValueEnumConst(wireName: r'unknown_default_open_api', fallback: true)
static const FruitType unknownDefaultOpenApi = _$unknownDefaultOpenApi;
static Serializer<FruitType> get serializer => _$fruitTypeSerializer;
const FruitType._(String name): super(name);
static BuiltSet<FruitType> get values => _$values;
static FruitType valueOf(String name) => _$valueOf(name);
}
/// Optionally, enum_class can generate a mixin to go with your enum for use
/// with Angular. It exposes your enum constants as getters. So, if you mix it
/// in to your Dart component class, the values become available to the
/// corresponding Angular template.
///
/// Trigger mixin generation by writing a line like this one next to your enum.
abstract class FruitTypeMixin = Object with _$FruitTypeMixin;

View File

@ -15,6 +15,8 @@ import 'package:openapi/src/date_serializer.dart';
import 'package:openapi/src/model/date.dart';
import 'package:openapi/src/model/addressable.dart';
import 'package:openapi/src/model/apple.dart';
import 'package:openapi/src/model/banana.dart';
import 'package:openapi/src/model/bar.dart';
import 'package:openapi/src/model/bar_create.dart';
import 'package:openapi/src/model/bar_ref.dart';
@ -25,6 +27,8 @@ import 'package:openapi/src/model/extensible.dart';
import 'package:openapi/src/model/foo.dart';
import 'package:openapi/src/model/foo_ref.dart';
import 'package:openapi/src/model/foo_ref_or_value.dart';
import 'package:openapi/src/model/fruit.dart';
import 'package:openapi/src/model/fruit_type.dart';
import 'package:openapi/src/model/pasta.dart';
import 'package:openapi/src/model/pizza.dart';
import 'package:openapi/src/model/pizza_speziale.dart';
@ -33,6 +37,8 @@ part 'serializers.g.dart';
@SerializersFor([
Addressable,$Addressable,
Apple,
Banana,
Bar,
BarCreate,
BarRef,
@ -43,6 +49,8 @@ part 'serializers.g.dart';
Foo,
FooRef,
FooRefOrValue,
Fruit,
FruitType,
Pasta,
Pizza,$Pizza,
PizzaSpeziale,

View File

@ -0,0 +1,16 @@
import 'package:test/test.dart';
import 'package:openapi/openapi.dart';
// tests for Apple
void main() {
final instance = AppleBuilder();
// TODO add properties to the builder and call build()
group(Apple, () {
// int seeds
test('to test the property `seeds`', () async {
// TODO
});
});
}

View File

@ -0,0 +1,16 @@
import 'package:test/test.dart';
import 'package:openapi/openapi.dart';
// tests for Banana
void main() {
final instance = BananaBuilder();
// TODO add properties to the builder and call build()
group(Banana, () {
// int length
test('to test the property `length`', () async {
// TODO
});
});
}

View File

@ -0,0 +1,26 @@
import 'package:test/test.dart';
import 'package:openapi/openapi.dart';
// tests for Fruit
void main() {
final instance = FruitBuilder();
// TODO add properties to the builder and call build()
group(Fruit, () {
// FruitType fruitType
test('to test the property `fruitType`', () async {
// TODO
});
// int seeds
test('to test the property `seeds`', () async {
// TODO
});
// int length
test('to test the property `length`', () async {
// TODO
});
});
}

View File

@ -0,0 +1,9 @@
import 'package:test/test.dart';
import 'package:openapi/openapi.dart';
// tests for FruitType
void main() {
group(FruitType, () {
});
}

View File

@ -7,9 +7,12 @@ src/main/java/org/openapitools/api/BarApi.java
src/main/java/org/openapitools/api/BarApiController.java
src/main/java/org/openapitools/api/FooApi.java
src/main/java/org/openapitools/api/FooApiController.java
src/main/java/org/openapitools/configuration/EnumConverterConfiguration.java
src/main/java/org/openapitools/configuration/HomeController.java
src/main/java/org/openapitools/configuration/SpringDocConfiguration.java
src/main/java/org/openapitools/model/Addressable.java
src/main/java/org/openapitools/model/Apple.java
src/main/java/org/openapitools/model/Banana.java
src/main/java/org/openapitools/model/Bar.java
src/main/java/org/openapitools/model/BarCreate.java
src/main/java/org/openapitools/model/BarRef.java
@ -20,6 +23,8 @@ src/main/java/org/openapitools/model/Extensible.java
src/main/java/org/openapitools/model/Foo.java
src/main/java/org/openapitools/model/FooRef.java
src/main/java/org/openapitools/model/FooRefOrValue.java
src/main/java/org/openapitools/model/Fruit.java
src/main/java/org/openapitools/model/FruitType.java
src/main/java/org/openapitools/model/Pasta.java
src/main/java/org/openapitools/model/Pizza.java
src/main/java/org/openapitools/model/PizzaSpeziale.java

View File

@ -0,0 +1,22 @@
package org.openapitools.configuration;
import org.openapitools.model.FruitType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
@Configuration
public class EnumConverterConfiguration {
@Bean(name = "org.openapitools.configuration.EnumConverterConfiguration.fruitTypeConverter")
Converter<String, FruitType> fruitTypeConverter() {
return new Converter<String, FruitType>() {
@Override
public FruitType convert(String source) {
return FruitType.fromValue(source);
}
};
}
}

View File

@ -0,0 +1,123 @@
package org.openapitools.model;
import java.net.URI;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
import javax.validation.constraints.*;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.*;
import javax.annotation.Generated;
/**
* Apple
*/
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public class Apple implements Fruit {
private Integer seeds;
private FruitType fruitType;
public Apple() {
super();
}
/**
* Constructor with only required parameters
*/
public Apple(Integer seeds) {
this.seeds = seeds;
this.fruitType = fruitType;
}
public Apple seeds(Integer seeds) {
this.seeds = seeds;
return this;
}
/**
* Get seeds
* @return seeds
*/
@NotNull
@Schema(name = "seeds", requiredMode = Schema.RequiredMode.REQUIRED)
@JsonProperty("seeds")
public Integer getSeeds() {
return seeds;
}
public void setSeeds(Integer seeds) {
this.seeds = seeds;
}
public Apple fruitType(FruitType fruitType) {
this.fruitType = fruitType;
return this;
}
/**
* Get fruitType
* @return fruitType
*/
@NotNull @Valid
@Schema(name = "fruitType", requiredMode = Schema.RequiredMode.REQUIRED)
@JsonProperty("fruitType")
public FruitType getFruitType() {
return fruitType;
}
public void setFruitType(FruitType fruitType) {
this.fruitType = fruitType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Apple apple = (Apple) o;
return Objects.equals(this.seeds, apple.seeds) &&
Objects.equals(this.fruitType, apple.fruitType);
}
@Override
public int hashCode() {
return Objects.hash(seeds, fruitType);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class Apple {\n");
sb.append(" seeds: ").append(toIndentedString(seeds)).append("\n");
sb.append(" fruitType: ").append(toIndentedString(fruitType)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}

View File

@ -0,0 +1,123 @@
package org.openapitools.model;
import java.net.URI;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
import javax.validation.constraints.*;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.*;
import javax.annotation.Generated;
/**
* Banana
*/
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public class Banana implements Fruit {
private Integer length;
private FruitType fruitType;
public Banana() {
super();
}
/**
* Constructor with only required parameters
*/
public Banana(Integer length) {
this.length = length;
this.fruitType = fruitType;
}
public Banana length(Integer length) {
this.length = length;
return this;
}
/**
* Get length
* @return length
*/
@NotNull
@Schema(name = "length", requiredMode = Schema.RequiredMode.REQUIRED)
@JsonProperty("length")
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public Banana fruitType(FruitType fruitType) {
this.fruitType = fruitType;
return this;
}
/**
* Get fruitType
* @return fruitType
*/
@NotNull @Valid
@Schema(name = "fruitType", requiredMode = Schema.RequiredMode.REQUIRED)
@JsonProperty("fruitType")
public FruitType getFruitType() {
return fruitType;
}
public void setFruitType(FruitType fruitType) {
this.fruitType = fruitType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Banana banana = (Banana) o;
return Objects.equals(this.length, banana.length) &&
Objects.equals(this.fruitType, banana.fruitType);
}
@Override
public int hashCode() {
return Objects.hash(length, fruitType);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class Banana {\n");
sb.append(" length: ").append(toIndentedString(length)).append("\n");
sb.append(" fruitType: ").append(toIndentedString(fruitType)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}

View File

@ -0,0 +1,40 @@
package org.openapitools.model;
import java.net.URI;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.model.Apple;
import org.openapitools.model.Banana;
import org.openapitools.model.FruitType;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
import javax.validation.constraints.*;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.*;
import javax.annotation.Generated;
@JsonIgnoreProperties(
value = "fruitType", // ignore manually set fruitType, it will be automatically generated by Jackson during serialization
allowSetters = true // allows the fruitType to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "fruitType", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = Apple.class, name = "APPLE"),
@JsonSubTypes.Type(value = Apple.class, name = "Apple"),
@JsonSubTypes.Type(value = Banana.class, name = "BANANA"),
@JsonSubTypes.Type(value = Banana.class, name = "Banana")
})
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public interface Fruit {
public FruitType getFruitType();
}

View File

@ -0,0 +1,56 @@
package org.openapitools.model;
import java.net.URI;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
import javax.validation.constraints.*;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.*;
import javax.annotation.Generated;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Gets or Sets FruitType
*/
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public enum FruitType {
APPLE("APPLE"),
BANANA("BANANA");
private String value;
FruitType(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
@JsonCreator
public static FruitType fromValue(String value) {
for (FruitType b : FruitType.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
}

View File

@ -232,3 +232,38 @@ components:
toppings:
type: string
type: object
FruitType:
enum:
- APPLE
- BANANA
type: string
Fruit:
discriminator:
mapping:
APPLE: '#/components/schemas/Apple'
BANANA: '#/components/schemas/Banana'
propertyName: fruitType
oneOf:
- $ref: '#/components/schemas/Apple'
- $ref: '#/components/schemas/Banana'
properties:
fruitType:
$ref: '#/components/schemas/FruitType'
required:
- fruitType
type: object
x-one-of-name: Fruit
Apple:
properties:
seeds:
type: integer
required:
- seeds
type: object
Banana:
properties:
length:
type: integer
required:
- length
type: object

View File

@ -0,0 +1,51 @@
package org.openapitools.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class FruitJacksonTest {
@Autowired
private ObjectMapper objectMapper;
@Test
void shouldSerializeAndDeserializeApple() throws JsonProcessingException {
int seeds = 7;
Fruit fruit = new Apple(seeds);
String json = objectMapper.writeValueAsString(fruit);
assertThat(json).contains("\"fruitType\":\"APPLE\"");
Fruit result = objectMapper.readValue(json, Fruit.class);
assertThat(result).isInstanceOfSatisfying(Apple.class, apple -> {
assertThat(apple.getSeeds()).isEqualTo(seeds);
assertThat(apple.getFruitType()).isEqualTo(FruitType.APPLE);
});
}
@Test
void shouldSerializeAndDeserializeBanana() throws JsonProcessingException {
int length = 7;
Fruit fruit = new Banana(length);
String json = objectMapper.writeValueAsString(fruit);
assertThat(json).contains("\"fruitType\":\"BANANA\"");
Fruit result = objectMapper.readValue(json, Fruit.class);
assertThat(result).isInstanceOfSatisfying(Banana.class, banana -> {
assertThat(banana.getLength()).isEqualTo(length);
assertThat(banana.getFruitType()).isEqualTo(FruitType.BANANA);
});
}
}