Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not supported specify nullable types via anyOf #84

Closed
StarProxima opened this issue Sep 18, 2023 · 5 comments
Closed

Not supported specify nullable types via anyOf #84

StarProxima opened this issue Sep 18, 2023 · 5 comments
Labels
enhancement New feature or request help wanted Extra attention is needed question Further information is requested

Comments

@StarProxima
Copy link
Collaborator

StarProxima commented Sep 18, 2023

The new pydantic 2.1 on backend (Python, FastApi) describes nullable types in the anyOf specification. It would be nice to support this way of specifying a nullable type.

Small example:

 "last_name": {
    "anyOf": [
      {
        "type": "string"
      },
      {
        "type": "null"
      }
    ],
    "title": "Last Name"
 }

Result:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@Freezed()
class User with _$User {
  const factory User({
    /// Адрес электронной почты
    required String email,
    /// Имя пользователя
    @JsonKey(name: 'first_name')
    required String firstName,
    /// Фамилия пользователя
    @JsonKey(name: 'last_name')
    required Object lastName,
    /// Никнейм пользователя
    required String username,
    /// Уникальный id пользователя в базе данных
    required int id,
    /// Уникальный uuid пользователя в базе данных
    required String uuid,
    /// Время создания аккаунта пользователя
    @JsonKey(name: 'created_at')
    required DateTime createdAt,
    /// Время удаления аккаунта пользователя
    @JsonKey(name: 'deleted_at')
    required Object deletedAt,
    /// Время изменения аккаунта пользователя, либо обновления refresh токена
    @JsonKey(name: 'updated_at')
    required Object updatedAt,
  }) = _User;
  
  factory User.fromJson(Map<String, Object?> json) => _$UserFromJson(json);
}

Expected result:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@Freezed()
class User with _$User {
  const factory User({
    /// Адрес электронной почты
    required String email,
    /// Имя пользователя
    @JsonKey(name: 'first_name')
    required String firstName,
    /// Фамилия пользователя
    @JsonKey(name: 'last_name')
    required String? lastName,
    /// Никнейм пользователя
    required String username,
    /// Уникальный id пользователя в базе данных
    required int id,
    /// Уникальный uuid пользователя в базе данных
    required String uuid,
    /// Время создания аккаунта пользователя
    @JsonKey(name: 'created_at')
    required DateTime createdAt,
    /// Время удаления аккаунта пользователя
    @JsonKey(name: 'deleted_at')
    required DateTime? deletedAt,
    /// Время изменения аккаунта пользователя, либо обновления refresh токена
    @JsonKey(name: 'updated_at')
    required DateTime? updatedAt,
  }) = _User;
  
  factory User.fromJson(Map<String, Object?> json) => _$UserFromJson(json);
}

OpenApi:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Microservice Auth API methods",
    "version": "0.2.2"
  },
  "paths": {},
  "components": {
    "schemas": {
      "User": {
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "title": "Email",
            "description": "Адрес электронной почты"
          },
          "first_name": {
            "type": "string",
            "title": "First Name",
            "description": "Имя пользователя"
          },
          "last_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Name",
            "description": "Фамилия пользователя"
          },
          "username": {
            "type": "string",
            "title": "Username",
            "description": "Никнейм пользователя"
          },
          "id": {
            "type": "integer",
            "title": "Id",
            "description": "Уникальный id пользователя в базе данных"
          },
          "uuid": {
            "type": "string",
            "format": "uuid",
            "title": "Uuid",
            "description": "Уникальный uuid пользователя в базе данных"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "Время создания аккаунта пользователя"
          },
          "deleted_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Deleted At",
            "description": "Время удаления аккаунта пользователя"
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Updated At",
            "description": "Время изменения аккаунта пользователя, либо обновления refresh токена"
          }
        },
        "type": "object",
        "required": [
          "email",
          "first_name",
          "last_name",
          "username",
          "id",
          "uuid",
          "created_at",
          "deleted_at",
          "updated_at"
        ],
        "title": "User"
      }
    },
    "tags": [
      {
        "name": "Auth",
        "description": "Auth Management"
      }
    ]
  }
}
@Carapacik Carapacik added the enhancement New feature or request label Sep 18, 2023
@Carapacik
Copy link
Owner

Is there any annotation in your backend so that nullable is used?

"last_name": {
    "type": "string",
    "nullable": true,
    "title": "Last Name"
 }

@StarProxima
Copy link
Collaborator Author

Is there any annotation in your backend so that nullable is used?

"last_name": {
    "type": "string",
    "nullable": true,
    "title": "Last Name"
 }

After talking to the backend team, there is an option to add nullable, but the type will also be set via anyOf:

 "last_name": {
    "anyOf": [
      {
        "type": "string"
      },
      {
        "type": "null"
      }
    ],
    "nullable": true,
    "title": "Last Name"
 }

So far, the only solution to the problem seems to me to support setting nullable type via anyOf.

@StarProxima
Copy link
Collaborator Author

@Carapacik This feature looks more difficult than adding a flag. I think it is better to be implemented by a maintainer, because you need a good understanding of the package structure. What do you think about it?

@Carapacik
Copy link
Owner

Already think about that in #5

@Carapacik Carapacik added help wanted Extra attention is needed question Further information is requested labels Sep 21, 2023
@StarProxima
Copy link
Collaborator Author

Found a workaround using a self-describing class on the backend:

Annotated[Union[UUID | SkipJsonSchema[None]], Field(json_schema_extra=lambda x: x.pop('default', None))]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants