mirror of
https://github.com/DreamExposure/DisCal-Discord-Bot.git
synced 2026-04-27 04:40:31 -05:00
Status tracking rewrite + code cleanup (#116)
This commit is contained in:
-195
@@ -1,195 +0,0 @@
|
||||
package org.dreamexposure.discal.core.object.network.discal;
|
||||
|
||||
import org.dreamexposure.discal.Application;
|
||||
import org.dreamexposure.discal.GitProperty;
|
||||
import org.dreamexposure.discal.core.database.DatabaseManager;
|
||||
import org.dreamexposure.discal.core.utils.JsonUtil;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static org.dreamexposure.discal.core.utils.GlobalVal.getSTATUS;
|
||||
|
||||
/**
|
||||
* @author NovaFox161
|
||||
* Date Created: 9/8/2018
|
||||
* For Project: DisCal-Discord-Bot
|
||||
* Author Website: https://www.novamaday.com
|
||||
* Company Website: https://www.dreamexposure.org
|
||||
* Contact: nova@dreamexposure.org
|
||||
*/
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Component
|
||||
public class NetworkInfo {
|
||||
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final List<ConnectedClient> clients = new CopyOnWriteArrayList<>();
|
||||
|
||||
private int calCount;
|
||||
private int announcementCount;
|
||||
private int guildCount;
|
||||
|
||||
private String uptime;
|
||||
private UUID instanceId;
|
||||
|
||||
|
||||
public Mono<Void> update() {
|
||||
return DatabaseManager.INSTANCE.getCalendarCount()
|
||||
.doOnNext(i -> this.calCount = i)
|
||||
.then(DatabaseManager.INSTANCE.getAnnouncementCount())
|
||||
.doOnNext(i -> this.announcementCount = i)
|
||||
.then();
|
||||
}
|
||||
|
||||
//Getters
|
||||
public List<ConnectedClient> getClients() {
|
||||
return new CopyOnWriteArrayList<>(this.clients);
|
||||
}
|
||||
|
||||
public boolean doesClientExist(final int clientIndex) {
|
||||
for (final ConnectedClient cc : this.clients) {
|
||||
if (cc.getClientIndex() == clientIndex)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ConnectedClient getClient(final int clientIndex) {
|
||||
for (final ConnectedClient cc : this.clients) {
|
||||
if (cc.getClientIndex() == clientIndex)
|
||||
return cc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addClient(final ConnectedClient client) {
|
||||
this.clients.add(client);
|
||||
this.clients.sort(Comparator.comparingInt(ConnectedClient::getClientIndex));
|
||||
|
||||
LOGGER.info(getSTATUS(), "Client connected to network | Index: " + client.getClientIndex());
|
||||
}
|
||||
|
||||
public void updateClient(ConnectedClient client) {
|
||||
if (this.doesClientExist(client.getClientIndex()))
|
||||
this.clients.remove(this.getClient(client.getClientIndex()));
|
||||
this.clients.add(client);
|
||||
this.clients.sort(Comparator.comparingInt(ConnectedClient::getClientIndex));
|
||||
}
|
||||
|
||||
public void removeClient(final int clientIndex) {
|
||||
if (this.doesClientExist(clientIndex)) {
|
||||
this.clients.remove(this.getClient(clientIndex));
|
||||
|
||||
LOGGER.warn(getSTATUS(), "Client disconnected from network | Index: " + clientIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeClient(final int clientIndex, final String reason) {
|
||||
if (this.doesClientExist(clientIndex)) {
|
||||
this.clients.remove(this.getClient(clientIndex));
|
||||
|
||||
LOGGER.warn(getSTATUS(), "Client disconnected from network | Index: " + clientIndex + " | Reason: " + reason);
|
||||
}
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return GitProperty.DISCAL_VERSION.getValue();
|
||||
}
|
||||
|
||||
public String getD4JVersion() {
|
||||
return GitProperty.DISCAL_VERSION_D4J.getValue();
|
||||
}
|
||||
|
||||
public int getTotalGuildCount() {
|
||||
int count = 0;
|
||||
for (final ConnectedClient cc : this.clients) {
|
||||
count += cc.getConnectedServers();
|
||||
}
|
||||
|
||||
this.guildCount = count;
|
||||
return this.guildCount;
|
||||
}
|
||||
|
||||
public int getClientCount() {
|
||||
return this.clients.size();
|
||||
}
|
||||
|
||||
public int getExpectedClientCount() {
|
||||
if (!clients.isEmpty())
|
||||
return clients.get(0).getExpectedClientCount();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getCalendarCount() {
|
||||
return this.calCount;
|
||||
}
|
||||
|
||||
public int getAnnouncementCount() {
|
||||
return this.announcementCount;
|
||||
}
|
||||
|
||||
public String getUptime() {
|
||||
return this.uptime;
|
||||
}
|
||||
|
||||
public UUID getInstanceId() {
|
||||
return this.instanceId;
|
||||
}
|
||||
|
||||
//Setters
|
||||
public void setCalCount(final int calCount) {
|
||||
this.calCount = calCount;
|
||||
}
|
||||
|
||||
public void setAnnouncementCount(final int announcementCount) {
|
||||
this.announcementCount = announcementCount;
|
||||
}
|
||||
|
||||
public void setInstanceId(final UUID instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public JSONObject toJson() {
|
||||
final JSONObject json = new JSONObject();
|
||||
|
||||
json.put("api_version", this.getVersion());
|
||||
json.put("d4j_version", this.getD4JVersion());
|
||||
json.put("api_uptime", Application.getHumanReadableUptime());
|
||||
json.put("api_instance_id", this.instanceId);
|
||||
json.put("announcements", this.getAnnouncementCount());
|
||||
json.put("total_guilds", this.getTotalGuildCount());
|
||||
json.put("calendars", this.getCalendarCount());
|
||||
|
||||
final JSONArray jClients = new JSONArray();
|
||||
for (final ConnectedClient c : this.clients)
|
||||
jClients.put(JsonUtil.INSTANCE.encodeToJSON(ConnectedClient.class, c));
|
||||
|
||||
json.put("clients", jClients);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public NetworkInfo fromJson(final JSONObject json) {
|
||||
this.uptime = json.getString("api_uptime");
|
||||
this.instanceId = UUID.fromString(json.getString("api_instance_id"));
|
||||
this.announcementCount = json.getInt("announcements");
|
||||
this.guildCount = json.getInt("total_guilds");
|
||||
this.calCount = json.getInt("calendars");
|
||||
|
||||
final JSONArray jClients = json.getJSONArray("clients");
|
||||
|
||||
for (int i = 0; i < jClients.length(); i++)
|
||||
this.clients.add(JsonUtil.INSTANCE.decodeFromJSON(ConnectedClient.class, jClients.getJSONObject(i)));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
package org.dreamexposure.discal.core.object.network.discal;
|
||||
@@ -1 +0,0 @@
|
||||
package org.dreamexposure.discal.core.object.network;
|
||||
@@ -1 +0,0 @@
|
||||
package org.dreamexposure.discal.core.object;
|
||||
@@ -7,13 +7,13 @@ import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@SpringBootApplication(exclude = [SessionAutoConfiguration::class, R2dbcAutoConfiguration::class])
|
||||
class Application {
|
||||
companion object {
|
||||
val instanceId: UUID = UUID.randomUUID()
|
||||
|
||||
@JvmStatic
|
||||
fun getShardIndex(): String {
|
||||
/*
|
||||
This fucking sucks. So k8s doesn't expose the pod ordinal for a pod in a stateful set
|
||||
@@ -38,22 +38,26 @@ class Application {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getShardCount(): Int {
|
||||
val shardCount = System.getenv("SHARD_COUNT")
|
||||
return shardCount?.toInt() ?: //Fall back to config
|
||||
BotSettings.SHARD_COUNT.get().toInt()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getHumanReadableUptime(): String {
|
||||
fun getUptime(): Duration {
|
||||
val mxBean = ManagementFactory.getRuntimeMXBean()
|
||||
|
||||
val rawDuration = System.currentTimeMillis() - mxBean.startTime
|
||||
val duration = Duration.ofMillis(rawDuration)
|
||||
return Duration.ofMillis(rawDuration)
|
||||
}
|
||||
|
||||
return "%d days, %d hours, %d minutes, %d seconds%n"
|
||||
.format(duration.toDays(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart())
|
||||
fun getMemoryUsedInMb(): Double {
|
||||
val totalMemory = Runtime.getRuntime().totalMemory()
|
||||
val freeMemory = Runtime.getRuntime().freeMemory()
|
||||
|
||||
val raw = (totalMemory - freeMemory) / (1024 * 1024).toDouble()
|
||||
|
||||
return (raw * 100).roundToInt().toDouble() / 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.dreamexposure.discal.core.enums.time.TimeFormat
|
||||
import org.dreamexposure.discal.core.extensions.asStringList
|
||||
import org.dreamexposure.discal.core.logger.LOGGER
|
||||
import org.dreamexposure.discal.core.utils.GlobalVal.DEFAULT
|
||||
import org.dreamexposure.novautils.database.DatabaseSettings
|
||||
import org.intellij.lang.annotations.Language
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.util.retry.Retry
|
||||
@@ -37,15 +36,6 @@ import java.util.*
|
||||
import java.util.function.Function
|
||||
|
||||
object DatabaseManager {
|
||||
private val settings: DatabaseSettings = DatabaseSettings(
|
||||
BotSettings.SQL_HOST.get(),
|
||||
BotSettings.SQL_PORT.get(),
|
||||
BotSettings.SQL_DB.get(),
|
||||
BotSettings.SQL_USER.get(),
|
||||
BotSettings.SQL_PASS.get(),
|
||||
BotSettings.SQL_PREFIX.get()
|
||||
)
|
||||
|
||||
private val pool: ConnectionPool
|
||||
|
||||
init {
|
||||
@@ -53,11 +43,11 @@ object DatabaseManager {
|
||||
builder()
|
||||
.option(DRIVER, "pool")
|
||||
.option(PROTOCOL, "mysql")
|
||||
.option(HOST, settings.hostname)
|
||||
.option(PORT, settings.port.toInt())
|
||||
.option(USER, settings.user)
|
||||
.option(PASSWORD, settings.password)
|
||||
.option(DATABASE, settings.database)
|
||||
.option(HOST, BotSettings.SQL_HOST.get())
|
||||
.option(PORT, BotSettings.SQL_PORT.get().toInt())
|
||||
.option(USER, BotSettings.SQL_USER.get())
|
||||
.option(PASSWORD, BotSettings.SQL_PASS.get())
|
||||
.option(DATABASE, BotSettings.SQL_DB.get())
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.dreamexposure.discal.core.extensions
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
fun Duration.getHumanReadable() = "%d days, %d hours, %d minutes, %d seconds%n"
|
||||
.format(toDays(), toHoursPart(), toMinutesPart(), toSecondsPart())
|
||||
+12
-12
@@ -4,35 +4,35 @@ import discord4j.common.util.Snowflake
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import org.dreamexposure.discal.core.crypto.KeyGenerator
|
||||
import org.dreamexposure.discal.core.enums.calendar.CalendarHost
|
||||
import org.dreamexposure.discal.core.serializers.SnowflakeAsStringSerializer
|
||||
import org.dreamexposure.novautils.crypto.KeyGenerator
|
||||
import java.time.Instant
|
||||
|
||||
@Serializable
|
||||
data class CalendarData(
|
||||
@Serializable(with = SnowflakeAsStringSerializer::class)
|
||||
@Serializable(with = SnowflakeAsStringSerializer::class)
|
||||
@SerialName("guild_id")
|
||||
val guildId: Snowflake = Snowflake.of(0),
|
||||
@SerialName("calendar_number")
|
||||
@SerialName("calendar_number")
|
||||
val calendarNumber: Int = 1,
|
||||
val host: CalendarHost,
|
||||
@SerialName("calendar_id")
|
||||
val host: CalendarHost,
|
||||
@SerialName("calendar_id")
|
||||
val calendarId: String = "primary",
|
||||
@SerialName("calendar_address")
|
||||
@SerialName("calendar_address")
|
||||
val calendarAddress: String = "primary",
|
||||
val external: Boolean = false,
|
||||
val external: Boolean = false,
|
||||
|
||||
//secure values that should not be serialized
|
||||
@Transient
|
||||
@Transient
|
||||
val credentialId: Int = 0,
|
||||
@Transient
|
||||
@Transient
|
||||
var privateKey: String = KeyGenerator.csRandomAlphaNumericString(16),
|
||||
@Transient
|
||||
@Transient
|
||||
var encryptedAccessToken: String = "N/a",
|
||||
@Transient
|
||||
@Transient
|
||||
var encryptedRefreshToken: String = "N/a",
|
||||
@Transient
|
||||
@Transient
|
||||
var expiresAt: Instant = Instant.now()
|
||||
): Comparable<CalendarData> {
|
||||
constructor(guildId: Snowflake, calendarNumber: Int, host: CalendarHost, calendarId: String,
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.dreamexposure.discal.core.`object`.network.discal
|
||||
|
||||
import discord4j.core.GatewayDiscordClient
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.dreamexposure.discal.Application
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
@Suppress("DataClassPrivateConstructor")
|
||||
@Serializable
|
||||
data class BotInstanceData private constructor(
|
||||
@SerialName("instance")
|
||||
val instanceData: InstanceData,
|
||||
|
||||
@SerialName("shard_index")
|
||||
val shardIndex: Int,
|
||||
|
||||
@SerialName("shard_count")
|
||||
val shardCount: Int,
|
||||
|
||||
val guilds: Int = 0,
|
||||
) {
|
||||
companion object {
|
||||
fun load(client: GatewayDiscordClient?): Mono<BotInstanceData> {
|
||||
return Mono.justOrEmpty(client)
|
||||
.flatMap { it.guilds.count() }
|
||||
.map(Long::toInt)
|
||||
.defaultIfEmpty(0)
|
||||
.map { guildCount ->
|
||||
BotInstanceData(
|
||||
instanceData = InstanceData(),
|
||||
shardIndex = Application.getShardIndex().toInt(),
|
||||
shardCount = Application.getShardCount(),
|
||||
guilds = guildCount
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package org.dreamexposure.discal.core.`object`.network.discal
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import org.dreamexposure.discal.GitProperty
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Serializable
|
||||
data class ConnectedClient(
|
||||
@SerialName("index")
|
||||
val clientIndex: Int = -1,
|
||||
|
||||
@SerialName("expected")
|
||||
val expectedClientCount: Int = -1,
|
||||
|
||||
val version: String = GitProperty.DISCAL_VERSION.value,
|
||||
|
||||
@SerialName("d4j_version")
|
||||
val d4jVersion: String = GitProperty.DISCAL_VERSION_D4J.value,
|
||||
|
||||
@SerialName("guilds")
|
||||
val connectedServers: Int = 0,
|
||||
|
||||
@SerialName("keep_alive")
|
||||
val lastKeepAlive: Long = System.currentTimeMillis(),
|
||||
|
||||
val uptime: String = "ERROR",
|
||||
|
||||
@SerialName("memory")
|
||||
val memUsed: Double = 0.0,
|
||||
|
||||
@Transient
|
||||
val instanceId: UUID = UUID.randomUUID()
|
||||
) {
|
||||
fun getLastKeepAliveHumanReadable(): String {
|
||||
val sdf = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
|
||||
|
||||
return sdf.format(Date(this.lastKeepAlive))
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package org.dreamexposure.discal.core.`object`.network.discal
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.dreamexposure.discal.Application
|
||||
import org.dreamexposure.discal.GitProperty
|
||||
import org.dreamexposure.discal.core.serializers.DurationAsStringSerializer
|
||||
import org.dreamexposure.discal.core.serializers.InstantAsStringSerializer
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Suppress("DataClassPrivateConstructor")
|
||||
@Serializable
|
||||
data class InstanceData(
|
||||
@SerialName("instance_id")
|
||||
val instanceId: String = Application.instanceId.toString(),
|
||||
|
||||
val version: String = GitProperty.DISCAL_VERSION.value,
|
||||
|
||||
@SerialName("d4j_version")
|
||||
val d4jVersion: String = GitProperty.DISCAL_VERSION_D4J.value,
|
||||
|
||||
@Serializable(with = DurationAsStringSerializer::class)
|
||||
val uptime: Duration = Application.getUptime(),
|
||||
|
||||
@SerialName("last_heartbeat")
|
||||
@Serializable(with = InstantAsStringSerializer::class)
|
||||
val lastHeartbeat: Instant = Instant.now(),
|
||||
|
||||
val memory: Double = Application.getMemoryUsedInMb(),
|
||||
) {
|
||||
@Suppress("unused") //Used in thymeleaf status page
|
||||
fun getHumanReadableHeartbeat(): String {
|
||||
val formatter = DateTimeFormatter
|
||||
.ofPattern("yyyy/MM/dd HH:mm:ss")
|
||||
.withZone(ZoneId.of("UTC"))
|
||||
|
||||
return "$formatter.format(lastHeartbeat) UTC"
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.dreamexposure.discal.core.`object`.network.discal
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
@Serializable
|
||||
data class NetworkData(
|
||||
@SerialName("total_calendars")
|
||||
var totalCalendars: Int = 0,
|
||||
|
||||
@SerialName("total_announcements")
|
||||
var totalAnnouncements: Int = 0,
|
||||
|
||||
@SerialName("api_status")
|
||||
var apiStatus: InstanceData? = null,
|
||||
|
||||
@SerialName("website_status")
|
||||
var websiteStatus: InstanceData? = null,
|
||||
|
||||
@SerialName("bot_status")
|
||||
val botStatus: MutableList<BotInstanceData> = CopyOnWriteArrayList()
|
||||
) {
|
||||
@SerialName("expected_shard_count")
|
||||
val expectedShardCount: Int
|
||||
get() {
|
||||
return if (botStatus.isNotEmpty()) botStatus[0].shardCount
|
||||
else -1
|
||||
}
|
||||
|
||||
@SerialName("total_guilds")
|
||||
val totalGuilds: Int
|
||||
get() {
|
||||
var guilds = 0
|
||||
botStatus.forEach { guilds += it.guilds }
|
||||
return guilds
|
||||
}
|
||||
|
||||
@Suppress("unused") //Used in thymeleaf status page
|
||||
fun getCurrentShardCount() = botStatus.size
|
||||
}
|
||||
+12
-21
@@ -2,30 +2,21 @@ package org.dreamexposure.discal.core.`object`.rest
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.dreamexposure.discal.core.serializers.UUIDasStringSerializer
|
||||
import java.util.*
|
||||
import org.dreamexposure.discal.core.`object`.network.discal.BotInstanceData
|
||||
import org.dreamexposure.discal.core.`object`.network.discal.InstanceData
|
||||
|
||||
//TODO: Redo
|
||||
@Serializable
|
||||
data class HeartbeatRequest(
|
||||
@SerialName("instance_id")
|
||||
@Serializable(with = UUIDasStringSerializer::class)
|
||||
val instanceId: UUID,
|
||||
val type: HeartbeatType,
|
||||
|
||||
@SerialName("client_index")
|
||||
val clientIndex: String,
|
||||
@SerialName("instance")
|
||||
val instanceData: InstanceData? = null,
|
||||
|
||||
@SerialName("expected_clients")
|
||||
val expectedClients: Int,
|
||||
|
||||
@SerialName("guilds")
|
||||
val guildCount: Int,
|
||||
|
||||
val memory: Double,
|
||||
|
||||
val uptime: String,
|
||||
|
||||
val version: String,
|
||||
|
||||
@SerialName("d4j_version")
|
||||
val d4jVersion: String,
|
||||
@SerialName("bot_instance")
|
||||
val botInstanceData: BotInstanceData? = null
|
||||
)
|
||||
|
||||
enum class HeartbeatType {
|
||||
BOT, WEBSITE
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.dreamexposure.discal.core.serializers
|
||||
|
||||
import discord4j.common.util.Snowflake
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.time.Duration
|
||||
|
||||
object DurationAsStringSerializer : KSerializer<Duration> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Duration", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Duration) = encoder.encodeString(value.toMillis().toString())
|
||||
|
||||
override fun deserialize(decoder: Decoder): Duration = Duration.ofMillis(decoder.decodeString().toLong())
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.dreamexposure.discal.core.serializers
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.time.Instant
|
||||
|
||||
object InstantAsStringSerializer : KSerializer<Instant> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Duration", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Instant) = encoder.encodeString(value.toString())
|
||||
|
||||
override fun deserialize(decoder: Decoder): Instant = Instant.parse(decoder.decodeString())
|
||||
}
|
||||
Reference in New Issue
Block a user