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(network): introducing Ktor interceptor #272

Merged
merged 5 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ allprojects {
repositories {
google()
mavenCentral()

// for testing Pluto staged repository
maven { url "https://s01.oss.sonatype.org/content/groups/staging/" }
}
Expand Down
1 change: 1 addition & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ ext {
composeCompileVersion = '1.4.7'
composeVersion = '1.4.0'
composeMaterial3Version = '1.0.1'

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.annotation.MenuRes
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuPopupHelper
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.forEach
import com.pluto.plugin.R
import com.pluto.utilities.spannable.createSpan

Expand Down Expand Up @@ -37,11 +38,9 @@ private fun Context.applyFontToMenu(m: Menu) {
}

private fun Context.applyFontToMenuItem(mi: MenuItem) {
val subMenu = mi.subMenu
if (mi.hasSubMenu() && subMenu != null) {
for (i in 0 until subMenu.size()) {
applyFontToMenuItem(subMenu.getItem(i))
}

mi.subMenu?.forEach {
applyFontToMenuItem(it)
}

mi.title?.let { title ->
Expand Down
4 changes: 3 additions & 1 deletion pluto-plugins/bundle/lib-no-op/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ android {

dependencies {
api project(path: ':pluto-plugins:plugins:exceptions:lib-no-op')
api project(path: ':pluto-plugins:plugins:network:lib-no-op')
api project(path: ':pluto-plugins:plugins:network:core:lib-no-op')
api project(path: ':pluto-plugins:plugins:network:interceptor-okhttp:lib-no-op')
api project(path: ':pluto-plugins:plugins:network:interceptor-ktor:lib-no-op')
api project(path: ':pluto-plugins:plugins:shared-preferences:lib-no-op')
api project(path: ':pluto-plugins:plugins:logger:lib-no-op')
api project(path: ':pluto-plugins:plugins:datastore:lib-no-op')
Expand Down
4 changes: 3 additions & 1 deletion pluto-plugins/bundle/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ android {

dependencies {
api project(path: ':pluto-plugins:plugins:exceptions:lib')
api project(path: ':pluto-plugins:plugins:network:lib')
api project(path: ':pluto-plugins:plugins:network:core:lib')
api project(path: ':pluto-plugins:plugins:network:interceptor-ktor:lib')
api project(path: ':pluto-plugins:plugins:network:interceptor-okhttp:lib')
api project(path: ':pluto-plugins:plugins:shared-preferences:lib')
api project(path: ':pluto-plugins:plugins:logger:lib')
api project(path: ':pluto-plugins:plugins:datastore:lib')
Expand Down
51 changes: 51 additions & 0 deletions pluto-plugins/plugins/network/core/lib-no-op/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
}

apply from: "$rootDir/scripts/build/utils.gradle"
apply from: "$rootDir/scripts/publish/module.gradle"

def verCode, verName, verBuild, verNameShort, verPublish
(verCode, verName, verBuild, verNameShort, verPublish) = genVersion()

ext {
PUBLISH_GROUP_ID = "com.plutolib.plugins"
PUBLISH_VERSION = verPublish
PUBLISH_ARTIFACT_ID = 'network-no-op'
}

android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion

defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
}

buildTypes {
release {
debuggable true
minifyEnabled false
shrinkResources false
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
namespace 'com.pluto.plugins.network'
lint {
abortOnError false
}

}

dependencies {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.pluto.plugins.network

data class RequestData(
val url: String,
val method: String,
val body: ProcessedBody?,
val headers: Map<String, String?>,
val sentTimestamp: Long
)

data class ResponseData(
val statusCode: Int,
val body: ProcessedBody?,
val headers: Map<String, String?>,
val sentTimestamp: Long,
val receiveTimestamp: Long,
val protocol: String = "",
val fromDiskCache: Boolean = false
)

data class ProcessedBody(
val body: CharSequence,
val contentType: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.pluto.plugins.network

import java.io.IOException

@SuppressWarnings("EmptyFunctionBlock", "UnusedPrivateMember")
class NetworkRecorder(private val request: RequestData) {

fun onError(e: IOException) {
}

fun onResponse(response: ResponseData) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.pluto.plugins.network

import android.content.Context

@SuppressWarnings("UnusedPrivateMember", "EmptyFunctionBlock")
object PlutoNetwork {

internal fun initialize(context: Context) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ android {
viewBinding true
}


defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion

buildConfigField "String", "VERSION_NAME", "\"${verPublish}\""
}

Expand Down Expand Up @@ -62,11 +60,10 @@ dependencies {
implementation project(path: ':pluto-plugins:base:lib')
implementation "androidx.core:core-ktx:$androidXCoreVersion"

implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation 'com.squareup.okio:okio:2.10.0'
implementation "io.ktor:ktor-client-core-jvm:2.3.2"

implementation "androidx.room:room-ktx:$roomsVersion"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
ksp "androidx.room:room-compiler:$roomsVersion"

implementation "com.squareup.moshi:moshi:$moshiVersion"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.pluto.plugins.network

import android.content.Context

object PlutoNetwork {

var applicationContext: Context? = null
private set

internal fun initialize(context: Context) {
applicationContext = context.applicationContext
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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

object NetworkData {

data class Request(
val url: String,
val method: String,
val body: Body?,
val headers: Map<String, String?>,
val sentTimestamp: Long
) {
internal val isGzipped: Boolean
get() = headers["Content-Encoding"].equals("gzip", ignoreCase = true)
}

data class Response(
val statusCode: Int,
val body: Body?,
val headers: Map<String, String?>,
val sentTimestamp: Long,
val receiveTimestamp: Long,
val protocol: String = "",
val fromDiskCache: Boolean = false
) {
internal val status: Status
get() = Status(statusCode, mapCode2Message(statusCode))
val isSuccessful: Boolean
get() = statusCode in 200..299
internal val isGzipped: Boolean
get() = headers["Content-Encoding"].equals("gzip", ignoreCase = true)
}

data class Body(
val body: CharSequence,
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"
}

internal val BINARY_MEDIA_TYPES = listOf("audio", "video", "image", "font")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.pluto.plugins.network.intercept

import com.pluto.plugins.network.internal.ApiCallData
import com.pluto.plugins.network.internal.MockConfig
import com.pluto.plugins.network.internal.interceptor.logic.NetworkCallsRepo
import com.pluto.plugins.network.internal.interceptor.logic.asExceptionData
import com.pluto.plugins.network.internal.mock.logic.MockSettingsRepo
import com.pluto.utilities.DebugLog
import java.io.IOException
import java.util.UUID

class NetworkInterceptor private constructor(private val request: NetworkData.Request, val option: Option) {
private val getRequestId: String = UUID.nameUUIDFromBytes("${System.currentTimeMillis()}::${request.url}".toByteArray()).toString()
private val apiCallData = ApiCallData(id = getRequestId, interceptorOption = option, request = request)
val requestUrlWithMockInfo: String = MockSettingsRepo.get(request.url, request.method)?.let {
apiCallData.mock = MockConfig(it)
NetworkCallsRepo.set(apiCallData)
it
} ?: run {
request.url
}

companion object {
fun intercept(request: NetworkData.Request, option: Option = Option()): NetworkInterceptor {
return NetworkInterceptor(request, option)
}
}

init {
NetworkCallsRepo.set(apiCallData)
}

fun onError(e: IOException) {
DebugLog.e("pluto.network", "error occurred", e)
apiCallData.exception = e.asExceptionData()
NetworkCallsRepo.set(apiCallData)
}

fun onResponse(response: NetworkData.Response) {
apiCallData.response = response
NetworkCallsRepo.set(apiCallData)
}

data class Option(
val name: String = "Custom",
val metadata: HashMap<String, String>? = null
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.pluto.plugins.network.internal

import com.pluto.plugins.network.intercept.NetworkData
import com.pluto.plugins.network.intercept.NetworkInterceptor
import com.pluto.plugins.network.internal.interceptor.logic.ExceptionData
import com.pluto.plugins.network.internal.share.getCurl
import com.pluto.utilities.list.ListItem

internal data class MockConfig(
val url: String,
)

internal data class Status(
val code: Int,
val message: String,
)

internal class ApiCallData(
val id: String,
val interceptorOption: NetworkInterceptor.Option,
val request: NetworkData.Request,
var response: NetworkData.Response? = null,
var exception: ExceptionData? = null,
var mock: MockConfig? = null,
val isCustomTrace: Boolean = false
) : ListItem() {
val curl: String
get() = request.getCurl()
val responseTime
get() = exception?.timeStamp ?: response?.receiveTimestamp

override fun isEqual(other: Any): Boolean {
if (other is ApiCallData) {
id == other.id && response == other.response && exception == other.exception
}
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package com.pluto.plugins.network.internal
import androidx.fragment.app.Fragment
import com.pluto.plugins.network.R

class NetworkFragment : Fragment(R.layout.pluto_network___fragment_network)
internal class NetworkFragment : Fragment(R.layout.pluto_network___fragment_network)
Loading
Loading