apple / apple/swift-openapi-generator

Latest yams results in incomplete generation of types

Open
#565 23 comments 1 reaction 0 assignees View on GitHub
kind/enhancement
Dominant language
Swift
Stars
2k
Forks
182
Avg merge
13h 28m
Merged PRs (30d)
5

Description

### Description

I'm not exactly sure where this bug is arising from (e.g. swift-openapi-generator or one of its dependencies like OpenAPIKit).

In short, using `swift-openapi-generator` with when the `yams` version is 5.0.X, the correct swift code (Types) is generated as part of generating types and client. When yams is 5.1.X, the generated code is missing fields. This is with an OpenAPI 3.1 json.

### Reproduction

I have attached an example openapi.json along with the difference in the resulting generated swift code. Specifically, in looking at `Components.Schemas.NewAccount` and `Components.Schemas.Account` you will see that the one created with yams 5.0.X contains a `phoneNumberPayload` as part of each. There is also a defined `PhoneNumber` struct and curiously it is not linked or used instead of `phoneNumberPayload` but that is perhaps a different improvement. For now, the `phoneNumberPayload` is usable with yams 5.0.X.

However, when building with yams 5.1.X, the generated `NewAccount` and `Account` do not contain `phoneNumberPayload`. It is dropped but should be there.

I have attached the example openapi.json and the example generated outputs.

[openapi.json](https://github.com/apple/swift-openapi-generator/files/15029882/openapi.json)
[generated_types_yams5.0.swift.txt](https://github.com/apple/swift-openapi-generator/files/15029884/generated_types_yams5.0.swift.txt)
[generated_types_yams5.1.swift.txt](https://github.com/apple/swift-openapi-generator/files/15029886/generated_types_yams5.1.swift.txt)

I'm not sure if this is related but Yams 5.1.0 release notes the following breaking change:

> Change how empty strings are decoded into nullable properties.
key: "" previously decoded into
struct Value: Codable { let key: String? } as Value(key: nil)
whereas after this change it decodes as Value(key: "").
This could be a breaking change if you were relying on the previous
semantics.

If it is helpful, the openapi.json was generated via Rust using Aide with Axum.

Rust struct definition

```rust
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]
pub enum Role {
#[default]
#[serde(rename="user")]
User,
#[serde(rename="admin")]
Admin,
}

#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]
pub struct PhoneNumber {
/// country code must contain only a set of digits 0-9
/// it should be formatted without leading zeros.
/// for example: U.S.A. should be `1` without `+` and without `00` e.g. not `001`
/// it should be assumed that the `+` would be added by the system
#[serde(rename = "countryCode")]
pub country_code: String,

/// the number should be a set of digits "333555222"
/// without parens or dashes. It should not contain the country code
/// however it should be formatted such that combining a `+` countryCode number
/// would result in a complete functional international phone number
#[serde(rename = "number")]
pub number: String
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]
pub struct NewAccount {

/// username as A-Za-z0-9
#[serde(rename = "username")]
pub username: String,

/// email address
#[serde(rename = "email")]
pub email: Option,

/// phone number
#[serde(rename = "phoneNumber")]
pub phone_number: Option,

/// The code (string) for referrals
#[serde(rename = "role")]
pub role: Role,

}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]
pub struct Account {

/// internal uuid for the user
#[serde(rename = "id")]
pub id: Uuid,

/// username as A-Za-z0-9
#[serde(rename = "username")]
pub username: String,

/// email address
#[serde(rename = "email")]
pub email: Option,

/// phone number
#[serde(rename = "phoneNumber")]
pub phone_number: Option,

/// The code (string) for referrals
#[serde(rename = "role")]
pub role: Role,

}
```

### Package version(s)

OpenAPIKit 3.1.3
swift-algorithms 1.2.0
swift-argument-parser 1.3.1
swift-collections 1.1.0
swift-http-types 1.0.3
swift-numerics 1.0.2
swift-openapi-generator 1.2.1
swift-openapi-runtime 1.4.0
swift-openapi-urlsession 1.0.1
Yams 5.0.6 or 5.1.0 as described resulting in the problem

### Expected behavior

The generated types for `Account` and `NewAccount` should contain `phoneNumberPayload` respectively (or even better if it directly contained `PhoneNumber`) for any version of Yams.

### Environment

```shell
$ swift -version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: x86_64-apple-macosx14.0
```

### Additional information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by reproducing generation with the attached openapi.json using Yams 5.0.6 and 5.1.0, then compare the generated_types_yams5.0.swift.txt and generated_types_yams5.1.swift.txt outputs for Account and NewAccount. Trace the generator or dependency path that drops phoneNumberPayload; done means both generated types retain that field across the reported Yams versions.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.