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

[79] Add configuration for ignoring specific endpoints #83

Merged
merged 5 commits into from
Dec 6, 2024
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
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -137,4 +138,7 @@ the sample is configured to use Ktor from the 3.x.x family, but if you want to t
</a>
<a href="https://medium.com/@bvantur">
<img src="https://miro.medium.com/v2/resize:fit:1400/1*psYl0y9DUzZWtHzFJLIvTw.png" alt="Medium" width="40" height="40">
</a>
</a>

## Sponsor my work
<a href="https://buymeacoffee.com/bvantur" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
44 changes: 44 additions & 0 deletions docs/IGNORE_ENDPOINTS_CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class InspektifyKtorConfig {
public var dataRetentionPolicy: DataRetentionPolicy = DataRetentionPolicy.DayDuration(14)
public var redactHeaders: List<String> = emptyList()
public var redactBodyProperties: List<String> = emptyList()
public var ignoreEndpoints: List<IgnorePathData> = emptyList()
}

public sealed interface LogLevel {
Expand All @@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand All @@ -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,
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IgnorePathData> = emptyList()

fun configureEndpointIgnoring(ignoreEndpoints: List<IgnorePathData>) {
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))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -102,6 +104,13 @@ internal object AppComponents {
return dataRetentionHandler!!
}

fun getInspektifyKtorIgnoreEndpointHandler(): InspektifyKtorIgnoreEndpointHandler {
if (ignoreEndpointHandler == null) {
ignoreEndpointHandler = InspektifyKtorIgnoreEndpointHandler()
}
return ignoreEndpointHandler!!
}

private val listOfNetworkTrafficHeaderAdapter =
object : ColumnAdapter<Set<Map.Entry<String, List<String>>>, String> {
override fun decode(databaseValue: String): Set<Map.Entry<String, List<String>>> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading
Loading