apple / apple/swift-openapi-generator

Support for XML request body and response body

Open
#556 10 comments 0 reactions 1 assignee Claimed by @ugocottin View on GitHub
area/generator area/runtime kind/feature size/M
Dominant language
Swift
Stars
2k
Forks
182
Avg merge
13h 28m
Merged PRs (30d)
5

Description

### Motivation

I want to be able de generate client and server from an OpenAPI specification, for an API which use XML as content type for request and response body.

Currently, `application/xml` contents for request and response are not generated by `swift-openapi-generator`, and only `OpenAPIRuntime.HTTPBody` enum associated value is available for xml content.

A Workaround is to use an external XML encoder and decoder, like [CoreOffice XMLCoder](https://github.com/CoreOffice/XMLCoder), and make the encoding from / decoding to `HTTPBody`.
```swift
struct Credentials: Encodable {
var username: String
var password_hash: String
}

struct Token: Decodable {
var token: String
}
let credentials = Credentials(username: "johnapplessed", password_hash: "d2hhdCBkaWQgeW91IGV4cGVjdD8=")
let encoder = XMLEncoder()
let encoded = try encoder.encode(credentials)
let response = try await client.authenticate(body: .xml(HTTPBody(encoded)))
let xmlBody = try response.ok.body.xml
let xmlData = try await Data(collecting: xmlBody, upTo: .max)
let decoder = XMLDecoder()
let decoded = try decoder.decode(Token.self, from: xmlData)
let token = decoded.token
```

### Proposed solution

One quick solution could be to implement a new `CodingStrategy` for xml in `swift-openapi-generator`.
For `swift-openapi-runtime`, we could add the following methods to `Converter`:

| Client/server | Set/get | Schema location | Coding strategy | Optional/required | Method name |
| ------------- | ------- | --------------- | --------------- | ------------------| --------------------------- |
| client | set | request body | XML | optional | setOptionalRequestBodyAsXML |
| client | set | request body | XML | required | setRequiredRequestBodyAsXML |
| client | get | response body | XML | required | getResponseBodyAsXML |
| server | get | request body | XML | optional | getOptionalRequestBodyAsXML |
| server | get | request body | XML | required | getRequiredRequestBodyAsXML |
| server | set | response body | XML | required | setResponseBodyAsXML |

Encoding / Decoding logic can be implemented into these methods, with an XML encoder / decoder.
As `Converter` have `encoder` and `decoder` properties as type `JSONEncoder` and `JSONDecoder`, we could:
1. Rename `encoder` to `jsonEncoder` and `decoder` to `jsonDecoder`, and instantiate a `xmlEncoder` and `xmlDecoder` in `Converter` init.
2. Instantiate a `XMLEncoder` or `XMLDecoder` in call of methods above.

Example:
```swift
extension Converter {
public func setRequiredRequestBodyAsXML(
_ value: T,
headerFields: inout HTTPFields,
contentType: String
) throws -> HTTPBody {
try setRequiredRequestBody(
value,
headerFields: &headerFields,
contentType: contentType,
convert: convertBodyCodableToXML
)
}
}
```

### Alternatives considered

I found that `Converter` struct is not suited for adding support of new content type. With the proposed solution, the `Converter` struct will have types of `<#HTTPMediaType#>Encoder` and `<#HTTPMediaType#>Decoder`, while not necessarily needed.
For XML only API, `Converter` still need a `JSONEncoder` and `JSONDecoder`.

I thought that, maybe, and I would like to have your opinion on this, we must implement a `Converter` for a specific http media type, like [Vapor is doing](https://github.com/vapor/vapor/blob/11cdb29614a5c7f8c5289f3c97b3398c3d89b395/Sources/Vapor/Content/ContentConfiguration.swift#L63-L66) with `ContentEncoder` and `ContentDecoder`. Each http media type would be assigned to a specific Converter.

What's your thoughts about this?

### Additional information

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.