Skip to content

Commit

Permalink
Add DefaultValueHook for setting default operation parameter values (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsvanvelzen authored Jun 12, 2021
1 parent ba61720 commit f2f46ef
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 12 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ root = true
insert_final_newline = true
end_of_line = lf
max_line_length = 120
trim_trailing_whitespace = true

[{*.kts,*.kt}]
charset = utf-8
Expand All @@ -14,3 +15,6 @@ tab_width = 4
charset = utf-8
indent_style = tab
tab_width = 4

[*.md]
trim_trailing_whitespace = false
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public class MediaInfoApi(

/**
* For backwards compatibility parameters can be sent via Query or Body, with Query having higher
* precedence.
* precedence.
* Query parameters are obsolete.
*
* @param itemId The item id.
Expand All @@ -103,7 +103,7 @@ public class MediaInfoApi(

/**
* For backwards compatibility parameters can be sent via Query or Body, with Query having higher
* precedence.
* precedence.
* Query parameters are obsolete.
*
* @param itemId The item id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class PlaylistsApi(

/**
* For backwards compatibility parameters can be sent via Query or Body, with Query having higher
* precedence.
* precedence.
* Query parameters are obsolete.
*/
public suspend fun createPlaylist(`data`: CreatePlaylistDto? = null):
Expand All @@ -63,7 +63,7 @@ public class PlaylistsApi(

/**
* For backwards compatibility parameters can be sent via Query or Body, with Query having higher
* precedence.
* precedence.
* Query parameters are obsolete.
*
* @param name The playlist name.
Expand Down
9 changes: 7 additions & 2 deletions openapi-generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Changes to the http client can thus be made without updating the generator.

The generator uses a special hook system to change the generated code to better suite the Kotlin
language. For example, the "ImageBlurHashes" property in BaseItemDto can be simplified by using a
Map, and therefor a hook is added for it. All these hooks need to be maintained and updated when the
OpenAPI specification changes.
Map, and thus a hook is added for it. All hooks need to be maintained and updated when the OpenAPI
specification changes.

## Running

Expand Down Expand Up @@ -61,6 +61,11 @@ Hooks are classes that will modify the output of the generator. They should be r
used to fix spelling, moving operations to different services or adding operations to additional
services.

- **DefaultValueHook**
A hook that allows changing the default value of parameters in operations. The hook returns an
instance of `CustomDefaultValue` that contains the code builder. The generator will use the
default value from the JSON schema if all hooks return null.

## Phases

The conversion happens in multiple phases. The phases in order are:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ val mainModule = module {
single { OpenApiTypeBuilder(getAll()) }
single { OpenApiReturnTypeBuilder(get()) }
single { OpenApiModelBuilder(get(), get()) }
single { OpenApiApiServicesBuilder(get(), get(), get(), getAll()) }
single { OpenApiApiServicesBuilder(get(), get(), get(), getAll(), getAll()) }
single { OpenApiConstantsBuilder() }

// API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.jellyfin.openapi.constants.Packages
import org.jellyfin.openapi.constants.Strings
import org.jellyfin.openapi.model.ApiServiceOperation
import org.jellyfin.openapi.model.ApiServiceOperationParameter
import org.jellyfin.openapi.model.CustomDefaultValue

open class OperationBuilder(
private val deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder,
Expand Down Expand Up @@ -40,6 +41,7 @@ open class OperationBuilder(
is String -> defaultValue("%S", data.defaultValue)
is Int -> defaultValue("%L", data.defaultValue)
is Boolean -> defaultValue("%L", data.defaultValue)
is CustomDefaultValue -> defaultValue(data.defaultValue.build())
// Set value to null by default for nullable values
null -> when {
typeClassName == List::class.asClassName() -> defaultValue("%N()", "emptyList")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.jellyfin.openapi.builder.openapi

import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.asTypeName
import io.swagger.v3.oas.models.Operation
import io.swagger.v3.oas.models.PathItem
import io.swagger.v3.oas.models.Paths
import io.swagger.v3.oas.models.parameters.Parameter
import net.pearx.kasechange.CaseFormat
import net.pearx.kasechange.toCamelCase
import org.jellyfin.openapi.OpenApiGeneratorError
Expand All @@ -13,17 +15,16 @@ import org.jellyfin.openapi.constants.MimeType
import org.jellyfin.openapi.constants.Security
import org.jellyfin.openapi.constants.Strings
import org.jellyfin.openapi.hooks.ApiTypePath
import org.jellyfin.openapi.hooks.DefaultValueHook
import org.jellyfin.openapi.hooks.ServiceNameHook
import org.jellyfin.openapi.model.ApiService
import org.jellyfin.openapi.model.ApiServiceOperation
import org.jellyfin.openapi.model.ApiServiceOperationParameter
import org.jellyfin.openapi.model.HttpMethod
import org.jellyfin.openapi.model.*

class OpenApiApiServicesBuilder(
private val apiNameBuilder: ApiNameBuilder,
private val openApiTypeBuilder: OpenApiTypeBuilder,
private val openApiReturnTypeBuilder: OpenApiReturnTypeBuilder,
private val serviceNameHooks: Collection<ServiceNameHook>,
private val defaultValueHooks: Collection<DefaultValueHook>,
) : Builder<Paths, Collection<ApiService>> {
private fun getMethod(method: PathItem.HttpMethod) = when (method) {
PathItem.HttpMethod.POST -> HttpMethod.POST
Expand All @@ -42,6 +43,13 @@ class OpenApiApiServicesBuilder(
hook.mapServiceNames(operation, serviceNames)
}

/**
* Returns the default value of the first hook that does not return null or use the default from the schema
*/
private fun buildDefaultValue(path: ApiTypePath, type: TypeName, parameterSpec: Parameter) = defaultValueHooks
.firstNotNullOfOrNull { hook -> hook.onBuildDefaultValue(path, type, parameterSpec) }
?: parameterSpec.schema?.default

private fun buildOperation(
operation: Operation,
path: String,
Expand All @@ -59,6 +67,8 @@ class OpenApiApiServicesBuilder(
.build(ApiTypePath(serviceName, operationName, parameterName), parameterSpec.schema)
.copy(nullable = parameterSpec.required != true)

val parameterTypePath = ApiTypePath(serviceName, operationName, parameterName)

when (parameterSpec.`in`) {
"path" -> pathParameters
"query" -> queryParameters
Expand All @@ -67,7 +77,7 @@ class OpenApiApiServicesBuilder(
name = parameterName,
originalName = parameterSpec.name,
type = type,
defaultValue = parameterSpec.schema?.default,
defaultValue = buildDefaultValue(parameterTypePath, type, parameterSpec),
description = parameterSpec.description,
deprecated = parameterSpec.deprecated == true
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.jellyfin.openapi.hooks

import com.squareup.kotlinpoet.TypeName
import io.swagger.v3.oas.models.parameters.Parameter
import org.jellyfin.openapi.model.CustomDefaultValue

interface DefaultValueHook {
/**
* Create a [CustomDefaultValue] to use when generating the code for this parameter.
* Return `null` if the hook does not want to modify the default value.
*/
fun onBuildDefaultValue(path: ApiTypePath, type: TypeName, parameterSpec: Parameter): CustomDefaultValue?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.jellyfin.openapi.model

import com.squareup.kotlinpoet.CodeBlock

/**
* Custom value builder used in hooks.
* The implementation should use a [CodeBlock.Builder] to create a value.
*
* Creating a default value with the integer 1 can be done with:
*
* ```kotlin
* CodeBlock.Builder()
* .add("1")
* .build()
* ```
*/
interface CustomDefaultValue {
fun build(): CodeBlock
}

0 comments on commit f2f46ef

Please sign in to comment.