Skip to content

[BUG] Dart incorrect nullable when required #21510

Open
@JohnGalt1717

Description

@JohnGalt1717

Bug Report Checklist

  • [ X] Have you provided a full/minimal spec to reproduce the issue?
  • [ X] Have you validated the input using an OpenAPI validator?
  • [ X] Have you tested with the latest master to confirm the issue still exists?
  • [ X] Have you searched for related issues/PRs?
  • [ X] What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

Right now nullable but required properties are marked as not nullable despite being clearly set to nullable. #21508 references the inverse issue of this with includeIfNull.

If the field is marked as nullable, then the property generated in dart should be nullable. There is no case where a field marked as nullable shouldn't, yet it is flagging repeatCycle as not nullable in dart while it clearly knows thati t was set to nullable.

openapi-generator version

7.12.0

OpenAPI declaration file content or url
"TodoDto": {
        "type": "object",
        "description": "Todo",
        "additionalProperties": false,
        "required": [
          "id",
          "createdOn",
          "createdBy",
          "callLogId",
          "subject",
          "description",
          "contacts",
          "categories",
          "reminderDateTime",
          "repeatCycle",
          "remindLocation",
          "status",
          "priority"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "createdOn": {
            "type": "string",
            "description": "Created On",
            "format": "date-time",
            "nullable": false
          },
          "createdBy": {
            "description": "Created By",
            "nullable": false,
            "$ref": "#/components/schemas/ContactRefDto"
          },
          "callLogId": {
            "type": "string",
            "description": "Call Log Referenced",
            "format": "uuid",
            "nullable": true
          },
          "subject": {
            "type": "string",
            "description": "Subject",
            "nullable": false
          },
          "description": {
            "type": "string",
            "description": "Description",
            "nullable": true
          },
          "contacts": {
            "type": "array",
            "description": "Contacts",
            "nullable": false,
            "items": {
              "$ref": "#/components/schemas/ContactRefDto"
            }
          },
          "categories": {
            "type": "array",
            "description": "Categories",
            "nullable": false,
            "items": {
              "$ref": "#/components/schemas/CategoryRefDto"
            }
          },
          "reminderDateTime": {
            "type": "string",
            "description": "Remind On",
            "format": "date-time",
            "nullable": true
          },
          "repeatCycle": {
            "description": "Repeat Cycle",
            "nullable": true,
            "$ref": "#/components/schemas/RepeatCycles"
          },
          "remindLocation": {
            "description": "Remind Location",
            "nullable": true,
            "$ref": "#/components/schemas/Location"
          },
          "status": {
            "description": "Status",
            "nullable": false,
            "$ref": "#/components/schemas/TodoStatuses"
          },
          "priority": {
            "description": "Priority",
            "nullable": false,
            "$ref": "#/components/schemas/Priorities"
          }
        }
      },

Results in:

@JsonSerializable(
  checked: true,
  createToJson: true,
  disallowUnrecognizedKeys: false,
  explicitToJson: true,
)
class TodoDto {
  /// Returns a new [TodoDto] instance.
  TodoDto({
    required this.id,
    required this.createdOn,
    required this.createdBy,
    required this.callLogId,
    required this.subject,
    required this.description,
    required this.contacts,
    required this.categories,
    required this.reminderDateTime,
    required this.repeatCycle,
    required this.remindLocation,
    required this.status,
    required this.priority,
  });

  @JsonKey(
    name: r'id',
    required: true,
    includeIfNull: true,
  )
  final String? id;

  /// Created On
  @JsonKey(
    name: r'createdOn',
    required: true,
    includeIfNull: true,
  )
  final DateTime createdOn;

  @JsonKey(
    name: r'createdBy',
    required: true,
    includeIfNull: true,
  )
  final ContactRefDto createdBy;

  /// Call Log Referenced
  @JsonKey(
    name: r'callLogId',
    required: true,
    includeIfNull: true,
  )
  final String? callLogId;

  /// Subject
  @JsonKey(
    name: r'subject',
    required: true,
    includeIfNull: true,
  )
  final String subject;

  /// Description
  @JsonKey(
    name: r'description',
    required: true,
    includeIfNull: true,
  )
  final String? description;

  /// Contacts
  @JsonKey(
    name: r'contacts',
    required: true,
    includeIfNull: true,
  )
  final List<ContactRefDto> contacts;

  /// Categories
  @JsonKey(
    name: r'categories',
    required: true,
    includeIfNull: true,
  )
  final List<CategoryRefDto> categories;

  /// Remind On
  @JsonKey(
    name: r'reminderDateTime',
    required: true,
    includeIfNull: true,
  )
  final DateTime? reminderDateTime;

  @JsonKey(
    name: r'repeatCycle',
    required: true,
    includeIfNull: true,
  )
  final RepeatCycles repeatCycle;

  @JsonKey(
    name: r'remindLocation',
    required: true,
    includeIfNull: true,
  )
  final Location remindLocation;

  @JsonKey(
    name: r'status',
    required: true,
    includeIfNull: true,
  )
  final TodoStatuses status;

  @JsonKey(
    name: r'priority',
    required: true,
    includeIfNull: true,
  )
  final Priorities priority;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is TodoDto &&
          other.id == id &&
          other.createdOn == createdOn &&
          other.createdBy == createdBy &&
          other.callLogId == callLogId &&
          other.subject == subject &&
          other.description == description &&
          other.contacts == contacts &&
          other.categories == categories &&
          other.reminderDateTime == reminderDateTime &&
          other.repeatCycle == repeatCycle &&
          other.remindLocation == remindLocation &&
          other.status == status &&
          other.priority == priority;

  @override
  int get hashCode =>
      (id == null ? 0 : id.hashCode) +
      createdOn.hashCode +
      createdBy.hashCode +
      (callLogId == null ? 0 : callLogId.hashCode) +
      subject.hashCode +
      (description == null ? 0 : description.hashCode) +
      contacts.hashCode +
      categories.hashCode +
      (reminderDateTime == null ? 0 : reminderDateTime.hashCode) +
      repeatCycle.hashCode +
      remindLocation.hashCode +
      status.hashCode +
      priority.hashCode;

  factory TodoDto.fromJson(Map<String, dynamic> json) =>
      _$TodoDtoFromJson(json);

  Map<String, dynamic> toJson() => _$TodoDtoToJson(this);

  @override
  String toString() {
    return toJson().toString();
  }
}

Generation Details
				"@openapitools/openapi-generator-cli",
				"generate",
				"-i",
				"http://localhost:16049/help/v1/openapi.json",
				"-g",
				"dart-dio",
				"-c",
				"open-generator-config.yaml",
				"--enable-post-process-file"

Steps to reproduce

Generate with the above command against the snippet provided.

Related issues/PRs

#21508

Suggest a fix

If the field is defined as "nullable": true, then the property generated 100% of the time should be nullable. Per #21508, the required array defines if it should be sent even if null, not the status as nullable. If OpenAPI had a default value then that is what it would be set to if not required and not nullable.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions