🔗 Interceptory

Interceptory pozwalają modyfikować requesty i response w pipeline. KNET oferuje 12+ gotowych interceptorów.

Jak działają?

// Interceptor to funkcja, która:
// 1. Otrzymuje request
// 2. Może go zmodyfikować
// 3. Przekazuje dalej w chain
// 4. Otrzymuje response
// 5. Może go zmodyfikować
// 6. Zwraca response

interface KNETInterceptor {
    suspend fun intercept(
        request: KNETRequest,
        chain: suspend (KNETRequest) -> KNETResponse
    ): KNETResponse
}
Przepływ
Request → [Interceptor 1] → [Interceptor 2] → [Interceptor 3] → HTTP
                                                                    ↓
Response ← [Interceptor 1] ← [Interceptor 2] ← [Interceptor 3] ← HTTP

Dodawanie interceptorów

val client = KNETClient.builder()
    .addInterceptor(KNETLoggingInterceptor())
    .addInterceptor(KNETHeaderInterceptor.bearer(token))
    .addInterceptor(KNETRetryInterceptor(maxRetries = 3))
    .addInterceptor(KNETCacheInterceptor(cache))
    .build()

📋 Lista interceptorów

KNETLoggingInterceptor

Loguje requesty i response do Logcat.

val interceptor = KNETLoggingInterceptor(
    level = LogLevel.BODY,  // NONE, BASIC, HEADERS, BODY
    tag = "KNET",
    logger = { tag, msg -> Log.d(tag, msg) }
)

// Przykład output:
// --> POST https://api.example.com/users
// Content-Type: application/json
// {"name":"Jan"}
// --> END POST
// <-- 201 Created (150ms)
// {"id":1,"name":"Jan"}
// <-- END HTTP

KNETHeaderInterceptor

Dodaje nagłówki do każdego requestu.

// Bearer token
val interceptor = KNETHeaderInterceptor.bearer(token)

// Basic auth
val interceptor = KNETHeaderInterceptor.basic(username, password)

// Custom headers
val interceptor = KNETHeaderInterceptor(mapOf(
    "X-API-Key" to apiKey,
    "X-Client-Version" to BuildConfig.VERSION_NAME,
    "Accept-Language" to Locale.getDefault().language
))

// Dynamic headers
val interceptor = KNETHeaderInterceptor { request ->
    mapOf(
        "X-Request-ID" to UUID.randomUUID().toString(),
        "X-Timestamp" to System.currentTimeMillis().toString()
    )
}

KNETRetryInterceptor

Automatyczny retry przy błędach.

val interceptor = KNETRetryInterceptor(
    maxRetries = 3,
    retryDelayMs = 1000,
    retryOnStatusCodes = setOf(500, 502, 503, 504),
    retryOnExceptions = setOf(
        SocketTimeoutException::class,
        ConnectException::class
    )
)

// Z exponential backoff
val interceptor = KNETExponentialBackoffInterceptor(
    maxRetries = 5,
    initialDelayMs = 1000,
    maxDelayMs = 30000,
    multiplier = 2.0,
    jitter = true  // Dodaje losowość
)

KNETCacheInterceptor

Cache odpowiedzi.

val cache = KNETMemoryCache(maxSize = 50)

val interceptor = KNETCacheInterceptor(
    cache = cache,
    defaultTtlMs = 300_000,  // 5 min
    cacheOnlyGet = true,     // Cache tylko GET
    respectCacheHeaders = true
)

KNETTimeoutInterceptor

Dynamiczne timeouty.

val interceptor = KNETTimeoutInterceptor(
    defaultTimeoutMs = 30_000,
    timeoutByHost = mapOf(
        "slow-api.example.com" to 60_000,
        "fast-api.example.com" to 10_000
    ),
    timeoutByPath = mapOf(
        "/upload" to 120_000,
        "/download" to 300_000
    )
)

KNETAuthInterceptor

Autoryzacja z auto-refresh tokena.

val interceptor = KNETAuthInterceptor(
    tokenProvider = { authRepository.getAccessToken() },
    refreshToken = {
        authRepository.refreshToken()
        authRepository.getAccessToken()
    },
    shouldRefresh = { response ->
        response.statusCode == 401
    }
)

KNETOfflineInterceptor

Obsługa trybu offline.

val interceptor = KNETOfflineInterceptor(
    isOnline = { networkChecker.isConnected() },
    offlineCache = offlineCache,
    queueOfflineRequests = true
)

KNETMetricsInterceptor

Zbieranie metryk.

val metrics = KNETMetricsCollector()

val interceptor = KNETMetricsInterceptor(metrics)

// Później
val stats = metrics.getStats()
println("Total requests: ${stats.totalRequests}")
println("Success rate: ${stats.successRate}%")
println("Avg latency: ${stats.averageLatencyMs}ms")

KNETMockInterceptor

Mockowanie odpowiedzi (do testów).

val interceptor = KNETMockInterceptor()
    .mock("GET", "/users") {
        KNETResponse(200, "OK", emptyMap(), """[{"id":1,"name":"Test"}]""")
    }
    .mock("POST", "/users") {
        KNETResponse(201, "Created", emptyMap(), """{"id":2}""")
    }
    .mockError("GET", "/error") {
        throw IOException("Simulated error")
    }

KNETCompressionInterceptor

Kompresja request body.

val interceptor = KNETCompressionInterceptor(
    algorithm = CompressionAlgorithm.GZIP,
    minSizeToCompress = 1024  // Kompresuj tylko > 1KB
)

KNETTransformInterceptor

Transformacja request/response.

val interceptor = KNETTransformInterceptor(
    transformRequest = { request ->
        // Dodaj timestamp do każdego body
        request.copy(
            data = (request.data as? Map<*, *>)?.plus(
                "timestamp" to System.currentTimeMillis()
            ) ?: request.data
        )
    },
    transformResponse = { response ->
        // Parsuj i waliduj
        response
    }
)

KNETErrorInterceptor

Centralna obsługa błędów.

val interceptor = KNETErrorInterceptor { request, error ->
    // Loguj błędy
    analytics.logError("API Error", mapOf(
        "url" to request.url,
        "error" to error.message
    ))

    // Możesz zwrócić fallback response
    // lub rzucić dalej
    throw error
}

Własny interceptor

class MyCustomInterceptor : KNETInterceptor {

    override suspend fun intercept(
        request: KNETRequest,
        chain: suspend (KNETRequest) -> KNETResponse
    ): KNETResponse {
        // 1. Przed requestem
        val startTime = System.currentTimeMillis()
        val modifiedRequest = request.copy(
            headers = request.headers + ("X-Custom" to "value")
        )

        // 2. Wykonaj request
        val response = chain(modifiedRequest)

        // 3. Po response
        val duration = System.currentTimeMillis() - startTime
        Log.d("MyInterceptor", "${request.url} took ${duration}ms")

        return response
    }
}

// Użycie
val client = KNETClient.builder()
    .addInterceptor(MyCustomInterceptor())
    .build()

Kolejność interceptorów

⚠️ Ważne

Kolejność interceptorów ma znaczenie! Pierwszy dodany jest wykonywany pierwszy dla requestu i ostatni dla response.

// Zalecana kolejność:
val client = KNETClient.builder()
    .addInterceptor(KNETLoggingInterceptor())      // 1. Logowanie (widzi wszystko)
    .addInterceptor(KNETMetricsInterceptor())      // 2. Metryki
    .addInterceptor(KNETErrorInterceptor())        // 3. Obsługa błędów
    .addInterceptor(KNETAuthInterceptor())         // 4. Auth (przed retry)
    .addInterceptor(KNETRetryInterceptor())        // 5. Retry
    .addInterceptor(KNETCacheInterceptor())        // 6. Cache (może pominąć sieć)
    .addInterceptor(KNETCompressionInterceptor())  // 7. Kompresja (przed siecią)
    .build()

📚 Zobacz też