Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/metrics build diff report #390

Merged
merged 77 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
93267c8
feat: WIP get build diff report
RomanDavlyatshin May 30, 2024
59818c1
feat: WIP get build diff report
RomanDavlyatshin May 31, 2024
d2a3f51
feat: add api-key auth to /metrics api
RomanDavlyatshin Jun 4, 2024
c879849
feat: change /build-diff-report response
iryabov Jun 4, 2024
afe39ad
fix: make SQL functions output prefixes consistent (double underscore…
RomanDavlyatshin Jun 4, 2024
ccd6385
Merge branch 'feature/metrics-build-diff-report-api' into feature/met…
RomanDavlyatshin Jun 5, 2024
5fb1e16
Merge pull request #392 from Drill4J/feature/metrics-build-diff-repor…
RomanDavlyatshin Jun 5, 2024
a90d076
fix: column naming in raw_data.get_build_risks_accumulated_coverage a…
RomanDavlyatshin Jun 5, 2024
8eb012d
feat: metrics service implementation
RomanDavlyatshin Jun 5, 2024
08792af
feat: rename metrics_functions migration to R__ to allow editing
RomanDavlyatshin Jun 5, 2024
2cde1f6
fix: AnySerializer - add case to encode Long
RomanDavlyatshin Jun 5, 2024
aa54634
chore: add TODO for coverageThreshold type
RomanDavlyatshin Jun 5, 2024
40184f0
feat: admin-metrics error handling for build-diff-report
RomanDavlyatshin Jun 5, 2024
ed9f0a0
chore: remove obsolete comment
RomanDavlyatshin Jun 5, 2024
d361758
feat: get_build_risks_accumulated_coverage - filter methods by group_…
RomanDavlyatshin Jun 5, 2024
32b182e
chore: remove obsolete comment
RomanDavlyatshin Jun 5, 2024
f379e04
refactor: rename function
RomanDavlyatshin Jun 5, 2024
11d5dfa
refactor: remove obsolete function
RomanDavlyatshin Jun 5, 2024
d7e918c
build: add status pages dependency
iryabov Jun 6, 2024
861cf16
feat: coalesce probe coverage ratio to 0
RomanDavlyatshin Jun 7, 2024
96106a5
feat: provide link to full_report in /build-diff-report api response
RomanDavlyatshin Jun 7, 2024
600e8a5
fix: join matched checksum methods coverage - take classname into acc…
RomanDavlyatshin Jun 7, 2024
6f42027
chore: add TODO
RomanDavlyatshin Jun 7, 2024
d30390e
chore: add license
bodyangug Jun 11, 2024
17a692c
fix: build diff report changes_modified_methods field typo
RomanDavlyatshin Jun 12, 2024
5654124
Merge remote-tracking branch 'refs/remotes/origin/feature/metrics-bui…
RomanDavlyatshin Jun 12, 2024
b8a3f0d
feat: add a script to DB migrations that removes all postgresql funct…
iryabov Jun 12, 2024
2b684ae
feat: remove default for DRILL_METRICS_UI_BASE_URL + return nulls for…
RomanDavlyatshin Jun 12, 2024
8f9feda
fix: get_build_risks_accumulated_coverage - filter out tests with no …
RomanDavlyatshin Jun 13, 2024
85e3969
fix: broken build-diff-report endpoint sql query
RomanDavlyatshin Jun 13, 2024
bf337ab
fix: comment out coverageThreshold for now
RomanDavlyatshin Jun 13, 2024
f2c07f2
fix: error in query to delete all functions from raw_data
RomanDavlyatshin Jun 13, 2024
a2d4e64
fix: get_recommended_tests - return tests distinct by definition id (…
RomanDavlyatshin Jun 13, 2024
63489cf
feat: add build and baseline_build links + return "links": null if ui…
RomanDavlyatshin Jun 13, 2024
0774541
fix: get_recommended_tests returning incorrect number of tests
RomanDavlyatshin Jun 13, 2024
5ddde84
fix: builds ids coverage source - add distinct
RomanDavlyatshin Jun 17, 2024
c227d8d
feat: get_build_risks_accumulated_coverage - return method's params a…
RomanDavlyatshin Jun 17, 2024
b24794e
chore: remove obsolete comments
RomanDavlyatshin Jun 17, 2024
b6c8b1a
feat: add MetricsServiceUiLinksConfig
RomanDavlyatshin Jun 17, 2024
0f82cd4
feat: add accumulated class coverage sql function
RomanDavlyatshin Jun 17, 2024
3c8cbd3
refactor: replace repetitive queries with get_methods function
RomanDavlyatshin Jun 19, 2024
d96c723
refactor: replace repeptive instance querying with a function
RomanDavlyatshin Jun 19, 2024
91ef82a
fix: add missing schema name to get_instance_ids fn
RomanDavlyatshin Jun 20, 2024
60be4e0
refactor: create get_same_group_and_app_methods sql fn
RomanDavlyatshin Jun 20, 2024
01a8388
fix: raw_data.get_instance fn rename id to __id to avoid naming ambig…
RomanDavlyatshin Jun 21, 2024
3ba8678
fix: add oauth2.automaticSignIn and simple.signUpEnabled to applicati…
RomanDavlyatshin Jul 9, 2024
0b698db
fix: change default oauth2 sign in button text
RomanDavlyatshin Jul 9, 2024
bc0c7eb
feat: add userBlocked to ApiKeyView
RomanDavlyatshin Jul 12, 2024
e5aab32
Merge pull request #394 from Drill4J/fix/auth-config-missing-params
RomanDavlyatshin Jul 15, 2024
6cbc812
refactor: metricsRoutes fn name
RomanDavlyatshin Jul 15, 2024
4e28650
refactor: rename accumulated risks fn
RomanDavlyatshin Jul 16, 2024
9c32544
refactor: sql fns unify names
RomanDavlyatshin Jul 16, 2024
41cfc5f
feat: add get_accumulated_coverage_total_percent sql fn
RomanDavlyatshin Jul 16, 2024
53aff1c
feat: add get_coverage_by_risks sql function
RomanDavlyatshin Jul 17, 2024
6289f86
fix: get_coverage_by_risks - remove unnecessary where clause
RomanDavlyatshin Jul 17, 2024
046faab
fix: get_coverage_by_risks using coverage from wrong builds/instances…
RomanDavlyatshin Jul 19, 2024
af4195e
feat: get_coverage_by_methods - add associated_test_ids
RomanDavlyatshin Jul 22, 2024
7c84414
feat: raw_data.get_coverage_by_methods filtering by packages/classes/…
RomanDavlyatshin Jul 25, 2024
dad8f7a
feat: raw_data.get_coverage_by_methods filter by test names, test run…
RomanDavlyatshin Jul 25, 2024
2923bd4
feat: filter methods coverage report by test_names and test_runners
RomanDavlyatshin Jul 31, 2024
fa250fd
feat: test_launch_id + store test_definitions and test_launches in se…
RomanDavlyatshin Aug 2, 2024
c495464
feat: aggregate coverage - add filtering by test_definition_id
RomanDavlyatshin Aug 5, 2024
0b21bc5
refactor: simplify filtered coverage calculation
RomanDavlyatshin Aug 6, 2024
65cf8a6
feat: add `testTaskId` parameter to `saveTestMetadata`
iryabov Aug 7, 2024
143758d
Merge pull request #395 from Drill4J/feature/test-task-id-EPMDJ-10878
RomanDavlyatshin Aug 7, 2024
36ceb90
fix: fix table alias from `tests` to `test_definitions`
iryabov Aug 9, 2024
1fbe332
feat: use raw_data.test_launches to group reports
RomanDavlyatshin Aug 9, 2024
7dd3df7
chore: remove obsolete comment
RomanDavlyatshin Aug 9, 2024
d3cd514
feat: coverage by test tasks
RomanDavlyatshin Aug 9, 2024
f3a95c0
Merge pull request #396 from Drill4J/feature/group-by-test-task-id
RomanDavlyatshin Aug 12, 2024
8a0d571
chore: remove obsolete comment
RomanDavlyatshin Aug 12, 2024
7f11c7d
fix: change return type for probes counts + add division-by-zero safe…
RomanDavlyatshin Aug 12, 2024
5057c90
feat: aggregate coverage by test task fn
RomanDavlyatshin Aug 12, 2024
80aefb7
refactor: add get_risks fn to reduce dup code
RomanDavlyatshin Aug 13, 2024
16a2464
chore: add license header
RomanDavlyatshin Aug 13, 2024
3b766d1
chore: add license header
RomanDavlyatshin Aug 13, 2024
7136935
feat: update get_recommended_tests fn and implement API route
RomanDavlyatshin Aug 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,27 @@ import com.epam.drill.admin.auth.route.*
import com.epam.drill.admin.config.dataSourceDIModule
import com.epam.drill.admin.metrics.config.MetricsDatabaseConfig
import com.epam.drill.admin.metrics.config.metricsDIModule
import com.epam.drill.admin.metrics.route.metricRoutes
import com.epam.drill.admin.metrics.route.metricsRoutes
import com.epam.drill.admin.metrics.route.metricsStatusPages
import com.epam.drill.admin.route.rootRoute
import com.epam.drill.admin.route.uiConfigRoute
import com.epam.drill.admin.writer.rawdata.config.RawDataWriterDatabaseConfig
import com.epam.drill.admin.writer.rawdata.config.rawDataWriterDIModule
import com.epam.drill.admin.writer.rawdata.route.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.serialization.kotlinx.protobuf.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.plugins.callloging.*
import io.ktor.server.plugins.compression.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.request.*
import io.ktor.server.resources.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.Json
import mu.KotlinLogging
import org.kodein.di.instance
Expand Down Expand Up @@ -69,6 +70,7 @@ fun Application.module() {
authStatusPages()
if (oauth2Enabled) oauthStatusPages()
defaultStatusPages()
metricsStatusPages()
}
val di = closestDI()
install(Authentication) {
Expand Down Expand Up @@ -98,6 +100,7 @@ fun Application.module() {
}
authenticate("api-key") {
tryApiKeyRoute()
metricsRoutes()
}

//Data
Expand All @@ -115,8 +118,6 @@ fun Application.module() {
// postRawJavaScriptCoverage(jsCoverageConverterAddress)
}
}

metricRoutes()
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions admin-app/src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ drill {
clientSecret = ${?DRILL_OAUTH2_CLIENT_SECRET}
scopes = ${?DRILL_OAUTH2_SCOPES}
redirectUrl = ${?DRILL_OAUTH2_REDIRECT_URL}
automaticSignIn = false
automaticSignIn = ${?DRILL_OAUTH2_AUTOMATIC_SIGN_IN}
tokenMapping {
}
userInfoMapping {
Expand All @@ -53,10 +55,20 @@ drill {
enabled = true
enabled = ${?DRILL_AUTH_SIMPLE_ENABLED}
signUpEnabled = true
signUpEnabled = ${?DRILL_AUTH_SIMPLE_SIGN_UP_ENABLED}
passwordStrength {
}
}
}
rawData {
}
metrics {
ui {
baseUrl = ${?DRILL_METRICS_UI_BASE_URL}
buildTestingReportPath = "/dashboard/2"
buildTestingReportPath = ${?DRILL_METRICS_UI_BUILD_TESTING_REPORT_PATH }
buildComparisonReportPath = "/dashboard/3"
buildComparisonReportPath = ${?DRILL_METRICS_UI_BUILD_COMPARISON_REPORT_PATH }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class OAuth2Config(private val config: ApplicationConfig) {
* A title of the OAuth2 sign-in button. Optional, "Sign in with SSO" by default.
*/
val signInButtonTitle: String
get() = config.propertyOrNull("buttonTitle")?.getString() ?: "Sign in with SSO"
get() = config.propertyOrNull("buttonTitle")?.getString() ?: "Sign in with Auth Provider"

/**
* A flag that indicates whether the automatic sign-in is enabled. Optional, true by default.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ data class ApiKeyView(
val createdAt: LocalDateTime,
val username: String,
val role: Role,
val userBlocked: Boolean
)

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ private fun ApiKeyEntity.toApiKeyView() = ApiKeyView(
createdAt = createdAt.toKotlinLocalDateTime(),
username = user?.username ?: throw IllegalStateException("User property cannot be null in ApiKeyEntity"),
role = user.role.let { Role.valueOf(it) },
userBlocked = user.blocked
)

private fun ApiKeyEntity.toUserApiKeyView() = UserApiKeyView(
Expand Down
1 change: 1 addition & 0 deletions admin-metrics/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ dependencies {
implementation("io.ktor:ktor-server-auth:$ktorVersion")
implementation("io.ktor:ktor-server-auth-jwt:$ktorVersion")
implementation("io.ktor:ktor-server-resources:$ktorVersion")
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ package com.epam.drill.admin.metrics.config

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.encodeToJsonElement
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.IColumnType
import org.jetbrains.exposed.sql.Transaction
Expand All @@ -45,33 +41,47 @@ object MetricsDatabaseConfig {
newSuspendedTransaction(dispatcher, database) { block() }
}

fun Transaction.executeQuery(sqlQuery: String, vararg params: Any): List<JsonObject> {
val result = mutableListOf<JsonObject>()
execSp(sqlQuery, *params) { resultSet ->
fun Transaction.executeQueryReturnMap(sqlQuery: String, vararg params: Any?): List<Map<String, Any?>> {
val result = mutableListOf<Map<String, Any?>>()
executePreparedStatement(sqlQuery, *params) { resultSet ->
val metaData = resultSet.metaData
val columnCount = metaData.columnCount

while (resultSet.next()) {
val rowObject = buildJsonObject {
for (i in 1..columnCount) {
val columnName = metaData.getColumnName(i)
val columnValue = resultSet.getObject(i)
val stringValue = columnValue?.toString()
put(columnName, Json.encodeToJsonElement(stringValue))
val rowObject = mutableMapOf<String, Any?>()

for (i in 1..columnCount) {
val columnName = metaData.getColumnName(i)
val columnType = metaData.getColumnType(i)

val columnValue = when (columnType) {
java.sql.Types.INTEGER -> resultSet.getInt(i)
java.sql.Types.BIGINT -> resultSet.getLong(i)
java.sql.Types.FLOAT -> resultSet.getFloat(i)
java.sql.Types.DOUBLE -> resultSet.getDouble(i)
java.sql.Types.DECIMAL, java.sql.Types.NUMERIC -> resultSet.getBigDecimal(i)
java.sql.Types.BOOLEAN -> resultSet.getBoolean(i)
java.sql.Types.VARCHAR, java.sql.Types.CHAR, java.sql.Types.LONGVARCHAR -> resultSet.getString(i)
java.sql.Types.DATE -> resultSet.getDate(i)
java.sql.Types.TIMESTAMP -> resultSet.getTimestamp(i)
java.sql.Types.TIME -> resultSet.getTime(i)
java.sql.Types.BINARY, java.sql.Types.VARBINARY, java.sql.Types.LONGVARBINARY -> resultSet.getBytes(i)
else -> resultSet.getObject(i) // Fallback to generic Object type
}
rowObject[columnName] = if (resultSet.wasNull()) null else columnValue
}
result.add(rowObject)
}
}
return result
}

private fun <T : Any> Transaction.execSp(stmt: String, vararg params: Any, transform: (ResultSet) -> T): T? {
private fun <T : Any> Transaction.executePreparedStatement(stmt: String, vararg params: Any?, transform: (ResultSet) -> T): T? {
if (stmt.isEmpty()) return null

return exec(object : Statement<T>(StatementType.SELECT, emptyList()) {
override fun PreparedStatementApi.executeInternal(transaction: Transaction): T? {
params.forEachIndexed { idx, value -> set(idx + 1, value) }
params.forEachIndexed { idx, value -> set(idx + 1, value ?: "NULL") }
executeQuery()
return resultSet?.use { transform(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,23 @@
*/
package com.epam.drill.admin.metrics.config

import com.epam.drill.admin.metrics.repository.*
import com.epam.drill.admin.metrics.repository.impl.*
import com.epam.drill.admin.metrics.repository.impl.MetricsRepositoryImpl
import com.epam.drill.admin.metrics.service.MetricsService
import com.epam.drill.admin.metrics.service.impl.MetricsServiceImpl
import io.ktor.server.application.*
import io.ktor.server.config.*
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.instance
import org.kodein.di.singleton

val metricsDIModule = DI.Module("metricsServices") {
bind<MetricsRepository>() with singleton { MetricsRepositoryImpl() }
}
bind<MetricsService>() with singleton {
val metricsConfig: ApplicationConfig = instance<Application>().environment.config.config("drill.metrics")
MetricsServiceImpl(
MetricsRepositoryImpl(),
MetricsServiceUiLinksConfig(metricsConfig.config("ui"))
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright 2020 - 2022 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.epam.drill.admin.metrics.config

import io.ktor.server.config.*

class MetricsServiceUiLinksConfig(
val baseUrl: String?,
val buildTestingReportPath: String?,
val buildComparisonReportPath: String?
) {
constructor(config: ApplicationConfig) : this(
baseUrl = config.propertyOrNull("baseUrl")?.getString(),
buildTestingReportPath = config.propertyOrNull("buildTestingReportPath")?.getString(),
buildComparisonReportPath = config.propertyOrNull("buildComparisonReportPath")?.getString()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright 2020 - 2022 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.epam.drill.admin.metrics.exception

class BuildNotFound(message: String): RuntimeException(message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright 2020 - 2022 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.epam.drill.admin.metrics.exception

class InvalidParameters(message: String): RuntimeException(message)
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,17 @@
*/
package com.epam.drill.admin.metrics.repository

import kotlinx.serialization.json.JsonObject

interface MetricsRepository {

suspend fun getRisksByBranchDiff(
groupId: String,
appId: String,
currentBranch: String,
currentVcsRef: String,
baseBranch: String,
baseVcsRef: String
): List<JsonObject>

suspend fun getTotalCoverage(
groupId: String,
appId: String,
currentVcsRef: String
): JsonObject
suspend fun buildExists(buildId: String): Boolean
suspend fun getBuildDiffReport(
buildId: String,
baselineBuildId: String,
coverageThreshold: Double
): Map<String, String>

suspend fun getSummaryByBranchDiff(
groupId: String,
appId: String,
currentBranch: String,
currentVcsRef: String,
baseBranch: String,
baseVcsRef: String
): JsonObject
suspend fun getRecommendedTests(
buildId: String,
baselineBuildId: String
): List<Map<String, Any>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Copyright 2020 - 2022 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.epam.drill.admin.metrics.repository.impl

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.sql.Timestamp

object AnySerializer : KSerializer<Any?> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Any")

override fun serialize(encoder: Encoder, value: Any?) {
when (value) {
null -> encoder.encodeNull()
is String -> encoder.encodeString(value)
is Int -> encoder.encodeInt(value)
is Long -> encoder.encodeLong(value)
is Number -> encoder.encodeDouble(value.toDouble())
is Boolean -> encoder.encodeBoolean(value)
is Timestamp -> encoder.encodeString(value.toString())
is Map<*, *> -> {
val mapSerializer = MapSerializer(String.serializer(), this)
encoder.encodeSerializableValue(mapSerializer, value as Map<String, Any?>)
}
is List<*> -> {
val listSerializer = ListSerializer(this)
encoder.encodeSerializableValue(listSerializer, value)
}
else -> throw SerializationException("Unsupported type")
}
}

override fun deserialize(decoder: Decoder): Any? {
throw SerializationException("Deserialization is not supported")
}
}

@Serializable
data class ApiResponse(
@Serializable(with = AnySerializer::class) val data: Map<String, Any?>?
)
Loading
Loading