Skip to content

Commit

Permalink
Merge pull request #334 from nielsvanvelzen/openapi-request-body-types
Browse files Browse the repository at this point in the history
Fix generator ignoring non-json request body types
  • Loading branch information
Maxr1998 authored Nov 6, 2021
2 parents 32674ff + 5f5e718 commit cfe3a28
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 26 deletions.
12 changes: 6 additions & 6 deletions jellyfin-api/api/jellyfin-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -522,12 +522,12 @@ public final class org/jellyfin/sdk/api/operations/ImageApi : org/jellyfin/sdk/a
public static synthetic fun getUserImageByIndexUrl$default (Lorg/jellyfin/sdk/api/operations/ImageApi;Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;ILjava/lang/String;Lorg/jellyfin/sdk/model/api/ImageFormat;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Ljava/lang/String;
public final fun getUserImageUrl (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Ljava/lang/String;Lorg/jellyfin/sdk/model/api/ImageFormat;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Z)Ljava/lang/String;
public static synthetic fun getUserImageUrl$default (Lorg/jellyfin/sdk/api/operations/ImageApi;Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Ljava/lang/String;Lorg/jellyfin/sdk/model/api/ImageFormat;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ZILjava/lang/Object;)Ljava/lang/String;
public final fun postUserImage (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Ljava/lang/Integer;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun postUserImage$default (Lorg/jellyfin/sdk/api/operations/ImageApi;Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Ljava/lang/Integer;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun postUserImageByIndex (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun postUserImageByIndex$default (Lorg/jellyfin/sdk/api/operations/ImageApi;Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;ILkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun setItemImage (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun setItemImageByIndex (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun postUserImage (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Ljava/lang/Integer;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun postUserImage$default (Lorg/jellyfin/sdk/api/operations/ImageApi;Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;Ljava/lang/Integer;[BLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun postUserImageByIndex (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;I[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun postUserImageByIndex$default (Lorg/jellyfin/sdk/api/operations/ImageApi;Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;I[BLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun setItemImage (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun setItemImageByIndex (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;I[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun updateItemImageIndex (Ljava/util/UUID;Lorg/jellyfin/sdk/model/api/ImageType;IILkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package org.jellyfin.sdk.api.operations
import io.ktor.utils.io.ByteReadChannel
import kotlin.Any
import kotlin.Boolean
import kotlin.ByteArray
import kotlin.Double
import kotlin.Int
import kotlin.String
Expand Down Expand Up @@ -2219,14 +2220,14 @@ public class ImageApi(
public suspend fun postUserImage(
userId: UUID = api.userId ?: throw MissingUserIdException(),
imageType: ImageType,
index: Int? = null
index: Int? = null,
`data`: ByteArray
): Response<Unit> {
val pathParameters = mutableMapOf<String, Any?>()
pathParameters["userId"] = userId
pathParameters["imageType"] = imageType
val queryParameters = mutableMapOf<String, Any?>()
queryParameters["index"] = index
val data = null
val response = api.post<Unit>("/Users/{userId}/Images/{imageType}", pathParameters,
queryParameters, data)
return response
Expand All @@ -2242,14 +2243,14 @@ public class ImageApi(
public suspend fun postUserImageByIndex(
userId: UUID = api.userId ?: throw MissingUserIdException(),
imageType: ImageType,
index: Int
index: Int,
`data`: ByteArray
): Response<Unit> {
val pathParameters = mutableMapOf<String, Any?>()
pathParameters["userId"] = userId
pathParameters["imageType"] = imageType
pathParameters["index"] = index
val queryParameters = emptyMap<String, Any?>()
val data = null
val response = api.post<Unit>("/Users/{userId}/Images/{imageType}/{index}", pathParameters,
queryParameters, data)
return response
Expand All @@ -2261,12 +2262,15 @@ public class ImageApi(
* @param itemId Item id.
* @param imageType Image type.
*/
public suspend fun setItemImage(itemId: UUID, imageType: ImageType): Response<Unit> {
public suspend fun setItemImage(
itemId: UUID,
imageType: ImageType,
`data`: ByteArray
): Response<Unit> {
val pathParameters = mutableMapOf<String, Any?>()
pathParameters["itemId"] = itemId
pathParameters["imageType"] = imageType
val queryParameters = emptyMap<String, Any?>()
val data = null
val response = api.post<Unit>("/Items/{itemId}/Images/{imageType}", pathParameters,
queryParameters, data)
return response
Expand All @@ -2282,14 +2286,14 @@ public class ImageApi(
public suspend fun setItemImageByIndex(
itemId: UUID,
imageType: ImageType,
imageIndex: Int
imageIndex: Int,
`data`: ByteArray
): Response<Unit> {
val pathParameters = mutableMapOf<String, Any?>()
pathParameters["itemId"] = itemId
pathParameters["imageType"] = imageType
pathParameters["imageIndex"] = imageIndex
val queryParameters = emptyMap<String, Any?>()
val data = null
val response = api.post<Unit>("/Items/{itemId}/Images/{imageType}/{imageIndex}", pathParameters,
queryParameters, data)
return response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.swagger.v3.oas.models.Paths
import io.swagger.v3.oas.models.media.IntegerSchema
import io.swagger.v3.oas.models.media.Schema
import io.swagger.v3.oas.models.parameters.Parameter
import io.swagger.v3.oas.models.parameters.RequestBody
import mu.KotlinLogging
import net.pearx.kasechange.CaseFormat
import net.pearx.kasechange.toCamelCase
Expand Down Expand Up @@ -63,6 +64,41 @@ class OpenApiApiServicesBuilder(
else -> null
}

private fun buildRequestBody(requestBody: RequestBody, serviceName: String, operationName: String): TypeName? {
// Filter out duplicate JSON values
val requestBodyTypes = requestBody.content.keys.filterNot {
MimeType.IGNORED_JSON_TYPES.contains(it)
}

// Check amount of types and get the first
val requestBodyType = when (requestBodyTypes.size) {
0 -> return null
1 -> requestBodyTypes.first()
else -> throw OpenApiGeneratorError("Multiple request body types are not supported. Found types: $requestBodyTypes")
}

// Retrieve info
val content = requestBody.content[requestBodyType]
val required = requestBody.required ?: false

// Determine the correct type
return when (requestBodyType) {
// JSON body - use type builder
MimeType.APPLICATION_JSON -> content?.schema?.let { schema ->
openApiTypeBuilder.build(
ApiTypePath(serviceName, operationName, ApiTypePath.PARAMETER_BODY),
schema
).copy(nullable = !required)
}
// Image body
MimeType.IMAGE_ALL -> Types.BYTE_ARRAY
// String body
MimeType.TEXT_PLAIN -> Types.STRING
// Unknown type
else -> throw OpenApiGeneratorError("""Unsupported request body type "$requestBodyType".""")
}
}

private fun buildOperation(
operation: Operation,
path: String,
Expand Down Expand Up @@ -118,6 +154,10 @@ class OpenApiApiServicesBuilder(
?.any(Security.AUTHENTICATION_POLICIES::contains)
?: false

val bodyType = operation.requestBody?.let { requestBody ->
buildRequestBody(requestBody, serviceName, operationName)
}

return ApiServiceOperation(
name = operationName,
description = operation.description ?: operation.summary,
Expand All @@ -128,9 +168,7 @@ class OpenApiApiServicesBuilder(
returnType = returnType,
pathParameters = pathParameters,
queryParameters = queryParameters,
bodyType = operation.requestBody?.content?.get(MimeType.APPLICATION_JSON)?.schema?.let { schema ->
openApiTypeBuilder.build(ApiTypePath(serviceName, operationName, ApiTypePath.PARAMETER_BODY), schema)
}
bodyType = bodyType,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package org.jellyfin.openapi.constants

object MimeType {
const val APPLICATION_ALL_PLUS_JSON = "application/*+json"
const val APPLICATION_JSON = "application/json"
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
const val APPLICATION_X_JAVASCRIPT = "application/x-javascript"
const val APPLICATION_X_MPEG_URL = "application/x-mpegURL"
const val AUDIO_ALL = "audio/*"
const val FONT_ALL = "font/*"
const val IMAGE_ALL = "image/*"
const val TEXT_ALL = "text/*"
const val TEXT_PLAIN = "text/plain"
const val TEXT_CSS = "text/css"
const val TEXT_XML = "text/xml"
const val TEXT_HTML = "text/html"
const val IMAGE_ALL = "image/*"
const val AUDIO_ALL = "audio/*"
const val TEXT_JSON = "text/json"
const val TEXT_PLAIN = "text/plain"
const val TEXT_XML = "text/xml"
const val VIDEO_ALL = "video/*"
const val FONT_ALL = "font/*"
const val APPLICATION_X_JAVASCRIPT = "application/x-javascript"
const val APPLICATION_X_MPEG_URL = "application/x-mpegURL"
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
const val APPLICATION_JSON = "application/json"

/**
* Array of duplicated JSON mime types in the OpenAPI specification generated by the Jellyfin server.
*/
val IGNORED_JSON_TYPES = arrayOf(TEXT_JSON, APPLICATION_ALL_PLUS_JSON)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.squareup.kotlinpoet.asTypeName
object Types {
// Custom
val BINARY = ClassName("io.ktor.utils.io", "ByteReadChannel")
val BYTE_ARRAY = ByteArray::class.asTypeName()
val UUID = ClassName(Packages.MODEL_TYPES, Classes.Types.UUID)
val DATETIME = ClassName(Packages.MODEL_TYPES, Classes.Types.DATETIME)
val JSON_ELEMENT = ClassName("kotlinx.serialization.json", "JsonElement")
Expand Down

0 comments on commit cfe3a28

Please sign in to comment.