Skip to content

OneOf with enum string and object variants incorrectly handled #184

Closed
@SolidTux

Description

@SolidTux

Description of the bug

When using "one of" fields where some variants are string enums and some are objects, the string enums are never deserialized.

Steps to reproduce

Use oneOf with differntly typed enums. Then the generated code only handles maps in the JSON and not strings.

  /// Returns a new [QueueResult] instance and imports its values from
  /// [value] if it's a [Map], null otherwise.
  // ignore: prefer_constructors_over_static_methods
  static QueueResult? fromJson(dynamic value) {
    if (value is Map) {
      final json = value.cast<String, dynamic>();

      // Ensure that the map contains the required keys.
      // Note 1: the values aren't checked for validity beyond being non-null.
      // Note 2: this code is stripped in release mode!
      assert(() {
        requiredKeys.forEach((key) {
          assert(json.containsKey(key),
              'Required key "QueueResult[$key]" is missing from JSON.');
          assert(json[key] != null,
              'Required key "QueueResult[$key]" has a null value in JSON.');
        });
        return true;
      }());

      return QueueResult(
        error: mapValueOfType<String>(json, r'error')!,
      );
    }
    return null;
  }

Additionally, it has the object variants required fields as non-nullable members:

class QueueResult {
  /// Returns a new [QueueResult] instance.
  QueueResult({
    required this.error,
  });

  /// Error occurred
  String error;
...
}

Minimal openapi specification

{
    "openapi": "3.1.0",
    "info": {
        "title": "test",
        "version": "1.0.0"
    },
    "paths": {
        "/status": {
            "get": {
                "tags": [],
                "summary": "Get user status",
                "operationId": "get_status",
                "responses": {
                    "200": {
                        "description": "User status",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UserStatus"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "QueueResult": {
                "oneOf": [
                    {
                        "type": "string",
                        "description": "Service not enabled for user",
                        "enum": [
                            "not_enabled"
                        ]
                    },
                    {
                        "type": "string",
                        "description": "Successfully queued",
                        "enum": [
                            "success"
                        ]
                    },
                    {
                        "type": "object",
                        "description": "Error occurred",
                        "required": [
                            "error"
                        ],
                        "properties": {
                            "error": {
                                "type": "string",
                                "description": "Error occurred"
                            }
                        }
                    }
                ],
                "description": "Queue attempt result"
            },
            "UserStatus": {
                "type": "object",
                "description": "Status",
                "required": [
                    "server_version",
                    "enabled"
                ],
                "properties": {
                    "enabled": {
                        "type": "boolean",
                        "description": "Service enabled"
                    },
                    "last_result": {
                        "oneOf": [
                            {
                                "type": "null"
                            },
                            {
                                "$ref": "#/components/schemas/QueueResult",
                                "description": "Result of last queue attempt"
                            }
                        ]
                    }
                }
            }
        }
    }
}

Annotation used

import 'package:openapi_generator_annotations/openapi_generator_annotations.dart';

@Openapi(
  additionalProperties: AdditionalProperties(
    pubName: 'test_api',
    pubAuthor: 'Daniel Hauck',
  ),
  inputSpec: InputSpec(path: 'openapi.json'),
  generatorName: Generator.dart,
  outputDirectory: 'api',
)
class TestApi {}

Expected behavior

All variants are correctly deserialized.

Logs


Screenshots

No response

Platform

Linux

Library version

6.1.0

Flutter version

3.23.2

Flutter channel

stable

Additional context

The spec is generated using utoipa from a Rust code similar to the following:

/// Status
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub struct UserStatus {
    /// Result of last queue attempt
    pub last_result: Option<QueueResult>,
    /// Service enabled
    pub enabled: bool,
}

/// Queue attempt result
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueueResult {
    /// Service not enabled for user
    NotEnabled,
    /// Successfully queued
    Success,
    Error(String),
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    base library issueReport here. https://github.com/OpenAPITools/openapi-generator/issues

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions