refact: [NETWORK] format on data part to be able to filter and search… (#288)

Co-authored-by: Florent Champigny <florent@bere.al>
This commit is contained in:
Florent CHAMPIGNY 2025-10-02 14:32:56 +02:00 committed by GitHub
parent 8c6ce2e244
commit 08e58f60f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 4420 additions and 67 deletions

View file

@ -40,7 +40,7 @@ import io.github.openflocon.flocondesktop.common.db.converters.MapStringsConvert
import kotlinx.coroutines.Dispatchers
@Database(
version = 61,
version = 62,
entities = [
FloconNetworkCallEntity::class,
FileEntity::class,

View file

@ -6,35 +6,6 @@ import io.github.openflocon.domain.network.models.responseByteSizeFormatted
import io.github.openflocon.flocondesktop.features.network.list.model.NetworkItemViewState
import io.ktor.http.Url
fun extractDomain(url: String): String {
// Parse l'URL en un objet Url
val parsedUrl = Url(url)
// Utilise host pour le domaine et encodedPathAndQuery pour le chemin
val domainAndPath = parsedUrl.host
// Le code ci-dessous pourrait aussi fonctionner, mais host est plus précis pour le domaine
// return parsedUrl.hostWithPort + parsedUrl.fullPath
return domainAndPath.removePrefix("www.")
}
fun extractDomainAndPath(url: String): String {
// Parse l'URL en un objet Url
val parsedUrl = Url(url)
// Utilise host pour le domaine et encodedPathAndQuery pour le chemin
val domainAndPath = parsedUrl.host + parsedUrl.encodedPathAndQuery
// Le code ci-dessous pourrait aussi fonctionner, mais host est plus précis pour le domaine
// return parsedUrl.hostWithPort + parsedUrl.fullPath
return domainAndPath.removePrefix("www.")
}
fun extractPath(url: String): String {
val parsedUrl = Url(url)
return parsedUrl.encodedPathAndQuery
}
fun toUi(
networkCall: FloconNetworkCallDomainModel,
deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel?
@ -45,7 +16,7 @@ fun toUi(
timeFormatted = networkCall.response?.durationFormatted,
requestSize = networkCall.request.byteSizeFormatted,
responseSize = networkCall.responseByteSizeFormatted(),
domain = getDomainUi(networkCall),
domain = networkCall.request.domainFormatted,
type = toTypeUi(networkCall),
method = getMethodUi(networkCall),
status = getStatusUi(networkCall),
@ -53,9 +24,3 @@ fun toUi(
isFromOldAppInstance = deviceIdAndPackageName?.appInstance?.let { it != networkCall.appInstance } ?: false
)
}
fun getDomainUi(networkRequest: FloconNetworkCallDomainModel): String = when (networkRequest.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> extractDomainAndPath(networkRequest.request.url)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> extractDomain(networkRequest.request.url)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> networkRequest.request.url
}

View file

@ -5,20 +5,18 @@ import io.github.openflocon.flocondesktop.features.network.list.model.NetworkIte
fun toTypeUi(call: FloconNetworkCallDomainModel): NetworkItemViewState.NetworkTypeUi = when (val s = call.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> NetworkItemViewState.NetworkTypeUi.GraphQl(
queryName = s.query,
queryName = call.request.queryFormatted,
)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> {
val query = extractPath(call.request.url)
NetworkItemViewState.NetworkTypeUi.Url(
query = query,
method = call.request.method,
query = call.request.queryFormatted,
)
}
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> {
NetworkItemViewState.NetworkTypeUi.Grpc(
method = call.request.method,
method = call.request.queryFormatted,
)
}
}

View file

@ -32,7 +32,6 @@ data class NetworkItemViewState(
@Immutable
data class Url(
val method: String,
val query: String,
) : NetworkTypeUi {
override fun contains(text: String): Boolean = query.contains(text, ignoreCase = true)
@ -68,7 +67,6 @@ fun previewNetworkItemViewState(): NetworkItemViewState = NetworkItemViewState(
status = NetworkStatusUi("200", NetworkStatusUi.Status.SUCCESS),
type = NetworkItemViewState.NetworkTypeUi.Url(
query = "/search?q=test",
method = "get",
),
isMocked = false,
isFromOldAppInstance = false,

View file

@ -31,6 +31,9 @@ private fun FloconNetworkCallEntity.toRequestDomainModel(): FloconNetworkCallDom
isMocked = request.isMocked,
startTimeFormatted = request.startTimeFormatted,
byteSizeFormatted = request.byteSizeFormatted,
domainFormatted = request.domainFormatted,
queryFormatted = request.queryFormatted,
methodFormatted = request.methodFormatted,
specificInfos = when (type) {
FloconNetworkCallType.HTTP -> FloconNetworkCallDomainModel.Request.SpecificInfos.Http
FloconNetworkCallType.GRAPHQL -> FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl(
@ -49,6 +52,7 @@ private fun FloconNetworkResponseEmbedded.toDomainModel(): FloconNetworkCallDoma
durationMs = durationMs,
issue = responseError,
durationFormatted = durationFormatted,
statusFormatted = statusFormatted,
)
} else {
FloconNetworkCallDomainModel.Response.Success(
@ -60,6 +64,7 @@ private fun FloconNetworkResponseEmbedded.toDomainModel(): FloconNetworkCallDoma
durationFormatted = durationFormatted,
byteSizeFormatted = responseByteSizeFormatted ?: "",
isImage = isImage,
statusFormatted = statusFormatted,
specificInfos = when {
graphql != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl(
httpCode = graphql.responseHttpCode,

View file

@ -8,8 +8,6 @@ import io.github.openflocon.data.local.network.models.graphql.NetworkCallGraphQl
import io.github.openflocon.data.local.network.models.graphql.NetworkCallGraphQlResponseEmbedded
import io.github.openflocon.data.local.network.models.grpc.NetworkCallGrpcResponseEmbedded
import io.github.openflocon.data.local.network.models.http.NetworkCallHttpResponseEmbedded
import io.github.openflocon.domain.device.models.AppInstance
import io.github.openflocon.domain.device.models.DeviceId
import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
@ -43,7 +41,10 @@ fun FloconNetworkCallDomainModel.toEntity(
)
else -> null
}
},
domainFormatted = request.domainFormatted,
queryFormatted = request.queryFormatted,
methodFormatted = request.methodFormatted,
),
response = response?.let { networkResponse ->
when(networkResponse) {
@ -61,6 +62,7 @@ fun FloconNetworkCallDomainModel.toEntity(
isImage = false,
durationFormatted = networkResponse.durationFormatted,
responseByteSizeFormatted = null,
statusFormatted = networkResponse.statusFormatted,
)
}
is FloconNetworkCallDomainModel.Response.Success -> {
@ -74,6 +76,7 @@ fun FloconNetworkCallDomainModel.toEntity(
responseByteSizeFormatted = networkResponse.byteSizeFormatted,
responseError = null,
isImage = networkResponse.isImage,
statusFormatted = networkResponse.statusFormatted,
graphql = when (val s = networkResponse.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> NetworkCallGraphQlResponseEmbedded(
responseHttpCode = s.httpCode,

View file

@ -58,6 +58,10 @@ data class FloconNetworkRequestEmbedded(
val requestByteSize: Long,
val isMocked: Boolean,
val domainFormatted: String, // for sorting & filtering
val methodFormatted: String, // for sorting & filtering
val queryFormatted: String, // for sorting & filtering
@Embedded(prefix = "graphql_")
val graphql: NetworkCallGraphQlRequestEmbedded?,
)
@ -72,6 +76,7 @@ data class FloconNetworkResponseEmbedded(
val responseByteSizeFormatted: String?,
val responseError: String?,
val isImage: Boolean,
val statusFormatted: String, // for sorting & filtering
@Embedded(prefix = "graphql_")
val graphql: NetworkCallGraphQlResponseEmbedded?,

View file

@ -57,6 +57,16 @@ fun toDomain(
val requestSize = decoded.requestSize ?: 0L
val startTime = decoded.startTime!!
val specificInfos = when {
graphQl != null -> FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl(
query = graphQl.request.requestBody.query,
operationType = graphQl.request.operationType,
)
decoded.floconNetworkType == "grpc" -> FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc
else -> FloconNetworkCallDomainModel.Request.SpecificInfos.Http
}
val request = FloconNetworkCallDomainModel.Request(
url = decoded.url!!,
startTime = startTime,
@ -67,15 +77,20 @@ fun toDomain(
byteSize = requestSize,
byteSizeFormatted = ByteFormatter.formatBytes(requestSize),
isMocked = decoded.isMocked ?: false,
specificInfos = when {
graphQl != null -> FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl(
query = graphQl.request.requestBody.query,
operationType = graphQl.request.operationType,
)
decoded.floconNetworkType == "grpc" -> FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc
else -> FloconNetworkCallDomainModel.Request.SpecificInfos.Http
},
specificInfos = specificInfos,
domainFormatted = extractDomain(
requestUrl = decoded.url,
specificInfos = specificInfos,
),
methodFormatted = extractMethod(
requestMethod = decoded.method,
specificInfos = specificInfos,
),
queryFormatted = extractQueryFormatted(
requestUrl = decoded.url,
requestMethod = decoded.method,
specificInfos = specificInfos,
),
)
FloconNetworkCallDomainModel(

View file

@ -0,0 +1,42 @@
package com.flocon.data.remote.network.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.ktor.http.Url
internal fun extractDomain(
requestUrl: String,
specificInfos: FloconNetworkCallDomainModel.Request.SpecificInfos,
): String = when (specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> extractDomainAndPath(requestUrl)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> extractDomain(requestUrl)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> requestUrl
}
private fun extractDomain(url: String): String {
// Parse the URL into a Url object
val parsedUrl = Url(url)
// Use host for the domain
val domainAndPath = parsedUrl.host
// The code below could also work, but host is more precise for the domain
// return parsedUrl.hostWithPort + parsedUrl.fullPath
return domainAndPath.removePrefix("www.")
}
private fun extractDomainAndPath(url: String): String {
// Parse the URL into a Url object
val parsedUrl = Url(url)
// Use host for the domain and encodedPathAndQuery for the path
val domainAndPath = parsedUrl.host + parsedUrl.encodedPathAndQuery
// The code below could also work, but host is more precise for the domain
// return parsedUrl.hostWithPort + parsedUrl.fullPath
return domainAndPath.removePrefix("www.")
}
internal fun extractPath(url: String): String {
val parsedUrl = Url(url)
return parsedUrl.encodedPathAndQuery
}

View file

@ -0,0 +1,14 @@
package com.flocon.data.remote.network.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
internal fun extractMethod(
requestMethod: String,
specificInfos: FloconNetworkCallDomainModel.Request.SpecificInfos,
): String = when (specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> specificInfos.operationType.lowercase()
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> toHttpMethodUi(requestMethod)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> "grpc"
}
private fun toHttpMethodUi(httpMethod: String) = httpMethod.lowercase()

View file

@ -0,0 +1,13 @@
package com.flocon.data.remote.network.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
internal fun extractQueryFormatted(
requestUrl: String,
requestMethod: String,
specificInfos: FloconNetworkCallDomainModel.Request.SpecificInfos,
): String = when (val s = specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> s.query
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> extractPath(requestUrl)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> requestMethod
}

View file

@ -0,0 +1,25 @@
package com.flocon.data.remote.network.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
// be sure this keep aligned with the ui mapper
internal fun failureStatus() = "Exception"
internal fun extractStatus(specificInfos: FloconNetworkCallDomainModel.Response.Success.SpecificInfos): String =
when (val s = specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> toGraphQlNetworkStatus(isSuccess = s.isSuccess)
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http -> toNetworkStatus(s.httpCode)
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc -> toGrpcNetworkStatus(specificInfos)
}
private fun toNetworkStatus(code: Int): String = code.toString()
private fun toGraphQlNetworkStatus(isSuccess: Boolean): String =
if (isSuccess) "Success" else "Error"
private fun toGrpcNetworkStatus(
specificInfos: FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc
): String {
return specificInfos.grpcStatus
}

View file

@ -1,5 +1,7 @@
package com.flocon.data.remote.network.models
import com.flocon.data.remote.network.mapper.failureStatus
import com.flocon.data.remote.network.mapper.extractStatus
import io.github.openflocon.domain.common.ByteFormatter
import io.github.openflocon.domain.common.time.formatDuration
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
@ -59,27 +61,32 @@ internal fun FloconNetworkResponseDataModel.toDomain(): FloconNetworkResponseOnl
durationMs = durationMs,
issue = responseError,
durationFormatted = durationFormatted,
statusFormatted = failureStatus(),
)
} else {
val responseSize = responseSize ?: 0L
val specificInfos = when (floconNetworkType) {
"grpc" -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc(
grpcStatus = responseGrpcStatus!!,
)
// otherwise tread like http
else -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http(
httpCode = responseHttpCode!!,
)
}
FloconNetworkCallDomainModel.Response.Success(
durationMs = durationMs,
contentType = responseContentType,
body = responseBody,
headers = responseHeaders.orEmpty(),
byteSize = responseSize,
specificInfos = when (floconNetworkType) {
"grpc" -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc(
grpcStatus = responseGrpcStatus!!,
)
// otherwise tread like http
else -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http(
httpCode = responseHttpCode!!,
)
},
specificInfos = specificInfos,
isImage = isImage,
durationFormatted = durationFormatted,
byteSizeFormatted = ByteFormatter.formatBytes(responseSize)
byteSizeFormatted = ByteFormatter.formatBytes(responseSize),
statusFormatted = extractStatus(specificInfos)
)
}
FloconNetworkResponseOnlyDomainModel(

View file

@ -14,12 +14,15 @@ data class FloconNetworkCallDomainModel(
val startTime: Long,
val startTimeFormatted: String,
val method: String,
val methodFormatted: String,
val headers: Map<String, String>,
val body: String?,
val byteSize: Long,
val byteSizeFormatted: String,
val isMocked: Boolean,
val specificInfos: SpecificInfos,
val domainFormatted: String, // extracted from url
val queryFormatted: String, // extracted from url
) {
sealed interface SpecificInfos {
data object Http: SpecificInfos
@ -35,6 +38,7 @@ data class FloconNetworkCallDomainModel(
val durationMs: Double
val durationFormatted: String
val statusFormatted: String // extracted from response
data class Success(
override val durationMs: Double,
@ -45,7 +49,8 @@ data class FloconNetworkCallDomainModel(
val byteSize: Long,
val byteSizeFormatted: String,
val specificInfos: SpecificInfos,
val isImage: Boolean
val isImage: Boolean,
override val statusFormatted: String, // extracted from response
) : Response {
sealed interface SpecificInfos {
data class Http(
@ -64,6 +69,7 @@ data class FloconNetworkCallDomainModel(
override val durationMs: Double,
override val durationFormatted: String,
val issue: String,
override val statusFormatted: String, // extracted from response
) : Response
}
}