8000 Override property with subtype during inheritance · Issue #372 · cjbooms/fabrikt · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Override property with subtype during inheritance #372

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

Open
rajki opened this issue Feb 17, 2025 · 3 comments
Open

Override property with subtype during inheritance #372

rajki opened this issue Feb 17, 2025 · 3 comments

Comments

@rajki
Copy link
Contributor
rajki commented Feb 17, 2025

Hey folks 👋
Firstly great job with the playground, it's incredibly useful and a great time saver for iterations.

I wanted to get your opinion on the following usecase.

Assume that there are two type of Actions- DanceAction and WorkAction. There are equivalently two Locations- Home and Workplace. DanceAction can only be performed at Home, and WorkAction can only be performed at Work.

This can be represented in Kotlin as follows,

sealed class Action {
    abstract val location: Location
}

sealed class Location

data object Home : Location()

data object Work : Location()

data class DanceAction(
    override val location: Home,
) : Action()

data class WorkAction(
    override val location: Work,
) : Action()

Given an OpenAPI as follows,

openapi: 3.1.0
info:
  title: Action API
  version: 1.0.0

components:
  schemas:
    Action:
      type: object
      required:
        - location
      properties:
        location:
          $ref: '#/components/schemas/Location'
      discriminator:
        propertyName: location
        mapping:
          Home: '#/components/schemas/Home'
          Work: '#/components/schemas/Work'

    Location:
      type: object
      discriminator:
        propertyName: type
        mapping:
          Home: '#/components/schemas/Home'
          Work: '#/components/schemas/Work'
      properties:
        type:
          type: string
          enum: [Home, Work]

    Home:
      allOf:
        - $ref: '#/components/schemas/Location'

    Work:
      allOf:
        - $ref: '#/components/schemas/Location'

    DanceAction:
      allOf:
        - $ref: '#/components/schemas/Action'
        - type: object
          properties:
            location:
              $ref: '#/components/schemas/Home'

    WorkAction:
      allOf:
        - $ref: '#/components/schemas/Action'
        - type: object
          properties:
            location:
              $ref: '#/components/schemas/Work'

Fabrikt currently generates the following code (trimmed imports for brevity),

// Action
package com.example.models

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME,
  include = JsonTypeInfo.As.EXISTING_PROPERTY,
  property = "location",
  visible = true,
)
@JsonSubTypes()
public sealed class Action(
  public open val location: Location,
)

// Location
package com.example.models

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME,
  include = JsonTypeInfo.As.EXISTING_PROPERTY,
  property = "type",
  visible = true,
)
@JsonSubTypes(JsonSubTypes.Type(value = Home::class, name = "Home"),JsonSubTypes.Type(value =
    Work::class, name = "Work"))
public sealed class Location() {
  public abstract val type: LocationType
}

// LocationType
package com.example.models

public enum class LocationType(
  @JsonValue
  public val `value`: String,
) {
  HOME("Home"),
  WORK("Work"),
  ;

  public companion object {
    private val mapping: Map<String, LocationType> = entries.associateBy(LocationType::value)

    public fun fromValue(`value`: String): LocationType? = mapping[value]
  }
}

// Home
package com.example.models

public data class Home(
  @get:JsonProperty("type")
  @get:NotNull
  @param:JsonProperty("type")
  override val type: LocationType = LocationType.HOME,
) : Location()

// Work
package com.example.models

public data class Work(
  @get:JsonProperty("type")
  @get:NotNull
  @param:JsonProperty("type")
  override val type: LocationType = LocationType.WORK,
) : Location()

// DanceAction
package com.example.models

public data class DanceAction(
  @param:JsonProperty("location")
  @get:JsonProperty("location")
  @get:NotNull
  @get:Valid
  override val location: Location,
) : Action(location)

// WorkAction
package com.example.models

public data class WorkAction(
  @param:JsonProperty("location")
  @get:JsonProperty("location")
  @get:NotNull
  @get:Valid
  override val location: Location,
) : Action(location)

Please note the location field in WorkAction being of type Location instead of Work. Do you feel it would be idiomatic to support this?

@rajki
Copy link
Contributor Author
rajki commented Mar 11, 2025

Hi @cjbooms , when you have a few free minutes, could you please chime in on what you think about this?

@cjbooms
Copy link
Owner
cjbooms commented Mar 11, 2025

@averabaq @ulrikandersen either of you free to review? Will be a few days before im back from holidays, will review then if not

@ulrikandersen
Copy link
Collaborator

I would like @cjbooms to chime in a well, but I can provide my feedback:

I am not sure how, if at all, the standard deals with overriding properties like in this case, but it seems OK to let the specific schema (DanceAction/WorkAction) take precedence over the base schema. My concern would be that we are overlooking other cases where it is not the expected behaviour.

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

No branches or pull requests

3 participants
0