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

Binary OpenAPI format not working as expected #364

Closed
missingmario opened this issue Nov 6, 2023 · 8 comments
Closed

Binary OpenAPI format not working as expected #364

missingmario opened this issue Nov 6, 2023 · 8 comments
Labels
kind/support Adopter support requests.

Comments

@missingmario
Copy link

The problem

I have been using the 0.2.x versions of the swift-openapi-* tools for a while now. I recently tried updating my code to use the "new" 0.3.x versions and stumbled upon the following issue.

As shown in the code below, the 0.3.x ignore any string field with a binary format.

I'm aware that this project is on a 0.x.y version and is therefore expected to have major changes from version to version without any guarantee of API stability; but I cannot find a way to make the generator produce properties with a Data type (without having to manually convert from binary to base64 on both the server and the client) with the new versions.

Thank you in advance for reading me!

Spec

openapi: 3.1.0
info:
  title: Gendemo
  description: Lorem.
  version: 1.0.0
paths:
  /hello:
    get:
      description: Lorem.
      operationId: hello
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
components:
  schemas:
    Message:
      type: object
      properties:
        key:
          type: string
        value:
          type: string
          format: binary

Code generated on 0.2.x

  • swift-openapi-generator: 0.2.3
  • swift-openapi-runtime: 0.2.5
/// - Remark: Generated from `#/components/schemas/Message`.
public struct Message: Codable, Hashable, Sendable {
    /// - Remark: Generated from `#/components/schemas/Message/key`.
    public var key: Swift.String?
    /// - Remark: Generated from `#/components/schemas/Message/value`.
    public var value: Foundation.Data?
    /// Creates a new `Message`.
    ///
    /// - Parameters:
    ///   - key:
    ///   - value:
    public init(key: Swift.String? = nil, value: Foundation.Data? = nil) {
        self.key = key
        self.value = value
    }
    public enum CodingKeys: String, CodingKey {
        case key
        case value
    }
}

Code generated on 0.3.x

  • swift-openapi-generator: 0.3.3
  • swift-openapi-runtime: 0.3.6
/// - Remark: Generated from `#/components/schemas/Message`.
public struct Message: Codable, Hashable, Sendable {
    /// - Remark: Generated from `#/components/schemas/Message/key`.
    public var key: Swift.String?
    /// Creates a new `Message`.
    ///
    /// - Parameters:
    ///   - key:
    public init(key: Swift.String? = nil) {
        self.key = key
    }
    public enum CodingKeys: String, CodingKey {
        case key
    }
}
@missingmario
Copy link
Author

Upon further inspection of the repo, I found out that #362 kind of solves this issue. I should've looked into the recent commits before posting an issue.

/// - Remark: Generated from `#/components/schemas/Message`.
public struct Message: Codable, Hashable, Sendable {
    /// - Remark: Generated from `#/components/schemas/Message/key`.
    public var key: Swift.String?
    /// - Remark: Generated from `#/components/schemas/Message/value`.
    public var value: OpenAPIRuntime.Base64EncodedData?
    /// Creates a new `Message`.
    ///
    /// - Parameters:
    ///   - key:
    ///   - value:
    public init(
        key: Swift.String? = nil,
        value: OpenAPIRuntime.Base64EncodedData? = nil
    ) {
        self.key = key
        self.value = value
    }
    public enum CodingKeys: String, CodingKey {
        case key
        case value
    }
}

Is the usage of Base64EncodedData the expected result from now on?

@czechboy0
Copy link
Contributor

Kind of. Are you sure you want to use format: binary and not format: byte?

In OpenAPI 3.1, those would be spelled contentEncoding: binary (raw octets) and contentEncoding: base64.

If you're sending these values in JSON, the former wouldn't work, only the latter. And the new base64 special type was added recently as well, after 0.3.0.

To summarize, you should be able to change from:

          type: string
          format: binary

to

          type: string
          format: byte

And it should work even with the latest released version (the OpenAPIKit bump only happened on main, hasn't been released yet).

Please confirm that this addresses your issue 🙂

@czechboy0 czechboy0 added the kind/support Adopter support requests. label Nov 6, 2023
@czechboy0
Copy link
Contributor

Oh I forgot this is currently still hidden behind a feature flag, you'll need to enable it:

@missingmario
Copy link
Author

Thank you for your response! Using format: byte kind of works, the property shows up as a Swift.String type (instead of ignoring it).

I suppose the base64DataEncodingDecoding flag enables the automatic encoding and decoding of the Base64EncodedData to Swift.Data?

Is there any documentation on how to use the feature flag? Or do I have to fork the generator in order to add it?

@czechboy0
Copy link
Contributor

It's documented here: https://swiftpackageindex.com/apple/swift-openapi-generator/0.3.3/documentation/swift-openapi-generator/configuring-the-generator#Create-a-configuration-file

You'll just add it to your config file, such as:

generate:
  - types
  - client
featureFlags:
  - base64DataEncodingDecoding

Then the generator will produce OpenAPIRuntime.Base64EncodedData instead of Swift.String.

@missingmario
Copy link
Author

It is indeed documented there, although there aren't any example config files including feature flags (which is understandable since there's only one for now). It might be a good idea to add additional examples that show how to do this in the docs.

Seems to work fine now.

Out of curiosity, is there a particular reason why OpenAPIRuntime.Base64EncodedData is used instead of Swift.Data? The previous conversion to Swift.Data seemed way more comfortable to work with.

@czechboy0
Copy link
Contributor

Data is a Foundation-specific type, and we use ArraySlice<UInt8> as the currency type for raw data, for example see HTTPBody.

Also, the fact that Data encodes as base64 is not actually defined in its API, so by creating a custom type we can enforce the exact OpenAPI semantics, and evolve it independently of Data's implementation details over time.

@missingmario
Copy link
Author

Makes a lot of sense. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/support Adopter support requests.
Projects
None yet
Development

No branches or pull requests

2 participants