OpenAPITools / OpenAPITools/openapi-generator

[BUG] Discriminator object with mappings is not correct on Kotlin

Open
#15,120 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

When creating a discriminator object with mappings, the code looks OK in Swift and Typescript but not on Kotlin.

    WSEvent:
      description: The discriminator object for all websocket events, you should use
        this to map event payloads to their own type
      discriminator:
        mapping:
          call.accepted: '#/components/schemas/CallAcceptedEvent'
          call.blocked_user: '#/components/schemas/BlockedUserEvent'
          call.created: '#/components/schemas/CallCreatedEvent'
          call.ended: '#/components/schemas/CallEndedEvent'
          call.member_added: '#/components/schemas/CallMemberAddedEvent'
          call.member_removed: '#/components/schemas/CallMemberRemovedEvent'
          call.member_updated: '#/components/schemas/CallMemberUpdatedEvent'
          call.permission_request: '#/components/schemas/PermissionRequestEvent'
          call.permissions_updated: '#/components/schemas/UpdatedCallPermissionsEvent'
          call.reaction_new: '#/components/schemas/CallReactionEvent'
          call.recording_started: '#/components/schemas/CallRecordingStartedEvent'
          call.recording_stopped: '#/components/schemas/CallRecordingStoppedEvent'
          call.rejected: '#/components/schemas/CallRejectedEvent'
          call.unblocked_user: '#/components/schemas/UnblockedUserEvent'
          call.updated: '#/components/schemas/CallUpdatedEvent'
          call.updated_permission: '#/components/schemas/CallMemberUpdatedPermissionEvent'
          connection.ok: '#/components/schemas/WSConnectedEvent'
          custom: '#/components/schemas/CustomVideoEvent'
          health.check: '#/components/schemas/HealthCheckEvent'
        propertyName: type
      oneOf:
      - $ref: '#/components/schemas/BlockedUserEvent'
      - $ref: '#/components/schemas/CallAcceptedEvent'
      - $ref: '#/components/schemas/CallCreatedEvent'
      - $ref: '#/components/schemas/CallEndedEvent'
      - $ref: '#/components/schemas/CallMemberAddedEvent'
      - $ref: '#/components/schemas/CallMemberRemovedEvent'
      - $ref: '#/components/schemas/CallMemberUpdatedEvent'
      - $ref: '#/components/schemas/CallMemberUpdatedPermissionEvent'
      - $ref: '#/components/schemas/CallReactionEvent'
      - $ref: '#/components/schemas/CallRecordingStartedEvent'
      - $ref: '#/components/schemas/CallRecordingStoppedEvent'
      - $ref: '#/components/schemas/CallRejectedEvent'
      - $ref: '#/components/schemas/CallUpdatedEvent'
      - $ref: '#/components/schemas/CustomVideoEvent'
      - $ref: '#/components/schemas/HealthCheckEvent'
      - $ref: '#/components/schemas/PermissionRequestEvent'
      - $ref: '#/components/schemas/UnblockedUserEvent'
      - $ref: '#/components/schemas/UpdatedCallPermissionsEvent'
      - $ref: '#/components/schemas/WSConnectedEvent'

Generates this code for Kotlin

/**
 *
 * Please note:
 * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * Do not edit this file manually.
 *
 */

@file:Suppress(
    "ArrayInDataClass",
    "EnumEntryName",
    "RemoveRedundantQualifierName",
    "UnusedImport"
)

package org.openapitools.client.models

import org.openapitools.client.models.BlockedUserEvent
import org.openapitools.client.models.CallAcceptedEvent
import org.openapitools.client.models.CallCreatedEvent
import org.openapitools.client.models.CallEndedEvent
import org.openapitools.client.models.CallMemberAddedEvent
import org.openapitools.client.models.CallMemberRemovedEvent
import org.openapitools.client.models.CallMemberUpdatedEvent
import org.openapitools.client.models.CallMemberUpdatedPermissionEvent
import org.openapitools.client.models.CallReactionEvent
import org.openapitools.client.models.CallRecordingStartedEvent
import org.openapitools.client.models.CallRecordingStoppedEvent
import org.openapitools.client.models.CallRejectedEvent
import org.openapitools.client.models.CallResponse
import org.openapitools.client.models.CallUpdatedEvent
import org.openapitools.client.models.CustomVideoEvent
import org.openapitools.client.models.HealthCheckEvent
import org.openapitools.client.models.MemberResponse
import org.openapitools.client.models.OwnCapability
import org.openapitools.client.models.OwnUserResponse
import org.openapitools.client.models.PermissionRequestEvent
import org.openapitools.client.models.ReactionResponse
import org.openapitools.client.models.UnblockedUserEvent
import org.openapitools.client.models.UpdatedCallPermissionsEvent
import org.openapitools.client.models.UserResponse
import org.openapitools.client.models.WSConnectedEvent

import com.squareup.moshi.Json

/**
 * The discriminator object for all websocket events, you should use this to map event payloads to their own type
 *
 * @param callCid 
 * @param createdAt 
 * @param type The type of event: \"connection.ok\" in this case
 * @param user 
 * @param call 
 * @param members The list of members that were updated
 * @param ringing true when the call was created with ring enabled
 * @param capabilitiesByRole The capabilities by role for this call
 * @param reaction 
 * @param custom Custom data for this object
 * @param connectionId The connection_id for this client
 * @param permissions The list of permissions requested by the user
 * @param ownCapabilities The capabilities of the current user
 * @param me 
 * @param blockedByUser 
 */


interface WSEvent {

    @Json(name = "call_cid")
    val callCid: kotlin.String
    @Json(name = "created_at")
    val createdAt: java.time.OffsetDateTime
    /* The type of event: \"connection.ok\" in this case */
    @Json(name = "type")
    val type: kotlin.String
    @Json(name = "user")
    val user: UserResponse
    @Json(name = "call")
    val call: CallResponse
    /* The list of members that were updated */
    @Json(name = "members")
    val members: kotlin.collections.List<MemberResponse>
    /* true when the call was created with ring enabled */
    @Json(name = "ringing")
    val ringing: kotlin.Boolean
    /* The capabilities by role for this call */
    @Json(name = "capabilities_by_role")
    val capabilitiesByRole: kotlin.collections.Map<kotlin.String, kotlin.collections.List<kotlin.String>>
    @Json(name = "reaction")
    val reaction: ReactionResponse
    /* Custom data for this object */
    @Json(name = "custom")
    val custom: kotlin.collections.Map<kotlin.String, kotlin.Any>
    /* The connection_id for this client */
    @Json(name = "connection_id")
    val connectionId: kotlin.String
    /* The list of permissions requested by the user */
    @Json(name = "permissions")
    val permissions: kotlin.collections.List<kotlin.String>
    /* The capabilities of the current user */
    @Json(name = "own_capabilities")
    val ownCapabilities: kotlin.collections.List<OwnCapability>
    @Json(name = "me")
    val me: OwnUserResponse
    @Json(name = "blocked_by_user")
    val blockedByUser: UserResponse?
}

Here is the right code generated in Swift5

//
// WSEvent.swift
//
// Generated by openapi-generator
// https://openapi-generator.tech
//

import Foundation
#if canImport(AnyCodable)
import AnyCodable
#endif

/** The discriminator object for all websocket events, you should use this to map event payloads to their own type */
public enum WSEvent: Codable, JSONEncodable, Hashable {
    case typeBlockedUserEvent(BlockedUserEvent)
    case typeCallAcceptedEvent(CallAcceptedEvent)
    case typeCallCreatedEvent(CallCreatedEvent)
    case typeCallEndedEvent(CallEndedEvent)
    case typeCallMemberAddedEvent(CallMemberAddedEvent)
    case typeCallMemberRemovedEvent(CallMemberRemovedEvent)
    case typeCallMemberUpdatedEvent(CallMemberUpdatedEvent)
    case typeCallMemberUpdatedPermissionEvent(CallMemberUpdatedPermissionEvent)
    case typeCallReactionEvent(CallReactionEvent)
    case typeCallRecordingStartedEvent(CallRecordingStartedEvent)
    case typeCallRecordingStoppedEvent(CallRecordingStoppedEvent)
    case typeCallRejectedEvent(CallRejectedEvent)
    case typeCallUpdatedEvent(CallUpdatedEvent)
    case typeCustomVideoEvent(CustomVideoEvent)
    case typeHealthCheckEvent(HealthCheckEvent)
    case typePermissionRequestEvent(PermissionRequestEvent)
    case typeUnblockedUserEvent(UnblockedUserEvent)
    case typeUpdatedCallPermissionsEvent(UpdatedCallPermissionsEvent)
    case typeWSConnectedEvent(WSConnectedEvent)

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .typeBlockedUserEvent(let value):
            try container.encode(value)
        case .typeCallAcceptedEvent(let value):
            try container.encode(value)
        case .typeCallCreatedEvent(let value):
            try container.encode(value)
        case .typeCallEndedEvent(let value):
            try container.encode(value)
        case .typeCallMemberAddedEvent(let value):
            try container.encode(value)
        case .typeCallMemberRemovedEvent(let value):
            try container.encode(value)
        case .typeCallMemberUpdatedEvent(let value):
            try container.encode(value)
        case .typeCallMemberUpdatedPermissionEvent(let value):
            try container.encode(value)
        case .typeCallReactionEvent(let value):
            try container.encode(value)
        case .typeCallRecordingStartedEvent(let value):
            try container.encode(value)
        case .typeCallRecordingStoppedEvent(let value):
            try container.encode(value)
        case .typeCallRejectedEvent(let value):
            try container.encode(value)
        case .typeCallUpdatedEvent(let value):
            try container.encode(value)
        case .typeCustomVideoEvent(let value):
            try container.encode(value)
        case .typeHealthCheckEvent(let value):
            try container.encode(value)
        case .typePermissionRequestEvent(let value):
            try container.encode(value)
        case .typeUnblockedUserEvent(let value):
            try container.encode(value)
        case .typeUpdatedCallPermissionsEvent(let value):
            try container.encode(value)
        case .typeWSConnectedEvent(let value):
            try container.encode(value)
        }
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let value = try? container.decode(BlockedUserEvent.self) {
            self = .typeBlockedUserEvent(value)
        } else if let value = try? container.decode(CallAcceptedEvent.self) {
            self = .typeCallAcceptedEvent(value)
        } else if let value = try? container.decode(CallCreatedEvent.self) {
            self = .typeCallCreatedEvent(value)
        } else if let value = try? container.decode(CallEndedEvent.self) {
            self = .typeCallEndedEvent(value)
        } else if let value = try? container.decode(CallMemberAddedEvent.self) {
            self = .typeCallMemberAddedEvent(value)
        } else if let value = try? container.decode(CallMemberRemovedEvent.self) {
            self = .typeCallMemberRemovedEvent(value)
        } else if let value = try? container.decode(CallMemberUpdatedEvent.self) {
            self = .typeCallMemberUpdatedEvent(value)
        } else if let value = try? container.decode(CallMemberUpdatedPermissionEvent.self) {
            self = .typeCallMemberUpdatedPermissionEvent(value)
        } else if let value = try? container.decode(CallReactionEvent.self) {
            self = .typeCallReactionEvent(value)
        } else if let value = try? container.decode(CallRecordingStartedEvent.self) {
            self = .typeCallRecordingStartedEvent(value)
        } else if let value = try? container.decode(CallRecordingStoppedEvent.self) {
            self = .typeCallRecordingStoppedEvent(value)
        } else if let value = try? container.decode(CallRejectedEvent.self) {
            self = .typeCallRejectedEvent(value)
        } else if let value = try? container.decode(CallUpdatedEvent.self) {
            self = .typeCallUpdatedEvent(value)
        } else if let value = try? container.decode(CustomVideoEvent.self) {
            self = .typeCustomVideoEvent(value)
        } else if let value = try? container.decode(HealthCheckEvent.self) {
            self = .typeHealthCheckEvent(value)
        } else if let value = try? container.decode(PermissionRequestEvent.self) {
            self = .typePermissionRequestEvent(value)
        } else if let value = try? container.decode(UnblockedUserEvent.self) {
            self = .typeUnblockedUserEvent(value)
        } else if let value = try? container.decode(UpdatedCallPermissionsEvent.self) {
            self = .typeUpdatedCallPermissionsEvent(value)
        } else if let value = try? container.decode(WSConnectedEvent.self) {
            self = .typeWSConnectedEvent(value)
        } else {
            throw DecodingError.typeMismatch(Self.Type.self, .init(codingPath: decoder.codingPath, debugDescription: "Unable to decode instance of WSEvent"))
        }
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the supplied OpenAPI schema and inspect how the Kotlin generator handles discriminator mappings, comparing the generated WSEvent with the Swift output shown. Confirm that the Kotlin output represents the mapped event variants correctly, then validate the result with the generator's existing Kotlin tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.