Skip to content

Commit

Permalink
fix default values
Browse files Browse the repository at this point in the history
this currently requires the manual implementation of RecordProperty's serializer. This can be simplified once Kotlin/kotlinx.serialization#2455 is resolved.
  • Loading branch information
pschichtel committed Sep 30, 2023
1 parent 04ef61a commit 5d3d12d
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 47 deletions.
72 changes: 67 additions & 5 deletions core/src/main/kotlin/tel/schich/idl/core/Model.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
package tel.schich.idl.core

import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.json.JsonClassDiscriminator
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import sun.awt.im.SimpleInputMethodWindow
import tel.schich.idl.core.constraint.CollectionSizeRange
import tel.schich.idl.core.constraint.FloatValueRange
import tel.schich.idl.core.constraint.IntegerValueRange
import tel.schich.idl.core.constraint.StringLengthRange
import java.util.UUID

@Serializable
@OptIn(ExperimentalSerializationApi::class)
Expand Down Expand Up @@ -104,17 +116,67 @@ data class Alias(
val aliasedModel: ModelReference,
) : Definition

@Serializable
data class DefaultValue(val value: JsonElement)

@Serializable
@Serializable(RecordPropertySerializer::class)
data class RecordProperty(
val metadata: BasicMetadata,
val model: ModelReference,
val nullable: Boolean = false,
val default: DefaultValue? = null,
val default: JsonElement? = null,
)

private object RecordPropertySerializer : KSerializer<RecordProperty> {
override val descriptor = buildClassSerialDescriptor(RecordProperty::class.qualifiedName!!) {
element("metadata", BasicMetadata.serializer().descriptor)
element("model", ModelReference.serializer().descriptor)
element("nullable", Boolean.serializer().descriptor, isOptional = true)
element("default", JsonElement.serializer().descriptor, isOptional = true)
}

override fun serialize(encoder: Encoder, value: RecordProperty) {
encoder.beginStructure(descriptor).apply {
encodeSerializableElement(descriptor, index = 0, BasicMetadata.serializer(), value.metadata)
encodeSerializableElement(descriptor, index = 1, ModelReference.serializer(), value.model)
if (value.nullable) {
encodeBooleanElement(descriptor, index = 2, value = true)
}
value.default?.let {
encodeSerializableElement(descriptor, index = 3, JsonElement.serializer(), it)
}
}.endStructure(descriptor)
}

override fun deserialize(decoder: Decoder) = decoder.decodeStructure(descriptor) {
var metadata: BasicMetadata? = null
var model: ModelReference? = null
var nullable = false
var default: JsonElement? = null

while (true) {
when (val index = decodeElementIndex(descriptor)) {
CompositeDecoder.DECODE_DONE ->
break
CompositeDecoder.UNKNOWN_NAME ->
throw SerializationException("unknown field!")
0 ->
metadata = decodeSerializableElement(descriptor, index, BasicMetadata.serializer())
1 ->
model = decodeSerializableElement(descriptor, index, ModelReference.serializer())
2 ->
nullable = decodeBooleanElement(descriptor, index)
3 ->
default = decodeSerializableElement(descriptor, index, JsonElement.serializer())
}
}
if (metadata == null) {
throw SerializationException("metadata is required in RecordProperty!")
}
if (model == null) {
throw SerializationException("model is required in RecordProperty!")
}
RecordProperty(metadata, model, nullable, default)
}
}

@Serializable
data class AdtConstructor(
val metadata: BasicMetadata,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object NullDefaultInNonNullableRecordPropertyValidator : ModuleValidator {
.flatMap { record ->
record.properties
.filterNot { it.nullable }
.filter { it.default != null && it.default.value is JsonNull }
.filter { it.default != null && it.default is JsonNull }
.map { property ->
NullDefaultInNonNullableRecordPropertyError(
module.reference,
Expand Down
3 changes: 1 addition & 2 deletions example/cvg/specs/assist.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ definitions:
- metadata:
name: authToken
nullable: true
default:
value: null
default: null
model:
name: AssistAuthToken
- metadata:
Expand Down
30 changes: 10 additions & 20 deletions example/cvg/specs/provisioning.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ definitions:
- metadata:
name: botURL
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.project
Expand All @@ -58,8 +57,7 @@ definitions:
- metadata:
name: botAccessToken
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.project
Expand All @@ -68,8 +66,7 @@ definitions:
- metadata:
name: botConfiguration
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.project
Expand All @@ -78,15 +75,13 @@ definitions:
- metadata:
name: language
nullable: true
default:
value: null
default: null
model:
name: ProvisionCallRequestLanguage
- metadata:
name: transcriberVendors
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.speech-service
Expand All @@ -95,24 +90,21 @@ definitions:
- metadata:
name: synthesizerVendors
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.speech-service
version: '1'
name: SynthesizerSelection
- metadata:
name: writeDialogData
default:
value: true
default: true
model:
name: EnabledDialogData
- metadata:
name: inactivityTimeout
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.project
Expand All @@ -121,8 +113,7 @@ definitions:
- metadata:
name: minimumNoiseLevel
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.project
Expand All @@ -131,8 +122,7 @@ definitions:
- metadata:
name: utteranceTimeout
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.project
Expand Down
6 changes: 2 additions & 4 deletions example/cvg/specs/shared/barge-in.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ definitions:
- metadata:
name: confidence
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.confidence
Expand All @@ -58,8 +57,7 @@ definitions:
- metadata:
name: phraseList
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.speech-service
Expand Down
15 changes: 5 additions & 10 deletions example/cvg/specs/shared/recording.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ definitions:
When a recording is terminated, it will be processed as soon as possible and can't be resumed.
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.time
Expand All @@ -56,15 +55,13 @@ definitions:
- metadata:
name: recordingId
nullable: true
default:
value: null
default: null
model:
name: RecordingId
- metadata:
name: speakers
nullable: true
default:
value: null
default: null
model:
module:
name: ai.vier.cvg.shared.speaker
Expand All @@ -84,14 +81,12 @@ definitions:
- metadata:
name: recordingId
nullable: true
default:
value: null
default: null
model:
name: RecordingId
- metadata:
name: terminate
default:
value: false
default: false
model:
name: TerminateRecordingOnStop
- metadata:
Expand Down
6 changes: 6 additions & 0 deletions example/simple/specs/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ definitions:
description: override
model:
name: product
default: null
nullable: true
- metadata:
name: prop2
description: override
model:
name: product
- type: constant
metadata:
name: constant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fun KotlinGeneratorContext<Model.Record>.generateRecord() {
val default = property.default
if (default != null) {
append(" = ")
append(constructInstance(subjectModule, referencedModule, referencedDefinition, default.value))
append(constructInstance(subjectModule, referencedModule, referencedDefinition, default))
}
append(",")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class OpenApiGenerator : JvmInProcessGenerator {
modules,
commonNamePrefix,
asNullable = property.nullable,
withDefault = property.default?.value,
withDefault = property.default,
property.metadata,
)
} else {
Expand Down
5 changes: 2 additions & 3 deletions runner/src/main/kotlin/tel/schich/idl/runner/Input.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:OptIn(ExperimentalSerializationApi::class)

package tel.schich.idl.runner

import com.fasterxml.jackson.databind.JsonNode
Expand Down Expand Up @@ -53,13 +55,11 @@ private inline fun <T> handleJsonException(deserializationStrategy: Deserializat
} catch (e: SerializationException) {
throw LoaderException("Failed to parse JSON!", e)
} catch (e: IllegalArgumentException) {
@OptIn(ExperimentalSerializationApi::class)
throw LoaderException("Failed to map JSON to ${deserializationStrategy.descriptor.serialName}!", e)
}
}

object JsonLoader : Loader {
@OptIn(ExperimentalSerializationApi::class)
override fun <T> load(data: ByteArray, deserializationStrategy: DeserializationStrategy<T>): T {
return handleJsonException(deserializationStrategy) {
json.decodeFromStream(it, ByteArrayInputStream(data))
Expand All @@ -68,7 +68,6 @@ object JsonLoader : Loader {
}


@OptIn(ExperimentalSerializationApi::class)
private fun convertJacksonTreeToJsonElement(jackson: JsonNode): JsonElement = when (jackson) {
is ObjectNode -> {
val obj = buildMap(jackson.size()) {
Expand Down

0 comments on commit 5d3d12d

Please sign in to comment.