Refact network model (#123)

* refact: [NETWORK] model with error

* refact: [NETWORK] model with error

* refact: [NETWORK] model with error

* refact: [NETWORK] model with error

* refact: [NETWORK] model with error

* refact

* merged with main

---------

Co-authored-by: Florent Champigny <florent@bere.al>
This commit is contained in:
Florent CHAMPIGNY 2025-08-21 10:25:03 +02:00 committed by GitHub
parent db3c8a118c
commit 35d896af23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 571 additions and 504 deletions

View file

@ -2,11 +2,8 @@ package io.github.openflocon.flocon.plugins.network
import io.github.openflocon.flocon.core.FloconPlugin
import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig
import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCall
import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest
import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse
import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest
import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse
import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse
interface FloconNetworkPlugin : FloconPlugin {

View file

@ -25,4 +25,5 @@ data class FloconNetworkResponse(
val body: String?,
val size: Long?,
val headers: Map<String, String>,
val error: String?,
)

View file

@ -39,6 +39,7 @@ fun floconNetworkCallResponseToJson(network: FloconNetworkCallResponse): JSONObj
response.body?.let { json.put("responseBody", it) }
json.put("responseHeaders", JSONObject(response.headers))
json.put("responseSize", response.size)
response.error?.let { json.put("responseError", it) }
}
return json

View file

@ -148,6 +148,7 @@ private class LoggingClientCallListener<RespT>(
contentType = "grpc",
size = 0L,
grpcStatus = status.code.toString(),
error = null,
),
)
}

View file

@ -110,7 +110,8 @@ val FloconKtorPlugin = createClientPlugin("FloconKtorPlugin") {
body = bodyString.takeUnless { isImage },
headers = responseHeadersMap,
size = responseSize,
grpcStatus = null
grpcStatus = null,
error = null,
)
floconNetworkPlugin.logResponse(

View file

@ -149,6 +149,7 @@ class FloconOkhttpInterceptor() : Interceptor {
headers = responseHeadersMap,
size = responseSize,
grpcStatus = null,
error = null,
)
floconNetworkPlugin.logResponse(
@ -171,12 +172,13 @@ class FloconOkhttpInterceptor() : Interceptor {
val durationMs: Double = (endTime - startTime) / 1e6
val floconCallResponse = FloconNetworkResponse(
httpCode = 0,
contentType = "error",
body = e.message, // TODO better handle of errors
httpCode = null,
contentType = null,
body = null,
headers = emptyMap(),
size = null,
grpcStatus = null,
error = e.message ?: e.javaClass.simpleName,
)
floconNetworkPlugin.logResponse(

View file

@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 41,
"identityHash": "d699dfc54e3dbaa0e3ae992b29140a1e",
"identityHash": "f1dea53a3a26fcc273eacc8cb3f47879",
"entities": [
{
"tableName": "FloconNetworkCallEntity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` REAL, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` INTEGER, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseError` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))",
"fields": [
{
"fieldPath": "callId",
@ -86,7 +86,7 @@
{
"fieldPath": "response.durationMs",
"columnName": "response_durationMs",
"affinity": "REAL"
"affinity": "INTEGER"
},
{
"fieldPath": "response.responseContentType",
@ -108,6 +108,11 @@
"columnName": "response_responseByteSize",
"affinity": "INTEGER"
},
{
"fieldPath": "response.responseError",
"columnName": "response_responseError",
"affinity": "TEXT"
},
{
"fieldPath": "response.graphql.isSuccess",
"columnName": "response_graphql_isSuccess",
@ -1089,7 +1094,7 @@
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd699dfc54e3dbaa0e3ae992b29140a1e')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1dea53a3a26fcc273eacc8cb3f47879')"
]
}
}

View file

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

View file

@ -1,6 +1,7 @@
package io.github.openflocon.flocondesktop.features.network.detail.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.httpCode
import io.github.openflocon.flocondesktop.common.ui.ByteFormatter
import io.github.openflocon.flocondesktop.common.ui.JsonPrettyPrinter
import io.github.openflocon.flocondesktop.features.network.detail.model.NetworkDetailHeaderUi
@ -17,43 +18,70 @@ import io.github.openflocon.flocondesktop.features.network.list.model.NetworkSta
fun toDetailUi(request: FloconNetworkCallDomainModel): NetworkDetailViewState = NetworkDetailViewState(
callId = request.callId,
fullUrl = request.networkRequest.url,
fullUrl = request.request.url,
method = toDetailMethodUi(request),
status = toDetailHttpStatusUi(request),
requestTimeFormatted = request.networkRequest.startTime.let { formatTimestamp(it) },
durationFormatted = request.networkResponse?.durationMs?.let { formatDuration(it) },
requestTimeFormatted = request.request.startTime.let { formatTimestamp(it) },
durationFormatted = request.response?.durationMs?.let { formatDuration(it) },
// request
requestBody = httpBodyToUi(request.networkRequest.body),
requestHeaders = toNetworkHeadersUi(request.networkRequest.headers),
requestSize = ByteFormatter.formatBytes(request.networkRequest.byteSize),
requestBody = httpBodyToUi(request.request.body),
requestHeaders = toNetworkHeadersUi(request.request.headers),
requestSize = ByteFormatter.formatBytes(request.request.byteSize),
// response
response = request.networkResponse?.let {
NetworkDetailViewState.Response(
body = httpBodyToUi(it.body),
size = ByteFormatter.formatBytes(it.byteSize),
headers = toNetworkHeadersUi(it.headers),
)
response = request.response?.let {
when(it) {
is FloconNetworkCallDomainModel.Response.Failure -> NetworkDetailViewState.Response.Error(
issue = it.issue,
)
is FloconNetworkCallDomainModel.Response.Success -> NetworkDetailViewState.Response.Success(
body = httpBodyToUi(it.body),
size = ByteFormatter.formatBytes(it.byteSize),
headers = toNetworkHeadersUi(it.headers),
)
}
},
graphQlSection = graphQlSection(request),
)
private fun toDetailHttpStatusUi(networkCall: FloconNetworkCallDomainModel): NetworkStatusUi = networkCall.networkResponse?.let { response ->
when (networkCall) {
is FloconNetworkCallDomainModel.Grpc -> toGrpcNetworkStatusUi(networkCall)
// here for grphql we want the http code, the graphql status will be displayed on the specific graphql section
is FloconNetworkCallDomainModel.GraphQl -> toNetworkStatusUi(code = networkCall.response!!.httpCode)
is FloconNetworkCallDomainModel.Http -> toNetworkStatusUi(code = networkCall.response!!.httpCode)
private fun toDetailHttpStatusUi(networkCall: FloconNetworkCallDomainModel): NetworkStatusUi = networkCall.response?.let { response ->
when (response) {
is FloconNetworkCallDomainModel.Response.Failure -> NetworkStatusUi(
text = response.issue,
status = NetworkStatusUi.Status.ERROR,
)
is FloconNetworkCallDomainModel.Response.Success -> when(val s = response.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc -> toGrpcNetworkStatusUi(networkCall)
// here for grphql we want the http code, the graphql status will be displayed on the specific graphql section
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> toNetworkStatusUi(code = s.httpCode)
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http -> toNetworkStatusUi(code = s.httpCode)
}
}
} ?: loadingStatus()
fun graphQlSection(networkCall: FloconNetworkCallDomainModel): NetworkDetailViewState.GraphQlSection? = (networkCall as? FloconNetworkCallDomainModel.GraphQl)?.let {
NetworkDetailViewState.GraphQlSection(
queryName = it.request.query,
method = getMethodUi(networkCall),
status = it.response?.isSuccess?.let {
toGraphQlNetworkStatusUi(isSuccess = it)
} ?: loadingStatus(),
)
fun graphQlSection(networkCall: FloconNetworkCallDomainModel): NetworkDetailViewState.GraphQlSection? {
return (networkCall.request.specificInfos as? FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl)?.let {
NetworkDetailViewState.GraphQlSection(
queryName = it.query,
method = getMethodUi(networkCall),
status = graphQlStatus(networkCall) ?: return null
)
}
}
private fun graphQlStatus(networkCall: FloconNetworkCallDomainModel) : NetworkStatusUi? {
return when(val r = networkCall.response) {
is FloconNetworkCallDomainModel.Response.Failure -> NetworkStatusUi(
text = r.issue,
status = NetworkStatusUi.Status.ERROR,
)
is FloconNetworkCallDomainModel.Response.Success -> when(val s = r.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> toGraphQlNetworkStatusUi(isSuccess = s.isSuccess)
else -> null
}
null -> loadingStatus()
}
}
fun httpBodyToUi(body: String?): String = body?.let { JsonPrettyPrinter.prettyPrint(body) } ?: ""
@ -68,12 +96,12 @@ fun toNetworkHeadersUi(headers: Map<String, String>?): List<NetworkDetailHeaderU
}.sortedBy { it.name }
} ?: emptyList()
fun toDetailMethodUi(request: FloconNetworkCallDomainModel): NetworkDetailViewState.Method = when (request) {
is FloconNetworkCallDomainModel.Grpc -> NetworkDetailViewState.Method.MethodName(
name = request.networkRequest.method,
fun toDetailMethodUi(request: FloconNetworkCallDomainModel): NetworkDetailViewState.Method = when (request.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> NetworkDetailViewState.Method.MethodName(
name = request.request.method,
)
is FloconNetworkCallDomainModel.GraphQl,
is FloconNetworkCallDomainModel.Http,
-> NetworkDetailViewState.Method.Http(toHttpMethodUi(request.networkRequest.method))
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl,
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http,
-> NetworkDetailViewState.Method.Http(toHttpMethodUi(request.request.method))
}

View file

@ -24,11 +24,18 @@ data class NetworkDetailViewState(
val response: Response?,
) {
@Immutable
data class Response(
val body: String,
val size: String,
val headers: List<NetworkDetailHeaderUi>,
)
sealed interface Response {
@Immutable
data class Success(
val body: String,
val size: String,
val headers: List<NetworkDetailHeaderUi>,
) : Response
@Immutable
data class Error(
val issue: String,
) : Response
}
@Immutable
data class GraphQlSection(

View file

@ -122,11 +122,11 @@ private fun Request(
Column {
Column(
modifier =
Modifier
.background(
color = FloconTheme.colorPalette.surfaceVariant,
shape = RoundedCornerShape(12.dp),
).padding(horizontal = 8.dp, vertical = 4.dp),
Modifier
.background(
color = FloconTheme.colorPalette.surfaceVariant,
shape = RoundedCornerShape(12.dp),
).padding(horizontal = 8.dp, vertical = 4.dp),
) {
DetailLineTextView(
modifier = Modifier.fillMaxWidth(),
@ -198,11 +198,11 @@ private fun Request(
) {
Column(
modifier =
Modifier
.background(
color = FloconTheme.colorPalette.surfaceVariant,
shape = RoundedCornerShape(12.dp),
).padding(horizontal = 8.dp, vertical = 4.dp),
Modifier
.background(
color = FloconTheme.colorPalette.surfaceVariant,
shape = RoundedCornerShape(12.dp),
).padding(horizontal = 8.dp, vertical = 4.dp),
) {
DetailLineTextView(
modifier = Modifier.fillMaxWidth(),
@ -304,53 +304,72 @@ private fun Response(
expanded = isResponseExpanded,
) {
// headers
Column {
DetailSectionTitleView(
isExpanded = isResponseHeadersExpanded,
title = "Response Headers",
onCopy = null,
onToggle = {
isResponseHeadersExpanded = it
},
modifier = Modifier.fillMaxWidth(),
)
FloconSectionExpandable(
modifier = Modifier.fillMaxWidth(),
expanded = isResponseHeadersExpanded,
) {
DetailHeadersView(
headers = response.headers,
modifier = Modifier.fillMaxWidth(),
labelWidth = headersLabelWidth,
)
}
Column(
modifier = Modifier
.fillMaxWidth()
) {
when (response) {
is NetworkDetailViewState.Response.Error -> {
FloconSectionExpandable(
modifier = Modifier.fillMaxWidth(),
expanded = isResponseBodyExpanded,
) {
CodeBlockView(
code = response.issue,
modifier = Modifier.fillMaxWidth(),
)
}
}
// body
DetailSectionTitleView(
isExpanded = isResponseBodyExpanded,
title = "Response Body",
onCopy = { onAction(NetworkAction.CopyText(response.body)) },
onToggle = {
isResponseBodyExpanded = it
},
onDetail = {
onAction(
NetworkAction.JsonDetail(
state.callId + "response",
response.body,
),
is NetworkDetailViewState.Response.Success -> {
DetailSectionTitleView(
isExpanded = isResponseHeadersExpanded,
title = "Response Headers",
onCopy = null,
onToggle = {
isResponseHeadersExpanded = it
},
modifier = Modifier.fillMaxWidth(),
)
},
modifier = Modifier.fillMaxWidth(),
)
FloconSectionExpandable(
modifier = Modifier.fillMaxWidth(),
expanded = isResponseBodyExpanded,
) {
CodeBlockView(
code = response.body,
modifier = Modifier.fillMaxWidth(),
)
FloconSectionExpandable(
modifier = Modifier.fillMaxWidth(),
expanded = isResponseHeadersExpanded,
) {
DetailHeadersView(
headers = response.headers,
modifier = Modifier.fillMaxWidth(),
labelWidth = headersLabelWidth,
)
}
// body
DetailSectionTitleView(
isExpanded = isResponseBodyExpanded,
title = "Response Body",
onCopy = { onAction(NetworkAction.CopyText(response.body)) },
onToggle = {
isResponseBodyExpanded = it
},
onDetail = {
onAction(
NetworkAction.JsonDetail(
state.callId + "response",
response.body,
),
)
},
modifier = Modifier.fillMaxWidth(),
)
FloconSectionExpandable(
modifier = Modifier.fillMaxWidth(),
expanded = isResponseBodyExpanded,
) {
CodeBlockView(
code = response.body,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}
}
@ -367,18 +386,18 @@ private fun NetworkDetailViewPreview() {
fullUrl = "http://www.google.com",
method = NetworkDetailViewState.Method.Http(NetworkMethodUi.Http.GET),
status =
NetworkStatusUi(
text = "200",
status = NetworkStatusUi.Status.SUCCESS,
),
NetworkStatusUi(
text = "200",
status = NetworkStatusUi.Status.SUCCESS,
),
requestHeaders =
listOf(
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
),
listOf(
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
),
requestBody =
"""
"""
{
"id": "123",
"name": "Flocon App",
@ -394,9 +413,9 @@ private fun NetworkDetailViewPreview() {
requestTimeFormatted = "00:00:00.000",
durationFormatted = "300ms",
requestSize = "0kb",
response = NetworkDetailViewState.Response(
response = NetworkDetailViewState.Response.Success(
body =
"""
"""
{
"networkStatusUi": "success",
"message": "Data received and processed.",
@ -408,13 +427,13 @@ private fun NetworkDetailViewPreview() {
""".trimIndent(),
size = "0kb",
headers =
listOf(
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
),
listOf(
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
previewNetworkDetailHeaderUi(),
),
),
graphQlSection = null,
),

View file

@ -245,7 +245,7 @@ class NetworkViewModel(
viewModelScope.launch(dispatcherProvider.viewModel) {
val domainModel = observeHttpRequestsByIdUseCase(action.item.uuid).firstOrNull()
?: return@launch
copyToClipboard(domainModel.networkRequest.url)
copyToClipboard(domainModel.request.url)
}
}

View file

@ -3,14 +3,14 @@ package io.github.openflocon.flocondesktop.features.network.list.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.flocondesktop.features.network.list.model.NetworkMethodUi
fun getMethodUi(networkRequest: FloconNetworkCallDomainModel): NetworkMethodUi = when (networkRequest) {
is FloconNetworkCallDomainModel.GraphQl -> when (val t = networkRequest.request.operationType.lowercase()) {
fun getMethodUi(networkCall: FloconNetworkCallDomainModel): NetworkMethodUi = when (val s = networkCall.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> when (val t = s.operationType.lowercase()) {
"query" -> NetworkMethodUi.GraphQl.QUERY
"mutation" -> NetworkMethodUi.GraphQl.MUTATION
else -> NetworkMethodUi.OTHER(networkRequest.request.operationType, icon = null)
else -> NetworkMethodUi.OTHER(s.operationType, icon = null)
}
is FloconNetworkCallDomainModel.Http -> toHttpMethodUi(networkRequest.networkRequest.method)
is FloconNetworkCallDomainModel.Grpc -> NetworkMethodUi.Grpc
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> toHttpMethodUi(networkCall.request.method)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> NetworkMethodUi.Grpc
}
fun toHttpMethodUi(httpMethod: String): NetworkMethodUi = when (httpMethod.lowercase()) {

View file

@ -1,6 +1,7 @@
package io.github.openflocon.flocondesktop.features.network.list.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.byteSize
import io.github.openflocon.flocondesktop.common.ui.ByteFormatter
import io.github.openflocon.flocondesktop.features.network.list.model.NetworkItemViewState
import io.ktor.http.Url
@ -43,21 +44,21 @@ fun extractPath(url: String): String {
fun toUi(networkCall: FloconNetworkCallDomainModel): NetworkItemViewState = NetworkItemViewState(
uuid = networkCall.callId,
dateFormatted = formatTimestamp(networkCall.networkRequest.startTime),
timeFormatted = networkCall.networkResponse?.durationMs?.let { formatDuration(it) },
requestSize = ByteFormatter.formatBytes(networkCall.networkRequest.byteSize),
responseSize = networkCall.networkResponse?.byteSize?.let { ByteFormatter.formatBytes(it) },
dateFormatted = formatTimestamp(networkCall.request.startTime),
timeFormatted = networkCall.response?.durationMs?.let { formatDuration(it) },
requestSize = ByteFormatter.formatBytes(networkCall.request.byteSize),
responseSize = networkCall.byteSize()?.let { ByteFormatter.formatBytes(it) },
domain = getDomainUi(networkCall),
type = toTypeUi(networkCall),
method = getMethodUi(networkCall),
status = getStatusUi(networkCall),
isMocked = networkCall.networkRequest.isMocked,
isMocked = networkCall.request.isMocked,
)
fun getDomainUi(networkRequest: FloconNetworkCallDomainModel): String = when (networkRequest) {
is FloconNetworkCallDomainModel.GraphQl -> extractDomainAndPath(networkRequest.networkRequest.url)
is FloconNetworkCallDomainModel.Http -> extractDomain(networkRequest.networkRequest.url)
is FloconNetworkCallDomainModel.Grpc -> networkRequest.networkRequest.url
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
}
fun formatDuration(duration: Double): String = duration.milliseconds.toString(unit = DurationUnit.MILLISECONDS)

View file

@ -8,11 +8,17 @@ fun loadingStatus() = NetworkStatusUi(
status = NetworkStatusUi.Status.LOADING,
)
fun getStatusUi(networkCall: FloconNetworkCallDomainModel): NetworkStatusUi = networkCall.networkResponse?.let { response ->
when (val t = networkCall) {
is FloconNetworkCallDomainModel.GraphQl -> toGraphQlNetworkStatusUi(isSuccess = t.response!!.isSuccess)
is FloconNetworkCallDomainModel.Http -> toNetworkStatusUi(networkCall.response!!.httpCode)
is FloconNetworkCallDomainModel.Grpc -> toGrpcNetworkStatusUi(t)
fun getStatusUi(networkCall: FloconNetworkCallDomainModel): NetworkStatusUi = networkCall.response?.let { response ->
when(response) {
is FloconNetworkCallDomainModel.Response.Failure -> NetworkStatusUi(
text = response.issue,
status = NetworkStatusUi.Status.ERROR,
)
is FloconNetworkCallDomainModel.Response.Success -> when (val s = response.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> toGraphQlNetworkStatusUi(isSuccess = s.isSuccess)
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http -> toNetworkStatusUi(s.httpCode)
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc -> toGrpcNetworkStatusUi(networkCall)
}
}
} ?: loadingStatus()
@ -26,11 +32,27 @@ fun toGraphQlNetworkStatusUi(isSuccess: Boolean): NetworkStatusUi = NetworkStatu
status = if (isSuccess) NetworkStatusUi.Status.SUCCESS else NetworkStatusUi.Status.ERROR,
)
fun toGrpcNetworkStatusUi(call: FloconNetworkCallDomainModel.Grpc): NetworkStatusUi {
fun toGrpcNetworkStatusUi(call: FloconNetworkCallDomainModel): NetworkStatusUi {
val response = call.response ?: return loadingStatus()
val isSuccess = response.responseStatus == "OK"
return NetworkStatusUi(
text = response.responseStatus,
status = if (isSuccess) NetworkStatusUi.Status.SUCCESS else NetworkStatusUi.Status.ERROR,
)
return when(response) {
is FloconNetworkCallDomainModel.Response.Failure -> NetworkStatusUi(
text = response.issue,
status = NetworkStatusUi.Status.ERROR,
)
is FloconNetworkCallDomainModel.Response.Success -> {
when(val s = response.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc -> {
val isSuccess = s.grpcStatus == "OK"
NetworkStatusUi(
text = s.grpcStatus,
status = if (isSuccess) NetworkStatusUi.Status.SUCCESS else NetworkStatusUi.Status.ERROR,
)
}
else -> NetworkStatusUi(
text = "not a grpc response",
status = NetworkStatusUi.Status.ERROR,
)
}
}
}
}

View file

@ -3,22 +3,22 @@ package io.github.openflocon.flocondesktop.features.network.list.mapper
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.flocondesktop.features.network.list.model.NetworkItemViewState
fun toTypeUi(call: FloconNetworkCallDomainModel): NetworkItemViewState.NetworkTypeUi = when (call) {
is FloconNetworkCallDomainModel.GraphQl -> NetworkItemViewState.NetworkTypeUi.GraphQl(
queryName = call.request.query,
fun toTypeUi(call: FloconNetworkCallDomainModel): NetworkItemViewState.NetworkTypeUi = when (val s = call.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> NetworkItemViewState.NetworkTypeUi.GraphQl(
queryName = s.query,
)
is FloconNetworkCallDomainModel.Http -> {
val query = extractPath(call.networkRequest.url)
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> {
val query = extractPath(call.request.url)
NetworkItemViewState.NetworkTypeUi.Url(
query = query,
method = call.networkRequest.method,
method = call.request.method,
)
}
is FloconNetworkCallDomainModel.Grpc -> {
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> {
NetworkItemViewState.NetworkTypeUi.Grpc(
method = call.networkRequest.method,
method = call.request.method,
)
}
}

View file

@ -45,12 +45,12 @@ private fun sort(
}
val comparator = when (sorted.column) {
NetworkColumnsTypeUiModel.RequestTime -> compareBy<Pair<FloconNetworkCallDomainModel, NetworkItemViewState>> { it.first.networkRequest.startTime }
NetworkColumnsTypeUiModel.RequestTime -> compareBy<Pair<FloconNetworkCallDomainModel, NetworkItemViewState>> { it.first.request.startTime }
NetworkColumnsTypeUiModel.Method -> compareBy { it.second.method.text }
NetworkColumnsTypeUiModel.Domain -> compareBy { it.second.domain }
NetworkColumnsTypeUiModel.Query -> compareBy { it.second.type.text }
NetworkColumnsTypeUiModel.Status -> compareBy { it.second.status.text }
NetworkColumnsTypeUiModel.Time -> compareBy { it.first.networkResponse?.durationMs }
NetworkColumnsTypeUiModel.Time -> compareBy { it.first.response?.durationMs }
}
val sortedComparator = when (sorted.sort) {

View file

@ -38,12 +38,12 @@ class ImagesRepositoryImpl(
deviceId: String,
call: FloconNetworkCallDomainModel,
) {
val duration = call.networkResponse?.durationMs ?: return
val duration = call.response?.durationMs ?: return
imagesLocalDataSource.addImage(
deviceId = deviceId,
image = DeviceImageDomainModel(
url = call.networkRequest.url,
time = (call.networkRequest.startTime + duration).toLong(),
url = call.request.url,
time = (call.request.startTime + duration).toLong(),
),
)
}

View file

@ -5,7 +5,6 @@ import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainMo
import io.github.openflocon.domain.network.models.BadQualityConfigDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkCallIdDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseOnlyDomainModel
import io.github.openflocon.domain.network.models.MockNetworkDomainModel

View file

@ -14,6 +14,7 @@ import io.github.openflocon.domain.network.models.BadQualityConfigId
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseOnlyDomainModel
import io.github.openflocon.domain.network.models.MockNetworkDomainModel
import io.github.openflocon.domain.network.models.getContentType
import io.github.openflocon.domain.network.repository.NetworkBadQualityRepository
import io.github.openflocon.domain.network.repository.NetworkImageRepository
import io.github.openflocon.domain.network.repository.NetworkMocksRepository
@ -100,7 +101,7 @@ class NetworkRepositoryImpl(
}
toDomainForResponse(response = response, request = request)
}?.let { call ->
if (call.networkResponse?.contentType?.startsWith("image/") == true) {
if (call.response?.getContentType()?.startsWith("image/") == true) {
networkImageRepository.onImageReceived(deviceId = deviceId, call = call)
}
networkLocalDataSource.save(
@ -159,48 +160,31 @@ class NetworkRepositoryImpl(
response: FloconNetworkResponseOnlyDomainModel,
request: FloconNetworkCallDomainModel,
): FloconNetworkCallDomainModel? {
try {
val networkResponse = response.networkResponse
return when (request) {
is FloconNetworkCallDomainModel.GraphQl -> {
// throw an exception if not http
val responseHttp = response as FloconNetworkResponseOnlyDomainModel.Http
val response = FloconNetworkCallDomainModel.GraphQl.Response(
networkResponse = networkResponse,
httpCode = responseHttp.httpCode,
isSuccess = responseHttp.httpCode in 200..299,
)
request.copy(
response = response,
)
return try {
val response = when (val r = response.response) {
is FloconNetworkCallDomainModel.Response.Success -> {
// specific case : map to graphQl if needed
when (val s = r.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http -> {
r.copy(
specificInfos = FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl(
httpCode = s.httpCode,
isSuccess = s.httpCode in 200..299
)
)
}
else -> r
}
}
is FloconNetworkCallDomainModel.Grpc -> {
val responseHttp = response as FloconNetworkResponseOnlyDomainModel.Grpc
val response = FloconNetworkCallDomainModel.Grpc.Response(
networkResponse = networkResponse,
responseStatus = responseHttp.grpcStatus,
)
request.copy(
response = response,
)
}
is FloconNetworkCallDomainModel.Http -> {
val responseHttp = response as FloconNetworkResponseOnlyDomainModel.Http
val response = FloconNetworkCallDomainModel.Http.Response(
networkResponse = networkResponse,
httpCode = responseHttp.httpCode,
)
request.copy(
response = response,
)
}
is FloconNetworkCallDomainModel.Response.Failure -> response.response
}
request.copy(
response = response,
)
} catch (t: Throwable) {
t.printStackTrace()
return null
null
}
}

View file

@ -2,81 +2,68 @@ package io.github.openflocon.data.local.network.mapper
import io.github.openflocon.data.local.network.models.FloconNetwockCallEntityLite
import io.github.openflocon.data.local.network.models.FloconNetworkCallType
import io.github.openflocon.data.local.network.models.FloconNetworkRequestLiteEmbedded
import io.github.openflocon.data.local.network.models.FloconNetworkResponseLiteEmbedded
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkRequestDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseDomainModel
import kotlin.Boolean
fun toDomainModel(request: FloconNetworkRequestLiteEmbedded): FloconNetworkRequestDomainModel {
return with(request) {
FloconNetworkRequestDomainModel(
url = this.url,
method = this.method,
startTime = this.startTime,
headers = this.requestHeaders,
body = null, // null on lite
byteSize = this.requestByteSize,
isMocked = this.isMocked,
)
}
}
fun FloconNetwockCallEntityLite.toDomainModel(): FloconNetworkCallDomainModel? {
return try {
val networkRequest = toDomainModel(request)
when (type) {
FloconNetworkCallType.HTTP -> FloconNetworkCallDomainModel.Http(
callId = callId,
networkRequest = networkRequest,
response = response?.let {
FloconNetworkCallDomainModel.Http.Response(
httpCode = response.http!!.responseHttpCode,
networkResponse = toDomainModel(response),
)
}
)
FloconNetworkCallType.GRAPHQL -> FloconNetworkCallDomainModel.GraphQl(
callId = callId,
request = FloconNetworkCallDomainModel.GraphQl.Request(
networkRequest = networkRequest,
query = request.graphql!!.query,
operationType = request.graphql.operationType,
),
response = response?.let {
FloconNetworkCallDomainModel.GraphQl.Response(
httpCode = response.graphql!!.responseHttpCode,
isSuccess = response.graphql.isSuccess,
networkResponse = toDomainModel(response),
)
}
)
FloconNetworkCallType.GRPC -> FloconNetworkCallDomainModel.Grpc(
callId = callId,
networkRequest = networkRequest,
response = response?.let {
FloconNetworkCallDomainModel.Grpc.Response(
networkResponse = toDomainModel(response),
responseStatus = response.grpc!!.responseStatus,
)
}
)
}
FloconNetworkCallDomainModel(
callId = callId,
request = toRequestDomainModel(),
response = response?.toDomainModel(),
)
} catch (t: Throwable) {
t.printStackTrace()
return null
}
}
private fun toDomainModel(response: FloconNetworkResponseLiteEmbedded): FloconNetworkResponseDomainModel = with(response) {
FloconNetworkResponseDomainModel(
contentType = responseContentType,
private fun FloconNetwockCallEntityLite.toRequestDomainModel(): FloconNetworkCallDomainModel.Request =
FloconNetworkCallDomainModel.Request(
url = request.url,
method = request.method,
startTime = request.startTime,
headers = request.requestHeaders,
body = null, // null on lite
headers = responseHeaders,
byteSize = responseByteSize,
durationMs = durationMs,
byteSize = request.requestByteSize,
isMocked = request.isMocked,
specificInfos = when (type) {
FloconNetworkCallType.HTTP -> FloconNetworkCallDomainModel.Request.SpecificInfos.Http
FloconNetworkCallType.GRAPHQL -> FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl(
query = request.graphql!!.query,
operationType = request.graphql.operationType,
)
FloconNetworkCallType.GRPC -> FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc
},
)
private fun FloconNetworkResponseLiteEmbedded.toDomainModel(): FloconNetworkCallDomainModel.Response? {
return if(responseError != null) {
FloconNetworkCallDomainModel.Response.Failure(
durationMs = durationMs,
responseError
)
} else {
FloconNetworkCallDomainModel.Response.Success(
contentType = responseContentType,
body = null, // null on lite
headers = responseHeaders,
byteSize = responseByteSize,
durationMs = durationMs,
specificInfos = when {
graphql != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl(
httpCode = graphql.responseHttpCode,
isSuccess = graphql.isSuccess,
)
grpc != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc(
grpcStatus = grpc.responseStatus,
)
http != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http(
httpCode = http.responseHttpCode,
)
else -> return null
}
)
}
}

View file

@ -2,82 +2,70 @@ package io.github.openflocon.data.local.network.mapper
import io.github.openflocon.data.local.network.models.FloconNetworkCallEntity
import io.github.openflocon.data.local.network.models.FloconNetworkCallType
import io.github.openflocon.data.local.network.models.FloconNetworkRequestEmbedded
import io.github.openflocon.data.local.network.models.FloconNetworkResponseEmbedded
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkRequestDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseDomainModel
private fun toDomainModel(request: FloconNetworkRequestEmbedded): FloconNetworkRequestDomainModel {
return with(request) {
FloconNetworkRequestDomainModel(
url = this.url,
method = this.method,
startTime = this.startTime,
headers = this.requestHeaders,
body = this.requestBody,
byteSize = this.requestByteSize,
isMocked = this.isMocked,
)
}
}
fun FloconNetworkCallEntity.toDomainModel(): FloconNetworkCallDomainModel? {
return try {
val networkRequest = toDomainModel(request)
when (type) {
FloconNetworkCallType.HTTP -> FloconNetworkCallDomainModel.Http(
callId = callId,
networkRequest = networkRequest,
response = response?.let {
FloconNetworkCallDomainModel.Http.Response(
httpCode = response.http!!.responseHttpCode,
networkResponse = toDomainModel(response),
)
}
)
FloconNetworkCallType.GRAPHQL -> FloconNetworkCallDomainModel.GraphQl(
callId = callId,
request = FloconNetworkCallDomainModel.GraphQl.Request(
networkRequest = networkRequest,
query = request.graphql!!.query,
operationType = request.graphql.operationType,
),
response = response?.let {
FloconNetworkCallDomainModel.GraphQl.Response(
httpCode = response.graphql!!.responseHttpCode,
isSuccess = response.graphql.isSuccess,
networkResponse = toDomainModel(response),
)
}
)
FloconNetworkCallType.GRPC -> FloconNetworkCallDomainModel.Grpc(
callId = callId,
networkRequest = networkRequest,
response = response?.let {
FloconNetworkCallDomainModel.Grpc.Response(
networkResponse = toDomainModel(response),
responseStatus = response.grpc!!.responseStatus,
)
}
)
}
FloconNetworkCallDomainModel(
callId = callId,
request = toRequestDomainModel(),
response = response?.toDomainModel(),
)
} catch (t: Throwable) {
t.printStackTrace()
return null
}
}
private fun toDomainModel(response: FloconNetworkResponseEmbedded): FloconNetworkResponseDomainModel =
with(response) {
FloconNetworkResponseDomainModel(
private fun FloconNetworkCallEntity.toRequestDomainModel(): FloconNetworkCallDomainModel.Request =
FloconNetworkCallDomainModel.Request(
url = request.url,
method = request.method,
startTime = request.startTime,
headers = request.requestHeaders,
body = request.requestBody,
byteSize = request.requestByteSize,
isMocked = request.isMocked,
specificInfos = when (type) {
FloconNetworkCallType.HTTP -> FloconNetworkCallDomainModel.Request.SpecificInfos.Http
FloconNetworkCallType.GRAPHQL -> FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl(
query = request.graphql!!.query,
operationType = request.graphql.operationType,
)
FloconNetworkCallType.GRPC -> FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc
},
)
private fun FloconNetworkResponseEmbedded.toDomainModel(): FloconNetworkCallDomainModel.Response? {
return if(responseError != null) {
FloconNetworkCallDomainModel.Response.Failure(
durationMs = durationMs,
issue = responseError
)
} else {
FloconNetworkCallDomainModel.Response.Success(
contentType = responseContentType,
body = responseBody,
headers = responseHeaders,
byteSize = responseByteSize,
durationMs = durationMs,
specificInfos = when {
graphql != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl(
httpCode = graphql.responseHttpCode,
isSuccess = graphql.isSuccess,
)
grpc != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc(
grpcStatus = grpc.responseStatus,
)
http != null -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http(
httpCode = http.responseHttpCode,
)
else -> return null
}
)
}
}

View file

@ -19,61 +19,75 @@ fun FloconNetworkCallDomainModel.toEntity(
callId = callId,
deviceId = deviceId,
packageName = packageName,
type = when (this) {
is FloconNetworkCallDomainModel.Http -> FloconNetworkCallType.HTTP
is FloconNetworkCallDomainModel.GraphQl -> FloconNetworkCallType.GRAPHQL
is FloconNetworkCallDomainModel.Grpc -> FloconNetworkCallType.GRPC
type = when (this.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.Http -> FloconNetworkCallType.HTTP
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> FloconNetworkCallType.GRAPHQL
is FloconNetworkCallDomainModel.Request.SpecificInfos.Grpc -> FloconNetworkCallType.GRPC
},
request = FloconNetworkRequestEmbedded(
url = networkRequest.url,
method = networkRequest.method,
startTime = networkRequest.startTime,
requestHeaders = networkRequest.headers,
requestBody = networkRequest.body,
requestByteSize = networkRequest.byteSize,
isMocked = networkRequest.isMocked,
graphql = when (this) {
is FloconNetworkCallDomainModel.GraphQl -> NetworkCallGraphQlRequestEmbedded(
query = this.request.query,
operationType = this.request.operationType,
url = request.url,
method = request.method,
startTime = request.startTime,
requestHeaders = request.headers,
requestBody = request.body,
requestByteSize = request.byteSize,
isMocked = request.isMocked,
graphql = when (val s = this.request.specificInfos) {
is FloconNetworkCallDomainModel.Request.SpecificInfos.GraphQl -> NetworkCallGraphQlRequestEmbedded(
query = s.query,
operationType = s.operationType,
)
else -> null
}
),
response = networkResponse?.let { networkResponse ->
FloconNetworkResponseEmbedded(
durationMs = networkResponse.durationMs,
responseContentType = networkResponse.contentType,
responseBody = networkResponse.body,
responseHeaders = networkResponse.headers,
responseByteSize = networkResponse.byteSize,
graphql = when (this) {
is FloconNetworkCallDomainModel.GraphQl -> NetworkCallGraphQlResponseEmbedded(
responseHttpCode = (this.response as FloconNetworkCallDomainModel.GraphQl.Response).httpCode,
isSuccess = (this.response as FloconNetworkCallDomainModel.GraphQl.Response).isSuccess,
response = response?.let { networkResponse ->
when(networkResponse) {
is FloconNetworkCallDomainModel.Response.Failure -> {
FloconNetworkResponseEmbedded(
durationMs = networkResponse.durationMs,
responseError = networkResponse.issue,
graphql = null,
http = null,
grpc = null,
responseContentType = null,
responseBody = null,
responseHeaders = emptyMap(),
responseByteSize = 0,
)
is FloconNetworkCallDomainModel.Grpc,
is FloconNetworkCallDomainModel.Http -> null
},
http = when (this) {
is FloconNetworkCallDomainModel.Http -> NetworkCallHttpResponseEmbedded(
responseHttpCode = (this.response as FloconNetworkCallDomainModel.Http.Response).httpCode,
)
is FloconNetworkCallDomainModel.Grpc,
is FloconNetworkCallDomainModel.GraphQl -> null
},
grpc = when (this) {
is FloconNetworkCallDomainModel.Grpc -> NetworkCallGrpcResponseEmbedded(
responseStatus = (this.response as FloconNetworkCallDomainModel.Grpc.Response).responseStatus,
)
is FloconNetworkCallDomainModel.Http,
is FloconNetworkCallDomainModel.GraphQl -> null
}
)
is FloconNetworkCallDomainModel.Response.Success -> {
FloconNetworkResponseEmbedded(
durationMs = networkResponse.durationMs,
responseContentType = networkResponse.contentType,
responseBody = networkResponse.body,
responseHeaders = networkResponse.headers,
responseByteSize = networkResponse.byteSize,
responseError = null,
graphql = when (val s = networkResponse.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> NetworkCallGraphQlResponseEmbedded(
responseHttpCode = s.httpCode,
isSuccess = s.isSuccess,
)
else -> null
},
http = when (val s = networkResponse.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http -> NetworkCallHttpResponseEmbedded(
responseHttpCode = s.httpCode,
)
else -> null
},
grpc = when (val s = networkResponse.specificInfos) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc -> NetworkCallGrpcResponseEmbedded(
responseStatus = s.grpcStatus,
)
else -> null
}
)
}
}
}
)
}

View file

@ -36,6 +36,7 @@ data class FloconNetworkRequestLiteEmbedded(
data class FloconNetworkResponseLiteEmbedded(
val durationMs: Double,
val responseError: String?,
val responseContentType: String?,
//val responseBody: String?,
val responseHeaders: Map<String, String>,

View file

@ -54,6 +54,7 @@ data class FloconNetworkResponseEmbedded(
val responseBody: String?,
val responseHeaders: Map<String, String>,
val responseByteSize: Long,
val responseError: String?,
@Embedded(prefix = "graphql_")
val graphql: NetworkCallGraphQlResponseEmbedded?,

View file

@ -4,6 +4,7 @@ import kotlinx.serialization.json.Json
internal inline fun <reified T> Json.safeDecodeFromString(data: String): T? = try {
decodeFromString(data)
} catch (e: Throwable) {
} catch (t: Throwable) {
t.printStackTrace()
null
}

View file

@ -8,7 +8,6 @@ import io.github.openflocon.data.core.network.graphql.model.GraphQlRequestBody
import io.github.openflocon.data.core.network.graphql.model.GraphQlResponseBody
import io.github.openflocon.domain.network.models.BadQualityConfigDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkRequestDomainModel
import io.github.openflocon.domain.network.models.MockNetworkDomainModel
import kotlinx.serialization.json.Json
import kotlin.uuid.ExperimentalUuidApi
@ -34,7 +33,8 @@ fun toDomain(decoded: FloconNetworkRequestDataModel): FloconNetworkCallDomainMod
val graphQl = extractGraphQl(decoded)
val callId = decoded.floconCallId!!
val networkRequest = FloconNetworkRequestDomainModel(
val request = FloconNetworkCallDomainModel.Request(
url = decoded.url!!,
startTime = decoded.startTime!!,
method = decoded.method!!,
@ -42,33 +42,21 @@ fun toDomain(decoded: FloconNetworkRequestDataModel): FloconNetworkCallDomainMod
body = decoded.requestBody,
byteSize = decoded.requestSize ?: 0L,
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
}
)
when {
graphQl != null -> FloconNetworkCallDomainModel.GraphQl(
callId = callId,
request = FloconNetworkCallDomainModel.GraphQl.Request(
query = graphQl.request.queryName ?: "anonymous",
operationType = graphQl.request.operationType,
networkRequest = networkRequest,
),
response = null,
)
decoded.floconNetworkType == "grpc" -> FloconNetworkCallDomainModel.Grpc(
callId = callId,
networkRequest = networkRequest,
response = null,
)
// decoded.floconNetworkType == "http"
else -> {
FloconNetworkCallDomainModel.Http(
callId = callId,
networkRequest = networkRequest,
response = null,
)
}
}
FloconNetworkCallDomainModel(
callId = callId,
request = request,
response = null, // for now it's null
)
} catch (t: Throwable) {
t.printStackTrace()
null

View file

@ -1,7 +1,7 @@
package com.flocon.data.remote.network.models
import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkCallIdDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseDomainModel
import io.github.openflocon.domain.network.models.FloconNetworkResponseOnlyDomainModel
import kotlinx.serialization.Serializable
@ -37,31 +37,41 @@ data class FloconNetworkResponseDataModel(
val responseHeaders: Map<String, String>? = null,
val responseSize: Long? = null,
val responseGrpcStatus: String? = null,
val responseError: String? = null,
)
internal fun FloconNetworkResponseDataModel.toDomain(): FloconNetworkResponseOnlyDomainModel? {
return try {
val callId = floconCallId!!
val networkResponse = FloconNetworkResponseDomainModel(
durationMs = durationMs ?: 0.0,
body = responseBody,
byteSize = responseSize ?: 0L,
headers = responseHeaders.orEmpty(),
contentType = responseContentType,
)
when (floconNetworkType) {
"grpc" -> FloconNetworkResponseOnlyDomainModel.Grpc(
floconCallId = callId,
networkResponse = networkResponse,
grpcStatus = responseGrpcStatus!!,
val durationMs = durationMs ?: 0.0
val response = if (responseError != null) {
FloconNetworkCallDomainModel.Response.Failure(
durationMs = durationMs,
issue = responseError,
)
// otherwise tread like http
else -> FloconNetworkResponseOnlyDomainModel.Http(
floconCallId = callId,
networkResponse = networkResponse,
httpCode = responseHttpCode!!,
} else {
FloconNetworkCallDomainModel.Response.Success(
durationMs = durationMs,
contentType = responseContentType,
body = responseBody,
headers = responseHeaders.orEmpty(),
byteSize = responseSize ?: 0L,
specificInfos = when (floconNetworkType) {
"grpc" -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc(
grpcStatus = responseGrpcStatus!!,
)
// otherwise tread like http
else -> FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http(
httpCode = responseHttpCode!!,
)
}
)
}
FloconNetworkResponseOnlyDomainModel(
floconCallId = callId,
response = response,
)
} catch (t: Throwable) {
t.printStackTrace()
return null

View file

@ -1,65 +1,89 @@
package io.github.openflocon.domain.network.models
sealed interface FloconNetworkCallDomainModel {
val callId: String
val networkRequest: FloconNetworkRequestDomainModel
val networkResponse: FloconNetworkResponseDomainModel?
data class FloconNetworkCallDomainModel(
val callId: String,
val request: Request,
val response: Response?,
) {
data class Http(
override val callId: String,
override val networkRequest: FloconNetworkRequestDomainModel,
val response: Response?,
) : FloconNetworkCallDomainModel {
data class Response(
val networkResponse: FloconNetworkResponseDomainModel,
val httpCode: Int, // ex: 200
)
override val networkResponse = response?.networkResponse
data class Request(
val url: String,
val startTime: Long,
val method: String,
val headers: Map<String, String>,
val body: String?,
val byteSize: Long,
val isMocked: Boolean,
val specificInfos: SpecificInfos,
) {
sealed interface SpecificInfos {
data object Http: SpecificInfos
data class GraphQl(
val query: String,
val operationType: String,
) : SpecificInfos
data object Grpc : SpecificInfos
}
}
data class GraphQl(
override val callId: String,
val request: Request,
val response: Response?,
) : FloconNetworkCallDomainModel {
sealed interface Response {
data class Request(
val networkRequest: FloconNetworkRequestDomainModel,
val query: String,
val operationType: String,
)
val durationMs: Double
data class Response(
val httpCode: Int, // ex: 200
val isSuccess: Boolean,
val networkResponse: FloconNetworkResponseDomainModel,
)
override val networkRequest = request.networkRequest
override val networkResponse = response?.networkResponse
}
data class Grpc(
override val callId: String,
override val networkRequest: FloconNetworkRequestDomainModel,
val response: Response?,
) : FloconNetworkCallDomainModel {
data class Response(
val networkResponse: FloconNetworkResponseDomainModel,
val responseStatus: String,
)
override val networkResponse = response?.networkResponse
data class Success(
override val durationMs: Double,
val contentType: String? = null,
val body: String? = null,
val headers: Map<String, String>,
val byteSize: Long,
val specificInfos: SpecificInfos,
) : Response {
sealed interface SpecificInfos {
data class Http(
val httpCode: Int, // ex: 200
) : SpecificInfos
data class GraphQl(
val httpCode: Int, // ex: 200
val isSuccess: Boolean,
) : SpecificInfos
data class Grpc(
val grpcStatus: String,
) : SpecificInfos
}
}
data class Failure(
override val durationMs: Double,
val issue: String,
) : Response
}
}
fun FloconNetworkCallDomainModel.Response.Success.SpecificInfos.httpCode() : Int? = when(this) {
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.GraphQl -> httpCode
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Http -> httpCode
is FloconNetworkCallDomainModel.Response.Success.SpecificInfos.Grpc -> null
}
fun FloconNetworkCallDomainModel.httpCode(): Int? {
return when (this) {
is FloconNetworkCallDomainModel.Http -> this.response?.httpCode
is FloconNetworkCallDomainModel.GraphQl -> this.response?.httpCode
else -> null
return when(this.response) {
is FloconNetworkCallDomainModel.Response.Failure -> null
is FloconNetworkCallDomainModel.Response.Success -> this.response.specificInfos.httpCode()
null -> null
}
}
fun FloconNetworkCallDomainModel.byteSize(): Long? {
return when(this.response) {
is FloconNetworkCallDomainModel.Response.Failure -> null
is FloconNetworkCallDomainModel.Response.Success -> this.response.byteSize
null -> null
}
}
fun FloconNetworkCallDomainModel.Response.getContentType(): String? {
return when(this) {
is FloconNetworkCallDomainModel.Response.Failure -> null
is FloconNetworkCallDomainModel.Response.Success -> this.contentType
}
}
@ -67,6 +91,7 @@ data class FloconNetworkCallIdDomainModel(
val floconCallId: String,
)
/*
data class FloconNetworkRequestDomainModel(
val url: String,
val startTime: Long,
@ -76,28 +101,9 @@ data class FloconNetworkRequestDomainModel(
val byteSize: Long,
val isMocked: Boolean,
)
*/
sealed interface FloconNetworkResponseOnlyDomainModel {
val floconCallId: String
val networkResponse: FloconNetworkResponseDomainModel
data class Http(
override val floconCallId: String,
override val networkResponse: FloconNetworkResponseDomainModel,
val httpCode: Int, // ex: 200
) : FloconNetworkResponseOnlyDomainModel
data class Grpc(
override val floconCallId: String,
override val networkResponse: FloconNetworkResponseDomainModel,
val grpcStatus: String,
) : FloconNetworkResponseOnlyDomainModel
}
data class FloconNetworkResponseDomainModel(
val contentType: String? = null,
val body: String? = null,
val headers: Map<String, String>,
val byteSize: Long,
val durationMs: Double
data class FloconNetworkResponseOnlyDomainModel(
val floconCallId: String,
val response: FloconNetworkCallDomainModel.Response,
)

View file

@ -4,21 +4,21 @@ import io.github.openflocon.domain.network.models.FloconNetworkCallDomainModel
class GenerateCurlCommandUseCase {
operator fun invoke(infos: FloconNetworkCallDomainModel): String {
val url = infos.networkRequest.url
val url = infos.request.url
val commandBuilder = StringBuilder("curl")
// 1. Add HTTP Method
commandBuilder.append(" -X ${infos.networkRequest.method}")
commandBuilder.append(" -X ${infos.request.method}")
// 2. Add Request Headers
infos.networkRequest.headers.forEach { (key, value) ->
infos.request.headers.forEach { (key, value) ->
// Escape double quotes within header values if they exist
val escapedValue = value.replace("\"", "\\\"")
commandBuilder.append(" -H \"$key: $escapedValue\"")
}
// 3. Add Request Body (if present)
infos.networkRequest.body?.let { body ->
infos.request.body?.let { body ->
if (body.isNotEmpty()) {
// Escape single quotes within the body for shell compatibility
// ' -> '\'' (closes current single quote, adds escaped single quote, reopens single quote)

View file

@ -23,18 +23,21 @@ class GenerateNetworkMockFromNetworkCallUseCase(
MockNetworkDomainModel(
id = Uuid.random().toString(), // generate
expectation = MockNetworkDomainModel.Expectation(
urlPattern = request.networkRequest.url,
method = request.networkRequest.method,
urlPattern = request.request.url,
method = request.request.method,
),
isEnabled = true, // enabled by default
response = request.networkResponse?.let {
MockNetworkDomainModel.Response(
httpCode = request.httpCode() ?: 200,
body = it.body ?: "",
mediaType = it.headers["Content-Type"] ?: "",
delay = 0,
headers = it.headers,
)
response = request.response?.let {
when(it) {
is FloconNetworkCallDomainModel.Response.Failure -> null // maybe generate error response in this case
is FloconNetworkCallDomainModel.Response.Success -> MockNetworkDomainModel.Response(
httpCode = request.httpCode() ?: 200,
body = it.body ?: "",
mediaType = it.headers["Content-Type"] ?: "",
delay = 0,
headers = it.headers,
)
}
} ?: MockNetworkDomainModel.Response(
httpCode = 200,
body = "",