diff --git a/README.md b/README.md index b375faa..8ee4c31 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,8 @@ Network transactions from previous sessions have a background in light gray colo 3. [Data retention policy](docs/DATA_RETENTION_POLICY.md) 4. [Shortcut for mobile clients](docs/SHORTCUT_FOR_MOBILE_CLIENTS.md) 5. [Excluding Inspektify from Release Builds](docs/EXCLUDING_INSPEKTIFY_FROM_RELEASE_BUILDS.md) -5. [Redact data from Inspektify](docs/EXCLUDING_INSPEKTIFY_FROM_RELEASE_BUILDS.md) +6. [Redact data from Inspektify](docs/EXCLUDING_INSPEKTIFY_FROM_RELEASE_BUILDS.md) +7. [Ignore endpoints configuration](docs/IGNORE_ENDPOINTS_CONFIGURATION.md) ## Sample project @@ -137,4 +138,7 @@ the sample is configured to use Ktor from the 3.x.x family, but if you want to t Medium - \ No newline at end of file + + +## Sponsor my work +Buy Me A Coffee \ No newline at end of file diff --git a/docs/IGNORE_ENDPOINTS_CONFIGURATION.md b/docs/IGNORE_ENDPOINTS_CONFIGURATION.md new file mode 100644 index 0000000..096a10c --- /dev/null +++ b/docs/IGNORE_ENDPOINTS_CONFIGURATION.md @@ -0,0 +1,44 @@ +# Ignore endpoints configuration + +You can exclude specific endpoints from interception and logging by configuring +the `ignoreEndpoints` property in `InspektifyKtorConfig`. This is a list of `IgnorePathData` +objects, where each object specifies the HTTP method, the endpoint, and the matching strategy. + +``` +install(InspektifyKtor) { + ignoreEndpoints = listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/health-check") + ), + IgnorePathData( + method = MethodType.POST, + matchingStrategy = EndpointMatchingStrategy.Contains("/login") + ), + IgnorePathData( + method = MethodType.ALL, + matchingStrategy = EndpointMatchingStrategy.Regex("https://reqres\\.in/.*"), + ) + ) +} +``` + +## IgnorePathData Fields + +- `method`: Defines the HTTP method to match, using MethodType (e.g., GET, POST). +- `endpointMatchingStrategy`: Defines in which way matching should happen when ignoring different + endpoints + +### MethodType property + +Supported HTTP methods: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`. + +There is a special case added to ignore all of HTTP methods if someone would like to ignore all +traffic coming in from specific endpoint. If you want to do that you can use `ALL` as methodType and +all traffic will be ignored that will match defined strategy. + +### EndpointMatchingStrategy property + +`Exact`: Exact URL matching. +`Contains`: Substring matching within the URL. +`Regex`: Matching URL against regular expression. \ No newline at end of file diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/InspektifyKtorConfig.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/InspektifyKtorConfig.kt index 93e5290..5baae23 100644 --- a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/InspektifyKtorConfig.kt +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/InspektifyKtorConfig.kt @@ -7,6 +7,7 @@ public class InspektifyKtorConfig { public var dataRetentionPolicy: DataRetentionPolicy = DataRetentionPolicy.DayDuration(14) public var redactHeaders: List = emptyList() public var redactBodyProperties: List = emptyList() + public var ignoreEndpoints: List = emptyList() } public sealed interface LogLevel { @@ -31,5 +32,26 @@ public sealed interface DataRetentionPolicy { public data class SessionCount(val numOfSessions: Int) : DataRetentionPolicy } +public data class IgnorePathData(val method: MethodType, val matchingStrategy: EndpointMatchingStrategy) + +public enum class MethodType(internal val value: String) { + GET("GET"), + POST("POST"), + PUT("PUT"), + DELETE("DELETE"), + PATCH("PATCH"), + HEAD("HEAD"), + OPTIONS("OPTIONS"), + ALL("*"); + + internal fun isAll(): Boolean = this == ALL +} + +public sealed interface EndpointMatchingStrategy { + public data class Exact(val value: String) : EndpointMatchingStrategy + public data class Contains(val value: String) : EndpointMatchingStrategy + public data class Regex(val value: String) : EndpointMatchingStrategy +} + internal const val INSPEKTIFY_SHORTCUT_ITEM_SHORT_NAME = "Inspektify" internal const val INSPEKTIFY_SHORTCUT_ITEM_LONG_NAME = "Open $INSPEKTIFY_SHORTCUT_ITEM_SHORT_NAME window" diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorClient.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorClient.kt index 5ea0b17..19d3afa 100644 --- a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorClient.kt +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorClient.kt @@ -25,9 +25,11 @@ internal class InspektifyKtorClient( private val requestHandler: InspektifyRequestHandler = AppComponents.getInspektifyRequestHandler(), private val responseHandler: InspektifyResponseHandler = AppComponents.getInspektifyResponseHandler(), private val trafficLogger: InspektifyNetworkTrafficLogger = AppComponents.getInspektifyNetworkTrafficLogger(), - private val dataRetentionHandler: InspektifyDataRetentionHandler = AppComponents.getInspektifyDataRetentionHandler() + private val dataRetentionHandler: InspektifyDataRetentionHandler = + AppComponents.getInspektifyDataRetentionHandler(), + private val ignoreEndpointHandler: InspektifyKtorIgnoreEndpointHandler = + AppComponents.getInspektifyKtorIgnoreEndpointHandler() ) { - private val coroutineScope = CoroutineScope(dispatcherProvider.main + SupervisorJob()) private var sessionId: Long? = null @@ -47,6 +49,7 @@ internal class InspektifyKtorClient( redactHeaders = config.redactHeaders redactBodyProperties = config.redactBodyProperties coroutineScope.launch(dispatcherProvider.main.immediate) { + ignoreEndpointHandler.configureEndpointIgnoring(config.ignoreEndpoints) configurePresentation(config.autoDetectEnabled, config.shortcutEnabled) dataRetentionHandler.configureDataRetentionPolicy(config.dataRetentionPolicy) } @@ -59,6 +62,8 @@ internal class InspektifyKtorClient( client.sendPipeline.intercept(HttpSendPipeline.Monitoring) { try { + if (ignoreEndpointHandler.shouldIgnoreEndpoint(context)) return@intercept + val networkTraffic = requestHandler.handleRequest( request = context, sessionId = sessionId, @@ -82,15 +87,17 @@ internal class InspektifyKtorClient( val networkTraffic = repository.getNetworkTrafficData( response.request.attributes[requestHandler.getNetworkTrafficIdKey()] ) - val networkTrafficWithResponse = responseHandler.handleResponse( - response = response, - networkTraffic = networkTraffic, - redactHeaders = redactHeaders, - redactBodyProperties = redactBodyProperties - ) + if (networkTraffic != null) { + val networkTrafficWithResponse = responseHandler.handleResponse( + response = response, + networkTraffic = networkTraffic, + redactHeaders = redactHeaders, + redactBodyProperties = redactBodyProperties + ) - repository.saveNetworkTrafficData(networkTrafficWithResponse) - trafficLogger.logResponse(networkTrafficWithResponse) + repository.saveNetworkTrafficData(networkTrafficWithResponse) + trafficLogger.logResponse(networkTrafficWithResponse) + } } val responseObserver = ResponseObserver.prepare { onResponse(responseHandler) } ResponseObserver.install(responseObserver, client) diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorIgnoreEndpointHandler.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorIgnoreEndpointHandler.kt new file mode 100644 index 0000000..9a23df5 --- /dev/null +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorIgnoreEndpointHandler.kt @@ -0,0 +1,38 @@ +package sp.bvantur.inspektify.ktor.client.data + +import io.ktor.client.request.HttpRequestBuilder +import sp.bvantur.inspektify.ktor.EndpointMatchingStrategy +import sp.bvantur.inspektify.ktor.IgnorePathData + +internal class InspektifyKtorIgnoreEndpointHandler { + + private var ignoreEndpoints: List = emptyList() + + fun configureEndpointIgnoring(ignoreEndpoints: List) { + this.ignoreEndpoints = ignoreEndpoints + } + + fun shouldIgnoreEndpoint(requestBuilder: HttpRequestBuilder): Boolean = ignoreEndpoints.filter { endpoint -> + if (endpoint.method.isAll()) { + true + } else { + endpoint.method.value == requestBuilder.method.value + } + }.any { endpoint -> + when (endpoint.matchingStrategy) { + is EndpointMatchingStrategy.Contains -> { + val containsValue = endpoint.matchingStrategy.value + if (containsValue.isEmpty()) { + false + } else { + requestBuilder.url.toString() + .contains(endpoint.matchingStrategy.value) + } + } + + is EndpointMatchingStrategy.Exact -> endpoint.matchingStrategy.value == requestBuilder.url.toString() + is EndpointMatchingStrategy.Regex -> requestBuilder.url.toString() + .matches(Regex(endpoint.matchingStrategy.value)) + } + } +} diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/datasource/NetworkTrafficLocalDataSource.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/datasource/NetworkTrafficLocalDataSource.kt index daeafb2..003b366 100644 --- a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/datasource/NetworkTrafficLocalDataSource.kt +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/client/data/datasource/NetworkTrafficLocalDataSource.kt @@ -41,10 +41,10 @@ internal class NetworkTrafficLocalDataSource( } } - suspend fun getNetworkTrafficData(id: Long): NetworkTrafficDataLocal = withContext(dispatcherProvider.io) { + suspend fun getNetworkTrafficData(id: Long): NetworkTrafficDataLocal? = withContext(dispatcherProvider.io) { database.inspektifyDBQueries.getNetworkTrafficById( id - ).executeAsOne() + ).executeAsOneOrNull() } suspend fun removeNetworkTrafficOlderThan(cutoffTimestamp: Long) { diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/data/NetworkTrafficRepositoryImpl.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/data/NetworkTrafficRepositoryImpl.kt index 07da627..74c0494 100644 --- a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/data/NetworkTrafficRepositoryImpl.kt +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/data/NetworkTrafficRepositoryImpl.kt @@ -12,9 +12,9 @@ internal class NetworkTrafficRepositoryImpl(private val localDataSource: Network localDataSource.saveNetworkTrafficData(networkTraffic) } - override suspend fun getNetworkTrafficData(id: Long): NetworkTraffic = localDataSource.getNetworkTrafficData( + override suspend fun getNetworkTrafficData(id: Long): NetworkTraffic? = localDataSource.getNetworkTrafficData( id - ).toNetworkTraffic() + )?.toNetworkTraffic() override suspend fun applyRetentionPolicyByDays(cutoffTimestamp: Long) { localDataSource.removeNetworkTrafficOlderThan(cutoffTimestamp) diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/di/AppModule.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/di/AppModule.kt index 9109635..d83a6e1 100644 --- a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/di/AppModule.kt +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/di/AppModule.kt @@ -6,6 +6,7 @@ import kotlinx.serialization.json.Json import sp.bvantur.inspektify.NetworkTrafficDataLocal import sp.bvantur.inspektify.db.InspektifyDB import sp.bvantur.inspektify.ktor.client.data.InspektifyDataRetentionHandler +import sp.bvantur.inspektify.ktor.client.data.InspektifyKtorIgnoreEndpointHandler import sp.bvantur.inspektify.ktor.client.data.InspektifyNetworkTrafficLogger import sp.bvantur.inspektify.ktor.client.data.InspektifyRequestHandler import sp.bvantur.inspektify.ktor.client.data.InspektifyResponseHandler @@ -29,6 +30,7 @@ internal object AppComponents { private var responseHandler: InspektifyResponseHandler? = null private var trafficLogger: InspektifyNetworkTrafficLogger? = null private var dataRetentionHandler: InspektifyDataRetentionHandler? = null + private var ignoreEndpointHandler: InspektifyKtorIgnoreEndpointHandler? = null fun getDatabaseInstance(): InspektifyDB { if (database == null) { @@ -102,6 +104,13 @@ internal object AppComponents { return dataRetentionHandler!! } + fun getInspektifyKtorIgnoreEndpointHandler(): InspektifyKtorIgnoreEndpointHandler { + if (ignoreEndpointHandler == null) { + ignoreEndpointHandler = InspektifyKtorIgnoreEndpointHandler() + } + return ignoreEndpointHandler!! + } + private val listOfNetworkTrafficHeaderAdapter = object : ColumnAdapter>>, String> { override fun decode(databaseValue: String): Set>> = diff --git a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/domain/NetworkTrafficRepository.kt b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/domain/NetworkTrafficRepository.kt index 389f523..5933566 100644 --- a/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/domain/NetworkTrafficRepository.kt +++ b/inspektify/src/commonMain/kotlin/sp/bvantur/inspektify/ktor/core/domain/NetworkTrafficRepository.kt @@ -5,7 +5,7 @@ import sp.bvantur.inspektify.ktor.client.domain.model.NetworkTraffic internal interface NetworkTrafficRepository { suspend fun saveNetworkTrafficData(networkTraffic: NetworkTraffic) - suspend fun getNetworkTrafficData(id: Long): NetworkTraffic + suspend fun getNetworkTrafficData(id: Long): NetworkTraffic? suspend fun applyRetentionPolicyByDays(cutoffTimestamp: Long) diff --git a/inspektify/src/commonTest/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorIgnoreEndpointHandlerTest.kt b/inspektify/src/commonTest/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorIgnoreEndpointHandlerTest.kt new file mode 100644 index 0000000..a495324 --- /dev/null +++ b/inspektify/src/commonTest/kotlin/sp/bvantur/inspektify/ktor/client/data/InspektifyKtorIgnoreEndpointHandlerTest.kt @@ -0,0 +1,401 @@ +@file:Suppress("ktlint:standard:max-line-length") + +package sp.bvantur.inspektify.ktor.client.data + +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.http.HttpMethod +import io.ktor.http.URLProtocol +import io.ktor.http.encodedPath +import sp.bvantur.inspektify.ktor.EndpointMatchingStrategy +import sp.bvantur.inspektify.ktor.IgnorePathData +import sp.bvantur.inspektify.ktor.MethodType +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class InspektifyKtorIgnoreEndpointHandlerTest { + + private lateinit var ignoreEndpointHandler: InspektifyKtorIgnoreEndpointHandler + + @BeforeTest + fun setup() { + ignoreEndpointHandler = InspektifyKtorIgnoreEndpointHandler() + } + + @Test + fun `GIVEN exact match strategy with correct endpoint defined and get method WHEN shouldIgnoreEndpoint is called THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/path/to/resource") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN exact match strategy with different endpoint defined and get method WHEN shouldIgnoreEndpoint is called THEN it returns that is should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + parameters.append("param1", "value1") + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/path/to/resource") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN exact match strategy with correct endpoint with params defined and post method WHEN shouldIgnoreEndpoint is called THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Post + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + parameters.append("param1", "value1") + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.POST, + matchingStrategy = EndpointMatchingStrategy.Exact( + "https://www.example.com/path/to/resource?param1=value1" + ) + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN exact match strategy and get method WHEN shouldIgnoreEndpoint is called with post method request THEN it returns that it should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Post + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/path/to/resource") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN exact match strategy with empty value and get method WHEN shouldIgnoreEndpoint is called THEN it returns that it should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Exact("") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN contains match strategy with correct endpoint defined and get method WHEN shouldIgnoreEndpoint is called THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Contains("https://www.example.com/path/to/resource") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN contains match strategy with different endpoint defined and get method WHEN shouldIgnoreEndpoint is called THEN it returns that is should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + parameters.append("param1", "value1") + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Contains("examples") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN contains match strategy with correct endpoint with params defined and post method WHEN shouldIgnoreEndpoint is called THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Post + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + parameters.append("param1", "value1") + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.POST, + matchingStrategy = EndpointMatchingStrategy.Contains("resource?param1=value1") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN contains match strategy with correct endpoint defined and get method WHEN shouldIgnoreEndpoint is called on endpoint with params THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + parameters.append("param1", "value1") + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Contains("https://www.example.com/path/to/resource") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN contains match strategy with empty value and get method WHEN shouldIgnoreEndpoint is called THEN it returns that it should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Contains("") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN regex match strategy with correct regex defined and get method WHEN shouldIgnoreEndpoint is called THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Regex("https://www\\.example\\.com/.*") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN regex match strategy with correct endpoint with params defined and post method WHEN shouldIgnoreEndpoint is called THEN it returns that is should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Post + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + parameters.append("param1", "value1") + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.POST, + matchingStrategy = EndpointMatchingStrategy.Regex("https://www\\.example\\.com/.*") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN regex match strategy with empty value and get method WHEN shouldIgnoreEndpoint is called THEN it returns that it should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Contains("") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN multiple exact match endpoints with one of them matching passed request WHEN shouldIgnoreEndpoint is called THEN it returns that it should be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.POST, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/other/resource") + ), + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/path/to/resource") + ) + ) + ) + + assertTrue { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } + + @Test + fun `GIVEN multiple exact and match endpoints with no matches WHEN shouldIgnoreEndpoint is called THEN it returns that it should not be ignored`() { + val request = HttpRequestBuilder().also { request -> + request.method = HttpMethod.Get + request.url { + protocol = URLProtocol.HTTPS + host = "www.example.com" + encodedPath = "/path/to/resource" + } + } + ignoreEndpointHandler.configureEndpointIgnoring( + listOf( + IgnorePathData( + method = MethodType.POST, + matchingStrategy = EndpointMatchingStrategy.Exact("https://www.example.com/other/resource") + ), + IgnorePathData( + method = MethodType.GET, + matchingStrategy = EndpointMatchingStrategy.Contains("https://www.example.com/different/resource") + ) + ) + ) + + assertFalse { + ignoreEndpointHandler.shouldIgnoreEndpoint(request) + } + } +} diff --git a/inspektify/src/iosMain/kotlin/sp/bvantur/inspektify/ktor/client/InspektifyShortcutHandler.kt b/inspektify/src/iosMain/kotlin/sp/bvantur/inspektify/ktor/client/InspektifyShortcutHandler.kt index df84d98..ab7c9d3 100644 --- a/inspektify/src/iosMain/kotlin/sp/bvantur/inspektify/ktor/client/InspektifyShortcutHandler.kt +++ b/inspektify/src/iosMain/kotlin/sp/bvantur/inspektify/ktor/client/InspektifyShortcutHandler.kt @@ -38,7 +38,9 @@ internal class InspektifySceneDelegate @OverrideInit constructor() : } @OptIn(BetaInteropApi::class) -fun getInspektifyUISceneConfiguration(configurationForConnectingSceneSession: UISceneSession): UISceneConfiguration { +public fun getInspektifyUISceneConfiguration( + configurationForConnectingSceneSession: UISceneSession +): UISceneConfiguration { val configuration = UISceneConfiguration( name = configurationForConnectingSceneSession.configuration.name, sessionRole = configurationForConnectingSceneSession.role @@ -48,4 +50,4 @@ fun getInspektifyUISceneConfiguration(configurationForConnectingSceneSession: UI } @Suppress("FunctionOnlyReturningConstant") -fun getInspektifyShortcutType(): String = "Inspektify" +public fun getInspektifyShortcutType(): String = "Inspektify" diff --git a/inspektifySample/composeApp/src/commonMain/kotlin/sp/bvantur/inspektify/sample/di/NetworkModule.kt b/inspektifySample/composeApp/src/commonMain/kotlin/sp/bvantur/inspektify/sample/di/NetworkModule.kt index 37639f5..afe879c 100644 --- a/inspektifySample/composeApp/src/commonMain/kotlin/sp/bvantur/inspektify/sample/di/NetworkModule.kt +++ b/inspektifySample/composeApp/src/commonMain/kotlin/sp/bvantur/inspektify/sample/di/NetworkModule.kt @@ -32,6 +32,12 @@ val networkModule = module { autoDetectEnabled = false logLevel = LogLevel.All dataRetentionPolicy = DataRetentionPolicy.SessionCount(4) +// ignoreEndpoints = listOf( +// IgnorePathData( +// method = MethodType.ALL, +// matchingStrategy = EndpointMatchingStrategy.Regex("https://reqres\\.in/.*"), +// ) +// ) } } } diff --git a/konsistTest/src/jvmTest/kotlin/inspektify/LibKonsistTest.kt b/konsistTest/src/jvmTest/kotlin/inspektify/LibKonsistTest.kt index af37951..004df45 100644 --- a/konsistTest/src/jvmTest/kotlin/inspektify/LibKonsistTest.kt +++ b/konsistTest/src/jvmTest/kotlin/inspektify/LibKonsistTest.kt @@ -32,7 +32,10 @@ class LibKonsistTest { val allowedPublicComponents = listOf( "InspektifyKtor", "InspektifyKtorConfig", - "NetworkTrafficHeader" + "NetworkTrafficHeader", + "IgnorePathData", + "MethodType", + "EndpointMatchingStrategy" ) Konsist.scopeFromModule("inspektify") @@ -93,7 +96,10 @@ class LibKonsistTest { "InspektifyKtorConfig:autoDetectEnabled", "InspektifyKtorConfig:shortcutEnabled", "InspektifyKtorConfig:redactHeaders", - "InspektifyKtorConfig:redactBodyProperties" + "InspektifyKtorConfig:redactBodyProperties", + "InspektifyKtorConfig:ignoreEndpoints", + "IgnorePathData:method", + "IgnorePathData:matchingStrategy" ) val publicComponents = mutableListOf()