mirror of
https://github.com/jellyfin/jellyfin-sdk-kotlin.git
synced 2025-02-17 06:37:29 +00:00
Implement DescriptionHook for more advanced description customization
This commit is contained in:
parent
cb793a9362
commit
65cff81b06
@ -72,6 +72,10 @@ Hooks are classes that will modify the output of the generator. They should be r
|
||||
instance of `CustomDefaultValue` that contains the code builder. The generator will use the
|
||||
default value from the JSON schema if all hooks return null.
|
||||
|
||||
- **DescriptionHook**
|
||||
A hook that allows customization of generated descriptions for operations, parameters, models and
|
||||
properties. The hook returns the new description or null if the description must be removed.
|
||||
|
||||
## Phases
|
||||
|
||||
The conversion happens in multiple phases. The phases in order are:
|
||||
|
@ -61,5 +61,5 @@ val mainModule = module {
|
||||
// Utilities
|
||||
single { DeprecatedAnnotationSpecBuilder() }
|
||||
single { TypeSerializerBuilder() }
|
||||
single { DescriptionBuilder() }
|
||||
single { DescriptionBuilder(getAll()) }
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ import org.jellyfin.openapi.constants.Classes
|
||||
import org.jellyfin.openapi.constants.Packages
|
||||
import org.jellyfin.openapi.constants.Strings
|
||||
import org.jellyfin.openapi.constants.Types
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
import org.jellyfin.openapi.model.ApiServiceOperation
|
||||
import org.jellyfin.openapi.model.ApiServiceOperationParameter
|
||||
import org.jellyfin.openapi.model.DefaultValue
|
||||
import org.jellyfin.openapi.model.IntRangeValidation
|
||||
import org.jellyfin.openapi.model.ParameterValidation
|
||||
|
||||
@ -30,7 +30,7 @@ open class OperationBuilder(
|
||||
addModifiers(KModifier.SUSPEND)
|
||||
|
||||
// Add description
|
||||
descriptionBuilder.build(data.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.OPERATION, data.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ open class OperationBuilder(
|
||||
defaultValue(data)
|
||||
|
||||
// Add description
|
||||
descriptionBuilder.build(data.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.OPERATION_PARAMETER, data.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
}.build()
|
||||
|
@ -10,12 +10,13 @@ import org.jellyfin.openapi.builder.extra.DeprecatedAnnotationSpecBuilder
|
||||
import org.jellyfin.openapi.builder.extra.DescriptionBuilder
|
||||
import org.jellyfin.openapi.constants.Packages
|
||||
import org.jellyfin.openapi.constants.Strings
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
import org.jellyfin.openapi.model.ApiServiceOperation
|
||||
import org.jellyfin.openapi.model.ApiServiceOperationParameter
|
||||
import org.jellyfin.openapi.model.DefaultValue
|
||||
|
||||
class OperationParameterModelBuilder(
|
||||
descriptionBuilder: DescriptionBuilder,
|
||||
private val descriptionBuilder: DescriptionBuilder,
|
||||
deprecatedAnnotationSpecBuilder: DeprecatedAnnotationSpecBuilder,
|
||||
) : OperationBuilder(descriptionBuilder, deprecatedAnnotationSpecBuilder) {
|
||||
private fun FunSpec.Builder.addOperationCall(
|
||||
@ -44,7 +45,9 @@ class OperationParameterModelBuilder(
|
||||
val parameters = data.pathParameters + data.queryParameters
|
||||
|
||||
ParameterSpec.builder(Strings.MODEL_REQUEST_PARAMETER_NAME, requestParameterType).apply {
|
||||
addKdoc("%L", Strings.MODEL_REQUEST_PARAMETER_DESCRIPTION)
|
||||
descriptionBuilder.build(DescriptionType.OPERATION_PARAMETER, Strings.MODEL_REQUEST_PARAMETER_DESCRIPTION)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
|
||||
// Add default value is all parameters have a default
|
||||
if (parameters.all { it.containsDefault() }) defaultValue(
|
||||
|
@ -6,6 +6,7 @@ import org.jellyfin.openapi.builder.extra.DeprecatedAnnotationSpecBuilder
|
||||
import org.jellyfin.openapi.builder.extra.DescriptionBuilder
|
||||
import org.jellyfin.openapi.constants.Strings
|
||||
import org.jellyfin.openapi.constants.Types
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
import org.jellyfin.openapi.model.ApiServiceOperation
|
||||
|
||||
class OperationUrlBuilder(
|
||||
@ -16,7 +17,7 @@ class OperationUrlBuilder(
|
||||
buildFunctionName(data.name)
|
||||
).apply {
|
||||
// Add description
|
||||
descriptionBuilder.build(data.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.OPERATION, data.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
|
||||
@ -37,7 +38,9 @@ class OperationUrlBuilder(
|
||||
|
||||
ParameterSpec.builder("includeCredentials", Types.BOOLEAN).apply {
|
||||
defaultValue("%L", data.requireAuthentication)
|
||||
addKdoc("%L", Strings.INCLUDE_CREDENTIALS_DESCRIPTION)
|
||||
descriptionBuilder.build(DescriptionType.OPERATION_PARAMETER, Strings.INCLUDE_CREDENTIALS_DESCRIPTION)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
}.build().let(::addParameter)
|
||||
|
||||
// Create parameter maps
|
||||
|
@ -1,26 +1,20 @@
|
||||
package org.jellyfin.openapi.builder.extra
|
||||
|
||||
import org.jellyfin.openapi.builder.Builder
|
||||
import org.jellyfin.openapi.hooks.DescriptionHook
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
|
||||
class DescriptionBuilder : Builder<String?, String?> {
|
||||
private val replacements = mapOf(
|
||||
// Replace CRLF with LF
|
||||
Regex("""\r\n?""") to "\n",
|
||||
// Replace <br /> elements with new line
|
||||
Regex("""<br\s?/?>""") to "\n",
|
||||
// Replace <seealso> elements with their value
|
||||
Regex("""<seealso\s+cref="(.*?)"\s?/>""") to "`$1`",
|
||||
Regex("""<seealso\s+cref="(.*?)"\s?>(.*?)</see>""") to "$2 (`$1`)",
|
||||
// Replace <see> elements with their value
|
||||
Regex("""<see\s+cref="(.*?)"\s?/>""") to "`$1`",
|
||||
Regex("""<see\s+cref="(.*?)"\s?>(.*?)</see>""") to "$2 (`$1`)",
|
||||
)
|
||||
class DescriptionBuilder(
|
||||
private val descriptionsHooks: Collection<DescriptionHook>,
|
||||
) {
|
||||
fun build(type: DescriptionType, description: String?): String? {
|
||||
if (description == null) return null
|
||||
|
||||
override fun build(data: String?): String? {
|
||||
if (data == null) return null
|
||||
|
||||
return replacements.entries.fold(data) { acc, (search, replace) ->
|
||||
acc.replace(search, replace)
|
||||
var modifiedDescription: String? = description
|
||||
for (hook in descriptionsHooks) {
|
||||
if (modifiedDescription == null) return null
|
||||
modifiedDescription = hook.modify(type, modifiedDescription)
|
||||
}
|
||||
|
||||
return modifiedDescription
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import org.jellyfin.openapi.builder.extra.DescriptionBuilder
|
||||
import org.jellyfin.openapi.constants.Packages
|
||||
import org.jellyfin.openapi.constants.Strings
|
||||
import org.jellyfin.openapi.constants.Types
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
import org.jellyfin.openapi.model.EmptyApiModel
|
||||
import org.jellyfin.openapi.model.JellyFile
|
||||
|
||||
@ -19,7 +20,7 @@ class EmptyModelBuilder(
|
||||
override fun build(data: EmptyApiModel): JellyFile {
|
||||
return TypeSpec.classBuilder(data.name.toPascalCase(from = CaseFormat.CAPITALIZED_CAMEL))
|
||||
.apply {
|
||||
descriptionBuilder.build(data.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.MODEL, data.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
if (data.deprecated) addAnnotation(deprecatedAnnotationSpecBuilder.build(Strings.DEPRECATED_CLASS))
|
||||
|
@ -14,6 +14,7 @@ import org.jellyfin.openapi.builder.extra.DescriptionBuilder
|
||||
import org.jellyfin.openapi.constants.Packages
|
||||
import org.jellyfin.openapi.constants.Strings
|
||||
import org.jellyfin.openapi.constants.Types
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
import org.jellyfin.openapi.model.EnumApiModel
|
||||
import org.jellyfin.openapi.model.JellyFile
|
||||
|
||||
@ -51,7 +52,7 @@ class EnumModelBuilder(
|
||||
}
|
||||
|
||||
// Header
|
||||
descriptionBuilder.build(data.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.MODEL, data.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
if (data.deprecated) addAnnotation(deprecatedAnnotationSpecBuilder.build(Strings.DEPRECATED_CLASS))
|
||||
|
@ -17,6 +17,7 @@ import org.jellyfin.openapi.builder.extra.defaultValue
|
||||
import org.jellyfin.openapi.constants.Packages
|
||||
import org.jellyfin.openapi.constants.Strings
|
||||
import org.jellyfin.openapi.constants.Types
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
import org.jellyfin.openapi.model.JellyFile
|
||||
import org.jellyfin.openapi.model.ObjectApiModel
|
||||
|
||||
@ -41,7 +42,7 @@ class ObjectModelBuilder(
|
||||
.builder(property.name, property.type)
|
||||
.initializer(property.name)
|
||||
.apply {
|
||||
descriptionBuilder.build(property.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.MODEL_PROPERTY, property.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
|
||||
@ -76,7 +77,7 @@ class ObjectModelBuilder(
|
||||
return TypeSpec.classBuilder(data.name.toPascalCase(from = CaseFormat.CAPITALIZED_CAMEL))
|
||||
.apply {
|
||||
modifiers += KModifier.DATA
|
||||
descriptionBuilder.build(data.description)?.let {
|
||||
descriptionBuilder.build(DescriptionType.MODEL, data.description)?.let {
|
||||
addKdoc("%L", it)
|
||||
}
|
||||
if (data.deprecated) addAnnotation(deprecatedAnnotationSpecBuilder.build(Strings.DEPRECATED_CLASS))
|
||||
|
@ -0,0 +1,12 @@
|
||||
package org.jellyfin.openapi.hooks
|
||||
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
|
||||
interface DescriptionHook {
|
||||
/**
|
||||
* Modify or drop a given description for API models and operations.
|
||||
*
|
||||
* @return The new description, null means removing the description.
|
||||
*/
|
||||
fun modify(type: DescriptionType, description: String): String?
|
||||
}
|
@ -5,7 +5,9 @@ import org.jellyfin.openapi.hooks.api.ClientLogOperationUrlHook
|
||||
import org.jellyfin.openapi.hooks.api.LargeOperationRequestModelHook
|
||||
import org.jellyfin.openapi.hooks.api.PlayStateServiceNameHook
|
||||
import org.jellyfin.openapi.hooks.model.DefaultUserIdHook
|
||||
import org.jellyfin.openapi.hooks.model.DotNetDescriptionHook
|
||||
import org.jellyfin.openapi.hooks.model.ImageMapsHook
|
||||
import org.jellyfin.openapi.hooks.model.SwashbuckleDescriptionHook
|
||||
import org.jellyfin.openapi.hooks.model.SyncPlayGroupUpdateHook
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
@ -22,4 +24,7 @@ val hooksModule = module {
|
||||
single { PlayStateServiceNameHook() } bind ServiceNameHook::class
|
||||
|
||||
single { DefaultUserIdHook() } bind DefaultValueHook::class
|
||||
|
||||
single { DotNetDescriptionHook() } bind DescriptionHook::class
|
||||
single { SwashbuckleDescriptionHook() } bind DescriptionHook::class
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package org.jellyfin.openapi.hooks.model
|
||||
|
||||
import org.jellyfin.openapi.hooks.DescriptionHook
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
|
||||
class DotNetDescriptionHook : DescriptionHook {
|
||||
private companion object {
|
||||
private val getsOrSetsRegex = Regex("""^(Gets or sets|Gets|Sets)\s+(.*)$""")
|
||||
}
|
||||
|
||||
override fun modify(type: DescriptionType, description: String): String {
|
||||
// Ignore API operations
|
||||
if (type == DescriptionType.OPERATION) return description
|
||||
|
||||
return description.replace(getsOrSetsRegex) { result ->
|
||||
result.groupValues[2].replaceFirstChar(Char::uppercase)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.jellyfin.openapi.hooks.model
|
||||
|
||||
import org.jellyfin.openapi.hooks.DescriptionHook
|
||||
import org.jellyfin.openapi.model.DescriptionType
|
||||
|
||||
class SwashbuckleDescriptionHook : DescriptionHook {
|
||||
private companion object {
|
||||
private val replacements = mapOf(
|
||||
// Replace CRLF with LF
|
||||
Regex("""\r\n?""") to "\n",
|
||||
// Replace <br /> elements with new line
|
||||
Regex("""<br\s?/?>""") to "\n",
|
||||
// Replace <seealso> elements with their value
|
||||
Regex("""<seealso\s+cref="(.*?)"\s?/>""") to "`$1`",
|
||||
Regex("""<seealso\s+cref="(.*?)"\s?>(.*?)</see>""") to "$2 (`$1`)",
|
||||
// Replace <see> elements with their value
|
||||
Regex("""<see\s+cref="(.*?)"\s?/>""") to "`$1`",
|
||||
Regex("""<see\s+cref="(.*?)"\s?>(.*?)</see>""") to "$2 (`$1`)",
|
||||
)
|
||||
}
|
||||
|
||||
override fun modify(type: DescriptionType, description: String): String = replacements.entries
|
||||
.fold(description) { acc, (search, replace) -> acc.replace(search, replace) }
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package org.jellyfin.openapi.model
|
||||
|
||||
enum class DescriptionType {
|
||||
OPERATION,
|
||||
OPERATION_PARAMETER,
|
||||
MODEL,
|
||||
MODEL_PROPERTY,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user