package {{ package_name }} import kotlinx.serialization.* import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.buildClassSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.decodeStructure import kotlinx.serialization.json.* import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.plus import java.math.BigDecimal import java.time.LocalDate import java.time.LocalDateTime import java.time.OffsetDateTime import java.time.format.DateTimeFormatterBuilder import java.io.File import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO import io.ktor.client.request.* import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.util.* @Serializable(NullSerializer::class) object NULL { } @Serializer(forClass=NULL::class) object NullSerializer : KSerializer { override val descriptor: SerialDescriptor get() = PrimitiveSerialDescriptor("NULL", PrimitiveKind.INT) override fun deserialize(decoder: Decoder): NULL { return NULL } @OptIn(ExperimentalSerializationApi::class) override fun serialize(encoder: Encoder, value: NULL) { (encoder as JsonEncoder).encodeJsonElement(JsonPrimitive(null)) } } object AnySerializer : JsonContentPolymorphicSerializer(Any::class) { override fun selectDeserializer(element: JsonElement) = throw NotImplementedError() } @Serializer(forClass=BigDecimal::class) object BigDecimalSerializer : KSerializer { override val descriptor: SerialDescriptor get() = buildClassSerialDescriptor("BigDecimal") { element("\$decimal", PrimitiveSerialDescriptor("BigDecimalElement", PrimitiveKind.STRING)) } override fun deserialize(decoder: Decoder): BigDecimal { return decoder.decodeStructure(descriptor) { val index = decodeElementIndex(descriptor) val string = decodeStringElement(descriptor, index) BigDecimal(string) } } override fun serialize(encoder: Encoder, value: BigDecimal) { encoder.encodeString(value.toString()) } } @Serializer(forClass=LocalDate::class) object DateSerializer : KSerializer { override val descriptor: SerialDescriptor get() = buildClassSerialDescriptor("LocalDate") { element("\$date", PrimitiveSerialDescriptor("LocalDateElement", PrimitiveKind.STRING)) } override fun deserialize(decoder: Decoder): LocalDate { return LocalDate.parse(decoder.decodeString()) } override fun serialize(encoder: Encoder, value: LocalDate) { encoder.encodeString(value.toString()) } } @Serializer(forClass=OffsetDateTime::class) object DateTimeSerializer : KSerializer { private val ISO_8601_FORMATTER = DateTimeFormatterBuilder() .parseCaseInsensitive() .appendInstant(3) .toFormatter(); override val descriptor: SerialDescriptor get() = buildClassSerialDescriptor("LocalDateTime") { element("\$date", PrimitiveSerialDescriptor("DateElement", PrimitiveKind.STRING)) } override fun deserialize(decoder: Decoder): OffsetDateTime { return decoder.decodeStructure(descriptor) { val index = decodeElementIndex(descriptor) val string = decodeStringElement(descriptor, index) OffsetDateTime.parse(string) } } override fun serialize(encoder: Encoder, value: OffsetDateTime) { encoder.encodeString(ISO_8601_FORMATTER.format(value)) } } @Serializable data class TeoException(override val message: String, val type: String, val errors: Map?) : Exception(message); fun replacePathArguments(path: String, args: Map): String { var result = path args.forEach { entry -> result = result.replace("{${entry.key}}", entry.value) } return result } suspend inline fun formRequest(methodString: String, path: String, input: Map, headers: Map? = null, query: String? = null): O { val client = HttpClient(CIO) val response: HttpResponse = client.request({{ conf.host.to_host_string() }} + path + if (query != null) { "?$query" } else { "" }) { method = HttpMethod.parse(methodString) setBody(MultiPartFormDataContent( formData { input.forEach { entry -> if (entry.value is String) { append(entry.key, entry.value as String) } else if (entry.value is File) { val file = entry.value as File append(entry.key, file.readBytes(), Headers.build { append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"") }) } else if (entry.value is List<*>) { val list = entry.value as List<*> list.forEachIndexed { index, item -> if (item is String) { append("${entry.key}[${index}]", item) } else if (item is File) { append("${entry.key}[${index}]", item.readBytes(), Headers.build { append(HttpHeaders.ContentDisposition, "filename=\"${item.name}\"") }) } } } } }, boundary = "TeoClientRequest" )) headers { append(HttpHeaders.ContentType, "application/json") headers?.forEach { entry -> append(entry.key, entry.value) } } } val bodyText = response.bodyAsText() return json_serializer.decodeFromString(bodyText) } suspend inline fun jsonRequest(methodString: String, path: String, input: I, headers: Map? = null, query: String? = null): O { val client = HttpClient(CIO) val response: HttpResponse = client.request({{ conf.host.to_host_string() }} + path + if (query != null) { "?$query" } else { "" }) { method = HttpMethod.parse(methodString) setBody(json_serializer.encodeToString(input)) headers { append(HttpHeaders.ContentType, "application/json") headers?.forEach { entry -> append(entry.key, entry.value) } } } val bodyText = response.bodyAsText() return json_serializer.decodeFromString(bodyText) } @OptIn(ExperimentalSerializationApi::class) val json_serializer = Json { explicitNulls = false serializersModule = serializersModule.plus(SerializersModule { contextual(BigDecimal::class, BigDecimalSerializer) polymorphic(Any::class, BigDecimal::class, BigDecimalSerializer) contextual(LocalDate::class, DateSerializer) polymorphic(Any::class, LocalDate::class, DateSerializer) contextual(OffsetDateTime::class, DateTimeSerializer) polymorphic(Any::class, OffsetDateTime::class, DateTimeSerializer) }) } {{ render_namespace(namespace, conf, namespace) }} val {{ conf.object_name }} = {{ conf.object_name|capitalize_first }}()