Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
d64107d
Create a new module for the Api Faker
hichamboushaba Mar 6, 2023
61c9e55
Add Room DB for holding the endpoints
hichamboushaba Mar 6, 2023
571b1b4
Add initial logic for intercepting and faking responses
hichamboushaba Mar 6, 2023
bea598f
Some changes to the DB schema
hichamboushaba Mar 7, 2023
e3c4cbb
Handle extracting data from the request for matching
hichamboushaba Mar 7, 2023
dccd118
Add some logging, and handle errors in parsing
hichamboushaba Mar 7, 2023
cbf04b6
Add some improvements to the DB schema and dao
hichamboushaba Mar 8, 2023
a9ad0ca
Rename classes
hichamboushaba Dec 3, 2024
9864ff6
Fix detekt issues
hichamboushaba Dec 3, 2024
7586064
Run unit tests of all libraries in CI
hichamboushaba Dec 3, 2024
97f8325
Disable schema export
hichamboushaba Mar 10, 2023
566bb53
Enable destructive migration
hichamboushaba Mar 9, 2023
cd04882
Improve the query statement
hichamboushaba Mar 8, 2023
e637209
Add ability to delete requests
hichamboushaba Mar 8, 2023
1eb669e
Add HttpMethod to the DB entities
hichamboushaba Mar 9, 2023
9a5c208
Fix endpoint extraction from Jetpack tunnel endpoints
hichamboushaba Mar 9, 2023
124ecf7
Fix detekt issue
hichamboushaba Dec 4, 2024
4982268
Improve logic of handling Jetpack tunnel endpoints
hichamboushaba Dec 4, 2024
73ba162
Improve the logic of extracting the http method for Jetpack tunneled …
hichamboushaba Dec 5, 2024
3d1c8c9
Mark ApiFaker log tag as internal
hichamboushaba Dec 5, 2024
c8ca8a3
Add an initial screen to list the different mocked endpoints
hichamboushaba Mar 8, 2023
4efe69b
Use hilt viewmodel
hichamboushaba Mar 7, 2023
c8d9067
Add the faker library to the app
hichamboushaba Dec 3, 2024
bd0e182
Add an entry to developer options and handle navigation to the API faker
hichamboushaba Dec 3, 2024
f68afa2
Use an app's Fragment to allow theming the UI properly
hichamboushaba Mar 7, 2023
8c8af90
Add a way to enable/disable the API Faker
hichamboushaba Mar 8, 2023
4d1b579
Minor improvements to the home screen's UI
hichamboushaba Mar 8, 2023
4ebb937
Improve navigation
hichamboushaba Mar 9, 2023
29f734d
Match the TopAppBar background color to the app theme
hichamboushaba Dec 4, 2024
82c3c21
Fix detekt issue
hichamboushaba Dec 4, 2024
c2b1f84
Persist the ApiFaker status
hichamboushaba Mar 9, 2023
9dd1b5d
Start config flow eagerly
hichamboushaba Dec 4, 2024
9b2d033
Fix issue with updating the faker config
hichamboushaba Dec 4, 2024
a25dfe5
Remove explicit argument type
hichamboushaba Dec 5, 2024
f56d3ac
Start working on the details screen
hichamboushaba Mar 7, 2023
bedf849
Handle remaining fields
hichamboushaba Mar 8, 2023
7134c74
Handle saving changes
hichamboushaba Mar 8, 2023
b02ce89
Fixes to saving and navigation
hichamboushaba Mar 8, 2023
72899dc
Rename classes
hichamboushaba Mar 8, 2023
7387eec
Improve the query statement
hichamboushaba Mar 8, 2023
e4ff1be
Minor improvements
hichamboushaba Mar 8, 2023
978c398
Consume the status configuration
hichamboushaba Mar 8, 2023
2d23d51
Add ability to delete requests
hichamboushaba Mar 8, 2023
dacfd29
Add support for filtering by http method
hichamboushaba Mar 9, 2023
d27d000
Improve navigation
hichamboushaba Mar 9, 2023
ea138c3
UI improvements
hichamboushaba Mar 9, 2023
d4d4775
Apply internal visibility to some declarations
hichamboushaba Mar 9, 2023
4f15cd4
Add an artifical delay
hichamboushaba Mar 10, 2023
52e10d2
Suppress deprecation temporarily
hichamboushaba Dec 4, 2024
fe1c676
Fix detekt issues
hichamboushaba Dec 4, 2024
876c3d9
Add some padding between the text fields and their captions
hichamboushaba Dec 5, 2024
33dbf0f
Display a hint when ApiFaker is enabled
hichamboushaba Mar 9, 2023
6c5bfe9
Add ability to disable the ApiFaker from the UI hint
hichamboushaba Mar 9, 2023
7534268
Set text color as white in the hint
hichamboushaba Dec 4, 2024
1412386
Fix detekt issues
hichamboushaba Dec 4, 2024
43b60e2
Add query parameters to the DB model
hichamboushaba Mar 9, 2023
c58a989
Filter requests using query parameters
hichamboushaba Mar 9, 2023
ddd80ea
Bump db version
hichamboushaba Dec 4, 2024
f2c6ad6
Add a way to add query parameters in the UI
hichamboushaba Mar 9, 2023
67ffb03
Some fixes
hichamboushaba Mar 9, 2023
a27be3a
Handle query parameters extraction for Jetpack tunnel requests
hichamboushaba Dec 4, 2024
c41e820
Fix detekt issues
hichamboushaba Dec 4, 2024
f090305
Log a warning message if multiple endpoints are matched
hichamboushaba Mar 10, 2023
a85c17c
Add more tests
hichamboushaba Dec 5, 2024
9375f2d
Make DB flow collection lifecycle aware
hichamboushaba Dec 10, 2024
d4b929e
Fix UI with long endpoint paths
hichamboushaba Dec 11, 2024
ad5f00f
Improve logic of warning log
hichamboushaba Dec 17, 2024
60ba59a
Merge pull request #13083 from woocommerce/task/api-faker-5
hichamboushaba Dec 17, 2024
e02f25c
Merge pull request #13076 from woocommerce/task/api-faker-4
hichamboushaba Dec 17, 2024
d209625
Merge pull request #13070 from woocommerce/task/api-faker-3
hichamboushaba Dec 17, 2024
933fbbb
Merge pull request #13064 from woocommerce/task/api-faker-2
hichamboushaba Dec 17, 2024
b29b2e6
Merge branch 'trunk' into task/api-faker-1
hichamboushaba Dec 17, 2024
1f7edf5
Add Compose icons dependency to the apiFaker lib
hichamboushaba Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .buildkite/commands/run-unit-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

echo "--- 🧪 Testing"
set +e
./gradlew testJalapenoDebugUnitTest lib:cardreader:testDebugUnitTest lib:iap:testDebugUnitTest
./gradlew testJalapenoDebugUnitTest testDebugUnitTest
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows running unit tests for all modules instead of having to list all of them

TESTS_EXIT_STATUS=$?
set -e

Expand Down
1 change: 1 addition & 0 deletions WooCommerce/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ dependencies {
implementation(libs.google.play.services.wearable)

// Debug dependencies
debugImplementation project(":libs:apifaker")
debugImplementation(libs.facebook.flipper.main)
debugImplementation(libs.facebook.soloader)
debugImplementation(libs.facebook.flipper.network.plugin) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin
import com.facebook.soloader.SoLoader
import com.woocommerce.android.apifaker.ApiFakerUiHelper
import com.woocommerce.android.util.SystemVersionUtils
import com.woocommerce.android.util.WooLog
import com.woocommerce.android.util.WooLog.T
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

@HiltAndroidApp
class WooCommerceDebug : WooCommerce() {
@Inject
lateinit var apiFakerUiHelper: ApiFakerUiHelper

override fun onCreate() {
if (FlipperUtils.shouldEnableFlipper(this)) {
SoLoader.init(this, false)
Expand All @@ -33,6 +38,7 @@ class WooCommerceDebug : WooCommerce() {
enableWebContentDebugging()
super.onCreate()
enableStrictMode()
apiFakerUiHelper.attachToApplication(this)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.woocommerce.android.apifaker

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.navigation.findNavController
import com.woocommerce.android.apifaker.ui.ApiFakerNavHost
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
import com.woocommerce.android.ui.main.AppBarStatus

class ApiFakerHostFragment : BaseFragment() {
override val activityAppBarStatus: AppBarStatus
get() = AppBarStatus.Hidden

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)

setContent {
WooThemeWithBackground {
ApiFakerNavHost(
onExit = { findNavController().navigateUp() }
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.woocommerce.android.R
import com.woocommerce.android.ui.base.BaseFragment
Expand Down Expand Up @@ -46,6 +47,12 @@ class DeveloperOptionsFragment : BaseFragment() {
selectedValue = event.selectedValue
)
}

is DeveloperOptionsViewModel.DeveloperOptionsEvents.OpenApiFaker -> {
findNavController().navigate(
DeveloperOptionsFragmentDirections.actionDeveloperOptionsFragmentToApiFaker()
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.ScopedViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand Down Expand Up @@ -77,11 +78,24 @@ class DeveloperOptionsViewModel @Inject constructor(
)
}

private val apiFakerFlow = flowOf(
NonToggleableListItem(
icon = R.drawable.ic_globe,
iconTint = R.color.color_primary,
label = UiString.UiStringText("API Faker"),
isEnabled = true,
onClick = {
triggerEvent(DeveloperOptionsEvents.OpenApiFaker)
}
)
)

val viewState = combine(
simulatedCardReaderFlow,
readerUpdateFrequencyFlow,
interacPaymentEnabledFlow,
savedPrivacySettingsOnDialogFlow
savedPrivacySettingsOnDialogFlow,
apiFakerFlow
) { items ->
DeveloperOptionsViewState(
rows = items.filterNotNull()
Expand Down Expand Up @@ -123,6 +137,8 @@ class DeveloperOptionsViewModel @Inject constructor(
val options: List<UpdateFrequencyUiModel>,
var selectedValue: UpdateFrequencyUiModel,
) : DeveloperOptionsEvents()

data object OpenApiFaker : DeveloperOptionsEvents()
}

data class DeveloperOptionsViewState(
Expand Down
11 changes: 10 additions & 1 deletion WooCommerce/src/main/res/navigation/nav_graph_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<include app:graph="@navigation/nav_graph_jetpack_install" />
<include app:graph="@navigation/nav_graph_domain_change" />
<include app:graph="@navigation/nav_graph_themes" />

<fragment
android:id="@+id/mainSettingsFragment"
android:name="com.woocommerce.android.ui.prefs.MainSettingsFragment"
Expand Down Expand Up @@ -169,7 +170,11 @@
<fragment
android:id="@+id/developerOptionsFragment"
android:name="com.woocommerce.android.ui.prefs.developer.DeveloperOptionsFragment"
android:label="DeveloperOptionsFragment" />
android:label="DeveloperOptionsFragment">
<action
android:id="@+id/action_developerOptionsFragment_to_ApiFaker"
app:destination="@id/apiFakerHostFragment" />
</fragment>
<fragment
android:id="@+id/accountSettingsFragment"
android:name="com.woocommerce.android.ui.prefs.account.AccountSettingsFragment"
Expand Down Expand Up @@ -199,4 +204,8 @@
android:id="@+id/pluginsFragment"
android:name="com.woocommerce.android.ui.prefs.plugins.PluginsFragment"
android:label="PluginsFragment" />
<fragment
android:id="@+id/apiFakerHostFragment"
android:name="com.woocommerce.android.apifaker.ApiFakerHostFragment"
android:label="ApiFakerHostFragment" />
</navigation>
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ androidx-navigation = '2.7.7'
androidx-preference = '1.2.1'
androidx-recyclerview-main = '1.3.2'
androidx-recyclerview-selection = '1.1.0'
androidx-room = "2.6.1"
androidx-test-espresso = '3.4.0'
androidx-test-ext = '1.1.5'
androidx-test-main = '1.4.0'
Expand Down Expand Up @@ -130,6 +131,7 @@ androidx-hilt-navigation-fragment = { group = "androidx.hilt", name = "hilt-navi
androidx-hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "androidx-hilt" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-savedstate = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "androidx-lifecycle" }
androidx-navigation-common = { group = "androidx.navigation", name = "navigation-common", version.ref = "androidx-navigation" }
Expand All @@ -141,6 +143,9 @@ androidx-preference-main = { group = "androidx.preference", name = "preference",
androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "androidx-preference" }
androidx-recyclerview-main = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx-recyclerview-main" }
androidx-recyclerview-selection = { group = "androidx.recyclerview", name = "recyclerview-selection", version.ref = "androidx-recyclerview-selection" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidx-room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx-room" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidx-room" }
androidx-test-espresso-contrib = { group = "androidx.test.espresso", name = "espresso-contrib", version.ref = "androidx-test-espresso" }
androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-test-espresso" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext" }
Expand Down
81 changes: 81 additions & 0 deletions libs/apifaker/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.google.dagger.hilt)
alias(libs.plugins.ksp)
}

repositories {
maven {
url 'https://a8c-libs.s3.amazonaws.com/android'
content {
includeGroup "org.wordpress"
includeGroup "org.wordpress.fluxc"
includeGroup "org.wordpress.fluxc.plugins"
includeGroup "org.wordpress.wellsql"
includeGroup "org.wordpress.mediapicker"
includeGroup "com.automattic"
includeGroup "com.automattic.tracks"
Comment on lines +13 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need all these? I see you only add FluxC as dependency (which also makes sense since it's where all the requests happen). But why include tracks, mediapicker, wellsql, wordpress ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this either, but I tried to just keep the same setup as what we have in the other modules, personally I think this code should be in the root build.gradle file, or better in a convention plugin, but since this is not the setup we have yet, I prefer to keep the same setup as the other modules here, as the includeGroup statements won't have any impact here, they are just used for defining the repo itself.

}
}

maven {
url "https://a8c-libs.s3.amazonaws.com/android/jcenter-mirror"
content {
includeVersion "com.android.volley", "volley", "1.1.1"
}
}
}

android {
namespace 'com.woocommerce.android.apifaker'

defaultConfig {
minSdkVersion gradle.ext.minSdkVersion
targetSdk gradle.ext.targetSdkVersion
compileSdk gradle.ext.compileSdkVersion

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
compose true
}

compileOptions {
sourceCompatibility libs.versions.java.get()
targetCompatibility libs.versions.java.get()
}
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.lifecycle.runtime.compose)

implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material.main)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material.icons.extended)
debugImplementation(libs.androidx.compose.ui.tooling.main)

implementation("${gradle.ext.fluxCBinaryPath}:${libs.versions.wordpress.fluxc.get()}") {
exclude group: "com.android.support"
exclude group: "org.wordpress", module: "utils"
}

implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)

implementation(libs.google.dagger.hilt.android.main)
implementation(libs.androidx.hilt.navigation.compose)
ksp(libs.google.dagger.hilt.compiler)

testImplementation(libs.junit)
testImplementation(libs.mockito.kotlin)
testImplementation(libs.mockito.inline)
}
4 changes: 4 additions & 0 deletions libs/apifaker/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.woocommerce.android.apifaker

import android.content.Context
import android.content.SharedPreferences
import com.woocommerce.android.apifaker.db.EndpointDao
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton

private const val PREF_FILE_NAME = "api_faker"
private const val PREFERENCE_KEY = "api_faker_enabled"

@Singleton
internal class ApiFakerConfig @Inject constructor(
context: Context,
endpointDao: EndpointDao
) {
private val configScope = CoroutineScope(Dispatchers.Main)
private val preferences = context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE)

private val prefFlow = preferences.prefFlow(PREFERENCE_KEY, false)

val enabled = combine(
prefFlow,
endpointDao.observeEndpointsCount().map { it == 0 }
) { pref, isEmpty ->
pref && !isEmpty
}.stateIn(configScope, SharingStarted.Eagerly, false)

fun setStatus(enabled: Boolean) {
preferences.edit().putBoolean(PREFERENCE_KEY, enabled).apply()
}

private fun SharedPreferences.prefFlow(key: String, defaultValue: Boolean) = callbackFlow {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, listenerKey ->
if (listenerKey == key) {
trySend(getBoolean(key, defaultValue))
}
}
registerOnSharedPreferenceChangeListener(listener)
trySend(getBoolean(key, defaultValue))
awaitClose { unregisterOnSharedPreferenceChangeListener(listener) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.woocommerce.android.apifaker

import android.util.Log
import okhttp3.Interceptor
import okhttp3.Interceptor.Chain
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.internal.EMPTY_RESPONSE
import javax.inject.Inject

private const val ARTIFICIAL_DELAY_MS = 500L

internal class ApiFakerInterceptor @Inject constructor(
private val apiFakerConfig: ApiFakerConfig,
private val endpointProcessor: EndpointProcessor
) : Interceptor {
@Suppress("TooGenericExceptionCaught", "SwallowedException")
override fun intercept(chain: Chain): Response {
if (!apiFakerConfig.enabled.value) {
return chain.proceed(chain.request())
}

Log.d(LOG_TAG, "Intercepting request: ${chain.request().url}")
val request = chain.request()
val fakeResponse = try {
endpointProcessor.fakeRequestIfNeeded(request)
} catch (e: Exception) {
Log.w(LOG_TAG, "Faking request: ${chain.request()} failed, ignoring")
null
}

return if (fakeResponse != null) {
Log.d(LOG_TAG, "Matched request: ${chain.request().url}:\nSending Mocked Response: $fakeResponse")
Thread.sleep(ARTIFICIAL_DELAY_MS)
Response.Builder()
.request(request)
.protocol(Protocol.HTTP_1_1)
.message("Fake Response")
.code(fakeResponse.statusCode)
// TODO check if it's safe to always use JSON as the content type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the body of the faked request is anything the user inputs from the app, wouldn't it make more sense to leave MediaType null? For example sometimes we get and html body for API errors. Haven't tested yet, but what happens if we keep this application/json content type and enter non json body?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point; but for now I'll keep it like this, my rationale for hardcording application/json here is that all of the API requests we have in the app use JSON responses, the HTML errors are an exception that can be caused by a specific site setup that returns an error before the site handles the error.

what happens if we keep this application/json content type and enter non json body?

It should work as you expect from the app, the upper layer will try to deserialize the content, and would fail, then return a JSON error, this is what happens when we receive HTML instead of JSON (for example this case: p1723565504566789/1720009283.216679-slack-C6H8C3G23 API org.json.JSONException: Value <!DOCTYPE of type java.lang.String cannot be converted to JSONObject)

.body(
fakeResponse.body?.toResponseBody("application/json".toMediaType())
?: EMPTY_RESPONSE
)
.addHeader("content-type", "application/json")
.build()
} else {
chain.proceed(request)
}
}
}
Loading
Loading