Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ part '{{classFilename}}.g.dart';
}
{{#discriminator}}{{#hasDiscriminatorWithNonEmptyMapping}}
{{>serialization/built_value/class_discriminator}}

{{/hasDiscriminatorWithNonEmptyMapping}}{{/discriminator}}
{{>serialization/built_value/class_serializer}}{{#vendorExtensions.x-is-parent}}

{{>serialization/built_value/class_concrete}}{{/vendorExtensions.x-is-parent}}
{{>serialization/built_value/class_inline_enums}}
{{>serialization/built_value/class_inline_enums}}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{^discriminator.isEnum}}
extension {{classname}}DiscriminatorExt on {{classname}} {
String? get discriminatorValue {
{{#mappedModels}}
Expand All @@ -17,4 +18,27 @@ extension {{classname}}BuilderDiscriminatorExt on {{classname}}Builder {
{{/mappedModels}}
return null;
}
}
}
{{/discriminator.isEnum}}
{{#discriminator.isEnum}}
extension {{classname}}DiscriminatorExt on {{classname}} {
{{discriminator.propertyType}} get discriminatorValue {
{{#mappedModels}}
if (this is {{modelName}}) {
return {{discriminator.propertyType}}.valueOf(r'{{mappingName}}');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Passing the wire discriminator value (mappingName) to built_value EnumClass.valueOf() is incorrect because valueOf expects the generated Dart constant name, not the wire name. When the wire value differs from the Dart identifier (e.g., in-progress vs inProgress), this will throw at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/dart/libraries/dio/serialization/built_value/class_discriminator.mustache, line 29:

<comment>Passing the wire discriminator value (`mappingName`) to built_value `EnumClass.valueOf()` is incorrect because `valueOf` expects the generated Dart constant name, not the wire name. When the wire value differs from the Dart identifier (e.g., `in-progress` vs `inProgress`), this will throw at runtime.</comment>

<file context>
@@ -17,4 +18,27 @@ extension {{classname}}BuilderDiscriminatorExt on {{classname}}Builder {
+        {{#mappedModels}}
+        if (this is {{modelName}}) {
+            return {{discriminator.propertyType}}.valueOf(r'{{mappingName}}');
+        }
+        {{/mappedModels}}
+        throw UnsupportedError('Invalid discriminator value for {{classname}}');
</file context>

{{/mappedModels}}
throw UnsupportedError('Invalid discriminator value for {{classname}}');
}
}
extension {{classname}}BuilderDiscriminatorExt on {{classname}}Builder {
{{discriminator.propertyType}} get discriminatorValue {
{{#mappedModels}}
if (this is {{modelName}}Builder) {
return {{discriminator.propertyType}}.valueOf(r'{{mappingName}}');
}
{{/mappedModels}}
throw UnsupportedError('Invalid discriminator value for {{classname}}Builder');
}
}
{{/discriminator.isEnum}}
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,32 @@ public void verifyWebhookImports() throws IOException {
Assert.assertFalse(apiContent.contains("&#x3D;"),
"Webhook should not contain HTML entity encoding (bug #22586 symptom)");
}

@Test
public void verifyEnumDiscriminatorUsesEnumType() throws IOException {
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("dart-dio")
.setInputSpec("src/test/resources/3_0/dart-dio/enum-discriminator.yaml")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
.addAdditionalProperty(CodegenConstants.SERIALIZATION_LIBRARY, DartDioClientCodegen.SERIALIZATION_LIBRARY_BUILT_VALUE)
.addAdditionalProperty(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, false)
.addAdditionalProperty(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, true);

ClientOptInput opts = configurator.toClientOptInput();

Generator generator = new DefaultGenerator().opts(opts);
List<File> files = generator.generate();
files.forEach(File::deleteOnExit);

File modelFile = new File(output, "lib/src/model/merchant_details_dto.dart");
TestUtils.ensureContainsFile(files, output, "lib/src/model/merchant_details_dto.dart");

String contents = Files.readString(modelFile.toPath(), StandardCharsets.UTF_8);
Assert.assertTrue(contents.contains("MerchantTypeEnum get discriminatorValue"));
Assert.assertTrue(contents.contains("MerchantTypeEnum.valueOf(r'TYPE_A')"));
Assert.assertFalse(contents.contains("String? get discriminatorValue"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
openapi: 3.0.3
info:
title: Dart Dio Enum Discriminator
version: 1.0.0
paths:
/merchant:
get:
responses:
'200':
description: ok
content:
application/json:
schema:
$ref: '#/components/schemas/MerchantDetailsDto'
components:
schemas:
MerchantDetailsDto:
type: object
properties:
uuid:
type: string
format: uuid
merchantType:
$ref: '#/components/schemas/MerchantTypeEnum'
required:
- uuid
- merchantType
discriminator:
propertyName: merchantType
mapping:
TYPE_A: '#/components/schemas/MerchantDetailsTypeADto'
TYPE_B: '#/components/schemas/MerchantDetailsTypeBDto'
MerchantDetailsTypeADto:
allOf:
- $ref: '#/components/schemas/MerchantDetailsDto'
- type: object
properties:
aField:
type: string
MerchantDetailsTypeBDto:
allOf:
- $ref: '#/components/schemas/MerchantDetailsDto'
- type: object
properties:
bField:
type: string
MerchantTypeEnum:
type: string
enum:
- TYPE_A
- TYPE_B
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,25 @@ abstract class Fruit implements Built<Fruit, FruitBuilder> {
}

extension FruitDiscriminatorExt on Fruit {
String? get discriminatorValue {
FruitType get discriminatorValue {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: discriminatorValue returns FruitType while discriminatorMapping remains Map<String, Type>, creating a type mismatch between complementary discriminator APIs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/fruit.dart, line 49:

<comment>`discriminatorValue` returns `FruitType` while `discriminatorMapping` remains `Map<String, Type>`, creating a type mismatch between complementary discriminator APIs.</comment>

<file context>
@@ -46,25 +46,25 @@ abstract class Fruit implements Built<Fruit, FruitBuilder> {
 
 extension FruitDiscriminatorExt on Fruit {
-    String? get discriminatorValue {
+    FruitType get discriminatorValue {
         if (this is Apple) {
-            return r'APPLE';
</file context>

if (this is Apple) {
return r'APPLE';
return FruitType.valueOf(r'APPLE');
}
if (this is Banana) {
return r'BANANA';
return FruitType.valueOf(r'BANANA');
}
return null;
throw UnsupportedError('Invalid discriminator value for Fruit');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: discriminatorValue throws for valid base Fruit/FruitBuilder wrapper instances instead of falling back to the known fruitType field.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/client/petstore/dart-dio/oneof_polymorphism_and_inheritance/lib/src/model/fruit.dart, line 56:

<comment>`discriminatorValue` throws for valid base `Fruit`/`FruitBuilder` wrapper instances instead of falling back to the known `fruitType` field.</comment>

<file context>
@@ -46,25 +46,25 @@ abstract class Fruit implements Built<Fruit, FruitBuilder> {
+            return FruitType.valueOf(r'BANANA');
         }
-        return null;
+        throw UnsupportedError('Invalid discriminator value for Fruit');
     }
 }
</file context>

}
}
extension FruitBuilderDiscriminatorExt on FruitBuilder {
String? get discriminatorValue {
FruitType get discriminatorValue {
if (this is AppleBuilder) {
return r'APPLE';
return FruitType.valueOf(r'APPLE');
}
if (this is BananaBuilder) {
return r'BANANA';
return FruitType.valueOf(r'BANANA');
}
return null;
throw UnsupportedError('Invalid discriminator value for FruitBuilder');
}
}

Expand Down
Loading