OpenAPITools / OpenAPITools/openapi-generator

[dart-dio-next][DISCUSSION] non-nullable model properties without a default value should be treated as required by default

Open
#10,972 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Enhancement: Feature
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

currently when generating model types the property's nullability is determined by 2 things

  1. the property must be nullable
  2. the property must be required

for example, this model:

"PaymentInfo": {
  "type": "object",
  "properties": {
    "redirectUrl": {
      "type": "string",
      "nullable": true
    },
    "invoiceId": {
      "type": "integer",
      "format": "int64"
    }
  },
  "additionalProperties": false
}

is generated as:

import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';

part 'payment_info.g.dart';

/// PaymentInfo
///
/// Properties:
/// * [redirectUrl]
/// * [invoiceId]
abstract class PaymentInfo implements Built<PaymentInfo, PaymentInfoBuilder> {
  @BuiltValueField(wireName: r'redirectUrl')
  String? get redirectUrl;

  @BuiltValueField(wireName: r'invoiceId')
  int? get invoiceId;

  PaymentInfo._();

  @BuiltValueHook(initializeBuilder: true)
  static void _defaults(PaymentInfoBuilder b) => b;

  factory PaymentInfo([void updates(PaymentInfoBuilder b)]) = _$PaymentInfo;

  @BuiltValueSerializer(custom: true)
  static Serializer<PaymentInfo> get serializer => _$PaymentInfoSerializer();
}
//stripped serializer for simplicty

as you can see, the invoiceId here is marked as nullable in the generated object, this is counter intuitive and leads to spamming ! everywhere in code

this problem is due to a limitation in built value, see:

  1. https://github.com/google/built_value.dart/issues/1050
  2. https://github.com/google/built_value.dart/issues/912

my suggestion is to provide an alternative factory method in addition to the built-in builder
e.g.

abstract class PaymentInfo implements Built<PaymentInfo, PaymentInfoBuilder> {
  @BuiltValueField(wireName: r'redirectUrl')
  String? get redirectUrl;

  @BuiltValueField(wireName: r'invoiceId')
  int get invoiceId;

  factory PaymentInfo.strict({
    //parameters defined to reflict the matching OAS3 specification
    String? redirectUrl,
    required int invoiceId,
  }) {
    //delegate the construction to the built-in builder
    final resB = PaymentInfoBuilder();
    resB.redirectUrl = redirectUrl;
    resB.invoiceId = invoiceId;
    return resB.build();
  }

  PaymentInfo._();

  @BuiltValueHook(initializeBuilder: true)
  static void _defaults(PaymentInfoBuilder b) => b;

  factory PaymentInfo([void updates(PaymentInfoBuilder b)]) = _$PaymentInfo;

  @BuiltValueSerializer(custom: true)
  static Serializer<PaymentInfo> get serializer => _$PaymentInfoSerializer();
}

the signatures would then be handled as such:

  1. non-nullable and required model property
    • non-nullable class property
    • required non-nullable factory parameter if there is no default value provided
    • non-nullable factory parameter if there is a default value provided
  2. nullable and required model properties
    • nullable class property
    • required nullable factory parameter if there is no default value provided
    • nullable factory parameter if there is a default value provided
  3. non-nullable and non-required model properties is this allowed ??
    • non-nullable class property
    • required non-nullable factory parameter if there is no default value provided
    • non-nullable factory parameter if there is a default value provided
  4. nullable and non-required model properties
    • nullable class property
    • nullable factory parameter if there is no default value provided
    • nullable factory parameter if there is a default value provided

what are your opinions on this?

@jaumard (2018/09) @josh-burton (2019/12) @amondnet (2019/12) @sbu-WBT (2020/12) @kuhnroyal (2020/12) @agilob (2020/12)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the dart-dio-next model-generation behavior described in the issue and the linked built_value limitations. Compare the generated PaymentInfo example with the proposed factory signatures and determine how defaults, nullability, and required properties should map to generated Dart models. Done means the agreed behavior is implemented and covered by generator tests, though no test or source file is named here.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.