Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.pluto.plugins.network.intercept
import com.pluto.plugins.network.internal.Status
import com.pluto.plugins.network.internal.interceptor.logic.mapCode2Message
import io.ktor.http.ContentType
import org.json.JSONObject

class NetworkData {

Expand All @@ -11,8 +12,32 @@ class NetworkData {
val method: String,
val body: Body?,
val headers: Map<String, String?>,
val sentTimestamp: Long
val sentTimestamp: Long,
) {
data class GraphqlData(
val queryType: String,
val queryName: String,
val variables: JSONObject,
)

val graphqlData: GraphqlData? = parseGraphqlData()

private fun parseGraphqlData(): GraphqlData? {
if (method != "POST" ||
body == null ||
!body.isJson
) return null
val json = runCatching { JSONObject(body!!.body.toString()) }.getOrNull() ?: return null
val query = json.optString("query") ?: return null
val variables = json.optJSONObject("variables") ?: JSONObject()
val match = graqphlQueryRegex.find(query)?.groupValues ?: return null
return GraphqlData(
queryType = match[1],
queryName = match[2],
variables = variables,
)
}

internal val isGzipped: Boolean
get() = headers["Content-Encoding"].equals("gzip", ignoreCase = true)
}
Expand All @@ -36,17 +61,19 @@ class NetworkData {

data class Body(
val body: CharSequence,
val contentType: String
val contentType: String,
) {
private val contentTypeInternal: ContentType = ContentType.parse(contentType)
private val mediaType: String = contentTypeInternal.contentType
internal val mediaSubtype: String = contentTypeInternal.contentSubtype
internal val isBinary: Boolean = BINARY_MEDIA_TYPES.contains(mediaType)
val sizeInBytes: Long = body.length.toLong()
internal val mediaTypeFull: String = "$mediaType/$mediaSubtype"
val isJson get() = mediaTypeFull == "application/json"
}

companion object {
internal val BINARY_MEDIA_TYPES = listOf("audio", "video", "image", "font")
private val graqphlQueryRegex = Regex("""\b(query|mutation)\s+(\w+)""")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
Expand Down Expand Up @@ -125,8 +126,16 @@ internal class DetailsFragment : Fragment(R.layout.pluto_network___fragment_deta

private val detailsObserver = Observer<DetailContentData> {
setupStatusView(it.api)
binding.method.text = it.api.request.method.uppercase()
binding.url.text = Url(it.api.request.url).toString()
val graphqlData = it.api.request.graphqlData
binding.graphqlIcon.isVisible = graphqlData != null
if (graphqlData != null) {
binding.method.text = "${graphqlData.queryType.uppercase()} ${graphqlData.queryName}"
binding.url.text = graphqlData.variables.toString()
} else {
binding.method.text = it.api.request.method.uppercase()
binding.url.text = Url(it.api.request.url).toString()
}

binding.overview.apply {
visibility = VISIBLE
set(it.api)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ internal class ListFragment : Fragment(R.layout.pluto_network___fragment_list) {
var list = emptyList<ApiCallData>()
viewModel.apiCalls.value?.let {
list = it.filter { api ->
api.request.url.toString().contains(search, true)
api.request.url.contains(search, true) ||
api.request.graphqlData?.queryName?.contains(search, true) ?: false
}
}
binding.noItemText.text = getString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ internal class OverviewStub : ConstraintLayout {
value = context.createSpan { append(semiBold(api.interceptorOption.name)) }
)
)
if (api.request.graphqlData != null) {
add(
KeyValuePairData(
key = context.getString(R.string.pluto_network___method_label),
value = api.request.method
)
)
add(
KeyValuePairData(
key = context.getString(R.string.pluto_network___url_label),
value = api.request.url
)
)
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.pluto.plugins.network.R
import com.pluto.plugins.network.databinding.PlutoNetworkItemNetworkBinding
import com.pluto.plugins.network.intercept.NetworkData.Response
Expand All @@ -30,16 +31,21 @@ internal class ApiItemHolder(parent: ViewGroup, actionListener: DiffAwareAdapter
private val error = binding.error
private val timeElapsed = binding.timeElapsed
private val proxyIndicator = binding.proxyIndicator
private val graphqlIcon = binding.graphqlIcon

override fun onBind(item: ListItem) {
if (item is ApiCallData) {
host.text = Url(item.request.url).host
timeElapsed.text = item.request.sentTimestamp.asTimeElapsed()
binding.root.setBackgroundColor(context.color(R.color.pluto___transparent))

val method = (item.request.graphqlData?.queryType ?: item.request.method).uppercase()
val urlOrQuery = item.request.graphqlData?.queryName ?: Url(item.request.url).encodedPath
graphqlIcon.isVisible = item.request.graphqlData != null

url.setSpan {
append(fontColor(item.request.method.uppercase(), context.color(R.color.pluto___text_dark_60)))
append(" ${Url(item.request.url).encodedPath}")
append(fontColor(method, context.color(R.color.pluto___text_dark_60)))
append(" $urlOrQuery")
}
progress.visibility = VISIBLE
status.visibility = INVISIBLE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="400"
android:viewportHeight="400">
<path
android:pathData="M57.47,302.66l-14.38,-8.3l160.15,-277.38l14.38,8.3z"
android:fillColor="#E535AB"/>
<path
android:pathData="M39.8,272.2h320.3v16.6h-320.3z"
android:fillColor="#E535AB"/>
<path
android:pathData="M206.35,374.03l-160.21,-92.5l8.3,-14.38l160.21,92.5z"
android:fillColor="#E535AB"/>
<path
android:pathData="M345.52,132.95l-160.21,-92.5l8.3,-14.38l160.21,92.5z"
android:fillColor="#E535AB"/>
<path
android:pathData="M54.48,132.88l-8.3,-14.38l160.21,-92.5l8.3,14.38z"
android:fillColor="#E535AB"/>
<path
android:pathData="M342.57,302.66l-160.15,-277.38l14.38,-8.3l160.15,277.38z"
android:fillColor="#E535AB"/>
<path
android:pathData="M52.5,107.5h16.6v185h-16.6z"
android:fillColor="#E535AB"/>
<path
android:pathData="M330.9,107.5h16.6v185h-16.6z"
android:fillColor="#E535AB"/>
<path
android:pathData="M203.52,367l-7.25,-12.56l139.34,-80.45l7.25,12.56z"
android:fillColor="#E535AB"/>
<path
android:pathData="M369.5,297.9c-9.6,16.7 -31,22.4 -47.7,12.8c-16.7,-9.6 -22.4,-31 -12.8,-47.7c9.6,-16.7 31,-22.4 47.7,-12.8C373.5,259.9 379.2,281.2 369.5,297.9"
android:fillColor="#E535AB"/>
<path
android:pathData="M90.9,137c-9.6,16.7 -31,22.4 -47.7,12.8c-16.7,-9.6 -22.4,-31 -12.8,-47.7c9.6,-16.7 31,-22.4 47.7,-12.8C94.8,99 100.5,120.3 90.9,137"
android:fillColor="#E535AB"/>
<path
android:pathData="M30.5,297.9c-9.6,-16.7 -3.9,-38 12.8,-47.7c16.7,-9.6 38,-3.9 47.7,12.8c9.6,16.7 3.9,38 -12.8,47.7C61.4,320.3 40.1,314.6 30.5,297.9"
android:fillColor="#E535AB"/>
<path
android:pathData="M309.1,137c-9.6,-16.7 -3.9,-38 12.8,-47.7c16.7,-9.6 38,-3.9 47.7,12.8c9.6,16.7 3.9,38 -12.8,47.7C340.1,159.4 318.7,153.7 309.1,137"
android:fillColor="#E535AB"/>
<path
android:pathData="M200,395.8c-19.3,0 -34.9,-15.6 -34.9,-34.9c0,-19.3 15.6,-34.9 34.9,-34.9c19.3,0 34.9,15.6 34.9,34.9C234.9,380.1 219.3,395.8 200,395.8"
android:fillColor="#E535AB"/>
<path
android:pathData="M200,74c-19.3,0 -34.9,-15.6 -34.9,-34.9c0,-19.3 15.6,-34.9 34.9,-34.9c19.3,0 34.9,15.6 34.9,34.9C234.9,58.4 219.3,74 200,74"
android:fillColor="#E535AB"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,30 @@
android:layout_height="wrap_content"
android:paddingBottom="@dimen/pluto___margin_medium">

<ImageView
android:id="@+id/graphqlIcon"
android:layout_width="@dimen/pluto___text_small"
android:layout_height="@dimen/pluto___text_small"
android:layout_marginStart="@dimen/pluto___margin_medium"
android:src="@drawable/pluto_network___ic_graphql"
app:layout_constraintBottom_toBottomOf="@id/method"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/method" />

<TextView
android:id="@+id/method"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/pluto___margin_medium"
android:layout_marginStart="@dimen/pluto___margin_mini"
android:layout_marginTop="@dimen/pluto___margin_medium"
android:layout_marginEnd="@dimen/pluto___margin_medium"
android:fontFamily="@font/muli_bold"
android:textColor="@color/pluto___dark"
android:textSize="@dimen/pluto___text_xmedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/graphqlIcon"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginStart="@dimen/pluto___margin_medium"
tools:text="POST" />

<TextView
Expand All @@ -126,7 +140,9 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/pluto___margin_medium"
android:layout_marginTop="@dimen/pluto___margin_micro"
android:ellipsize="end"
android:fontFamily="@font/muli"
android:maxLines="5"
android:textColor="@color/pluto___dark_60"
android:textSize="@dimen/pluto___text_xmedium"
app:layout_constraintTop_toBottomOf="@+id/method"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,17 @@
android:id="@+id/url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/pluto___margin_small"
android:layout_marginStart="@dimen/pluto___margin_mini"
app:layout_goneMarginStart="@dimen/pluto___margin_small"
android:layout_marginTop="@dimen/pluto___margin_medium"
android:layout_marginLeft="@dimen/pluto___margin_small"
android:fontFamily="@font/muli_semibold"
android:textColor="@color/pluto___text_dark"
android:textSize="@dimen/pluto___text_small"
android:layout_marginEnd="@dimen/pluto___margin_mini"
app:layout_constraintStart_toEndOf="@+id/status"
app:layout_constraintStart_toEndOf="@+id/graphqlIcon"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginRight="@dimen/pluto___margin_mini"
app:layout_constraintEnd_toStartOf="@+id/proxyIndicator"
tools:text="api endpoint" />
tools:text="POST /api/v2" />

<ImageView
android:id="@+id/proxyIndicator"
Expand All @@ -74,20 +73,31 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/url" />

<ImageView
android:id="@+id/graphqlIcon"
android:layout_width="@dimen/pluto___text_small"
android:layout_height="@dimen/pluto___text_small"
android:layout_marginStart="@dimen/pluto___margin_small"
android:src="@drawable/pluto_network___ic_graphql"
app:layout_constraintBottom_toBottomOf="@id/url"
app:layout_constraintStart_toEndOf="@id/status"
app:layout_constraintTop_toTopOf="@id/url" />

<TextView
android:id="@+id/host"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/pluto___margin_micro"
android:layout_marginEnd="@dimen/pluto___margin_small"
android:layout_marginBottom="@dimen/pluto___margin_medium"
android:layout_marginStart="@dimen/pluto___margin_small"
android:ellipsize="end"
android:fontFamily="@font/muli"
android:textColor="@color/pluto___text_dark_60"
android:textSize="@dimen/pluto___text_xsmall"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/timeElapsed"
app:layout_constraintStart_toStartOf="@+id/url"
app:layout_constraintStart_toEndOf="@+id/status"
app:layout_constraintTop_toBottomOf="@+id/url"
android:layout_marginRight="@dimen/pluto___margin_small"
tools:text="https host" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class DemoNetworkFragment : Fragment(R.layout.fragment_demo_network) {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.graphqlQuery.setOnClickListener { okhttpViewModel.graphqlQuery() }
binding.graphqlQueryError.setOnClickListener { okhttpViewModel.graphqlQueryError() }
binding.graphqlMutation.setOnClickListener { okhttpViewModel.graphqlMutation() }
binding.graphqlMutationError.setOnClickListener { okhttpViewModel.graphqlMutationError() }
binding.postCall.setOnClickListener { okhttpViewModel.post() }
binding.getCall.setOnClickListener { okhttpViewModel.get() }
binding.getCallKtor.setOnClickListener { ktorViewModel.get() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ interface ApiService {
)
@POST("xml")
suspend fun xml(@Body hashMapOf: RequestBody): Any

// https://studio.apollographql.com/public/SpaceX-pxxbxen/variant/current/home
@POST("https://spacex-production.up.railway.app/")
suspend fun graphql(@Body body: Any): Any
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,58 @@ class OkhttpViewModel : ViewModel() {
}
}

fun graphqlQuery() {
viewModelScope.launch {
enqueue {
apiService.graphql(
mapOf(
GQL_QUERY to "query Launches(\$limit: Int){launches(limit: \$limit){mission_name}}",
GQL_VARIABLES to mapOf("limit" to GQL_LIMIT_VALID),
)
)
}
}
}

fun graphqlQueryError() {
viewModelScope.launch {
enqueue {
apiService.graphql(
mapOf(
GQL_QUERY to "query Launches(\$limit: Int){launches(limit: \$limit){mission_name}}",
GQL_VARIABLES to mapOf("limit" to GQL_LIMIT_INVALID),
)
)
}
}
}

fun graphqlMutation() {
viewModelScope.launch {
enqueue {
apiService.graphql(
mapOf(
GQL_QUERY to "mutation Insert_users(\$objects: [users_insert_input!]!) {insert_users(objects: \$objects) {affected_rows}}",
GQL_VARIABLES to mapOf("objects" to emptyList<Any>()),
)
)
}
}
}

fun graphqlMutationError() {
viewModelScope.launch {
enqueue {
apiService.graphql(
mapOf(
GQL_QUERY to "mutation Insert_users(\$objects: [users_insert_input!]!) {insert_users112231321(objects: \$objects) {affected_rows}}",
GQL_VARIABLES to mapOf("objects" to emptyList<Any>()),
)
)
}
}
}

fun post() {
val label = "POST call"
viewModelScope.launch {
Expand Down Expand Up @@ -79,4 +131,11 @@ class OkhttpViewModel : ViewModel() {
)
}
}

companion object {
private const val GQL_QUERY = "query"
private const val GQL_LIMIT_VALID = 3
private const val GQL_LIMIT_INVALID = -1111
private const val GQL_VARIABLES = "variables"
}
}
Loading
Loading