Description
Question
Hey guys :)
Currently I'm not sure if I'm not understanding things correctly or if there's indeed a missing/buggy functionality - kinda new to both openapi and swift.
In general I do have an Endpoint that returns some nested data.
Class structure look like this (pseudo):
Action(details: ActionDetails)
ActionDetails(type: String)
ActionDetailsFoo(foo: String): ActionDetails
ActionDetailsBar(bar: int): ActionDetails
GET /nextAction
returns Action
{
"details": {
"type": "FOO",
"foo": "whatever"
}
}
See (manually adjusted) file with notes:
openapi: 3.0.1
info:
title: OpenAPI definition
version: v0
servers:
- url: http://localhost:8080
description: Generated server url
paths:
/nextAction:
get:
tags:
- open-api-controller
operationId: nextAction
responses:
default:
description: OK
content:
'application/json':
schema:
$ref: '#/components/schemas/Action'
components:
schemas:
Action:
type: object
properties:
details:
$ref: '#/components/schemas/ActionDetails'
ActionDetails:
required:
- type
type: object
properties:
type:
type: string
discriminator:
propertyName: type
mapping:
FOO: '#/components/schemas/ActionDetailsFoo'
BAR: '#/components/schemas/ActionDetailsBar'
anyOf: # manually added; removing this will always parse an object from ActionDetails without further info
- $ref: '#/components/schemas/ActionDetailsFoo'
- $ref: '#/components/schemas/ActionDetailsBar'
ActionDetailsBar:
type: object
allOf:
# - $ref: '#/components/schemas/ActionDetails' # keeping this ref to parent will end up in a loop
- type: object
properties:
bar:
type: integer
format: int32
ActionDetailsFoo:
type: object
allOf:
# - $ref: '#/components/schemas/ActionDetails'
- type: object
properties:
foo:
type: string
I've been playing around a lot with the openapi spec but no matter how I put it, I can't really get it running as expected
A) removing the anyOf on the ActionDetails level will ignore any subtype when parsing
note: the anyOf is currently manually added after generation
public struct ActionDetails: Codable, Hashable, Sendable {
public var _type: Swift.String
public init(_type: Swift.String) {
self._type = _type
}
public enum CodingKeys: String, CodingKey {
case _type = "type"
}
}
B) anyOf within the ActionDetails and allOf within the subclasses -> endless loop during runtime
ActionDetails.init() -> ActionDetailsFoo.init() -> ActionDetails.init() -> ActionDetailsFoo.init() -> ...
C) anyOf within the ActionDetails and removing allOf within subclasses:
kinda works, but I would have to manipulate the file manually and add all attributes that they should have from inheritance manually
From my understanding a) should be the correct way, as having a discriminator should take all schemas that have the parent schema with allOf included into consideration for alternate schemas.
Therefore I'm wondering if I'm doing it wrong or if this is indeed a bug / missing feature, can someone please enlighten me? :)
Also I'm generally new to swift and wonder if there's a more convenient way of getting the actual object then a switch on the cases? Maybe casting or anything? I usually have handlers and would like to just put the ActionDetails and they already know what kind of type they'd expect.
let result = client..
// is it possible to do something like
result as? ActionDetailsFoo
// instead of
switch(result) {
.case ActionDetailsFoo(let foo):
.case ActionDetailsBar(let bar):
}
I actually do have a similar issue when sending responses of subtypes. I wonder if there's any elegant way in which I could send any subclass of ActionDetails instead of ActionDetails that lacks all the extra information to an endpoint that expects an object of type ActionDetails.