📦 Response Unwrapper

KNETResponseUnwrapper automatycznie rozpakowuje zagnieżdżone odpowiedzi API do właściwych danych.

💡 Problem

Wiele API zwraca dane w strukturze wrapper:

{
  "success": true,
  "data": { "id": 1, "name": "John" },
  "meta": { "page": 1, "total": 100 }
}

Zamiast ręcznie wyciągać data, użyj unwrappera.

📦 Import

import rip.nerd.kitsunenet.util.KNETResponseUnwrapper
import rip.nerd.kitsunenet.util.unwrap
import rip.nerd.kitsunenet.util.unwrapList

🚀 Szybki start

val unwrapper = KNETResponseUnwrapper.forDataField()

// Response: { "data": { "id": 1, "name": "John" } }
val user = unwrapper.unwrap<User>(response)

// Response: { "data": [{ "id": 1 }, { "id": 2 }] }
val users = unwrapper.unwrapList<User>(response)

// Extension functions
val user = response.unwrap<User>()
val users = response.unwrapList<User>()

📋 Popularne formaty API

Standard "data" field

// { "success": true, "data": {...}, "meta": {...} }
val unwrapper = KNETResponseUnwrapper.forDataField()

Django REST Framework

// { "results": [...], "count": 100, "next": "..." }
val unwrapper = KNETResponseUnwrapper.forResultsField()

"result" field

// { "result": {...} }
val unwrapper = KNETResponseUnwrapper.forResultField()

"items" field

// { "items": [...], "total": 100 }
val unwrapper = KNETResponseUnwrapper.forItemsField()

Bez wrappera

// Dane bezpośrednio w response: { "id": 1, "name": "John" }
val unwrapper = KNETResponseUnwrapper.noWrapper()

🔧 Custom konfiguracja

val unwrapper = KNETResponseUnwrapper.builder()
    // Pole z danymi
    .dataField("result")

    // Pole z metadanymi
    .metaField("pagination")

    // Pole sukcesu
    .successField("ok")

    // Pole błędu
    .errorField("error")

    // Pole wiadomości
    .messageField("message")

    // Rzuć wyjątek przy błędzie
    .throwOnError(true)

    .build()

📊 Z metadanymi

// Response:
// {
//   "data": { "id": 1, "name": "John" },
//   "meta": { "version": "1.0", "timestamp": 12345 }
// }

val (user, meta) = unwrapper.unwrapWithMeta<User, ApiMeta>(response)

println("User: ${user?.name}")
println("API version: ${meta?.version}")

Lista z metadanymi

val (users, meta) = unwrapper.unwrapListWithMeta<User, PageMeta>(response)

📄 Paginacja

// Response:
// {
//   "data": [...],
//   "meta": { "page": 1, "total_pages": 10, "total": 100 }
// }

val result = unwrapper.unwrapPaginated<User>(response)

println("Items: ${result.items.size}")
println("Page: ${result.page}/${result.totalPages}")
println("Total: ${result.totalItems}")
println("Has next: ${result.hasNext}")
println("Has prev: ${result.hasPrev}")

// Custom nazwy pól
val result = unwrapper.unwrapPaginated<User>(
    response = response,
    pageField = "current_page",
    totalPagesField = "last_page",
    totalItemsField = "total_count"
)

❌ Obsługa błędów

// Sprawdź czy jest błąd
if (unwrapper.hasError(response)) {
    val errorMsg = unwrapper.getError(response)
    showError(errorMsg)
    return
}

// Lub z throwOnError
val unwrapper = KNETResponseUnwrapper.builder()
    .throwOnError(true)
    .build()

try {
    val user = unwrapper.unwrap<User>(response)
} catch (e: KNETResponseUnwrapper.ApiErrorException) {
    showError(e.message)
    // e.response - oryginalna odpowiedź
}

💡 Praktyczne przykłady

Repository z unwrapperem

class UserRepository(private val client: KNETClient) {

    private val unwrapper = KNETResponseUnwrapper.forDataField()

    suspend fun getUser(id: String): User? {
        val response = client.get("https://api.example.com/users/$id")
        return unwrapper.unwrap<User>(response)
    }

    suspend fun getUsers(page: Int): PaginatedResult<User> {
        val response = client.get("https://api.example.com/users?page=$page")
        return unwrapper.unwrapPaginated(response)
    }
}

Extension dla swojego API

// Stwórz unwrapper dla swojego API
object MyApiUnwrapper {
    private val unwrapper = KNETResponseUnwrapper.builder()
        .dataField("result")
        .successField("ok")
        .errorField("error")
        .build()

    inline fun <reified T> KNETResponse.unwrapMyApi(): T? {
        return unwrapper.unwrap<T>(this)
    }
}

// Użycie
val user = response.unwrapMyApi<User>()

Obsługa różnych formatów

class ApiService(private val client: KNETClient) {

    // Różne unwrappery dla różnych endpointów
    private val standardUnwrapper = KNETResponseUnwrapper.forDataField()
    private val legacyUnwrapper = KNETResponseUnwrapper.forResultField()
    private val listUnwrapper = KNETResponseUnwrapper.forItemsField()

    suspend fun getUser(id: String): User? {
        val response = client.get("/api/v2/users/$id")
        return standardUnwrapper.unwrap(response)
    }

    suspend fun getLegacyUser(id: String): User? {
        val response = client.get("/api/v1/users/$id")
        return legacyUnwrapper.unwrap(response)
    }
}

🔗 API Reference

KNETResponseUnwrapper

MetodaOpis
unwrap<T>(response)Rozpakowuje do obiektu
unwrapList<T>(response)Rozpakowuje do listy
unwrapWithMeta<T,M>(response)Z metadanymi
unwrapListWithMeta<T,M>(response)Lista z meta
unwrapPaginated<T>(response)Z paginacją
unwrapToJson(response)Do JSONObject
unwrapToJsonList(response)Do listy JSON
hasError(response)Sprawdza błąd
getError(response)Pobiera błąd

Presets

MetodaFormat
forDataField(){ "data": {...} }
forResultField(){ "result": {...} }
forResultsField(){ "results": [...] }
forItemsField(){ "items": [...] }
noWrapper()Bez wrappera

📚 Zobacz też