Grund: Compose-app med login mot gym-API:t, VS Code-task och CI-release
Some checks failed
release / build-release (push) Failing after 1m42s
Some checks failed
release / build-release (push) Failing after 1m42s
- Kotlin + Jetpack Compose + Material 3, minSdk 31 (Android 12) - GraphQL-klient (OkHttp) med login/refresh/validate/logout som webben - Tokenlagring i DataStore, auto-refresh av JWT - Signering via gitignorerad signing/ lokalt och Actions-secrets i CI - VS Code build-task -> build/FitnessDroid.apk - Gitea Actions: bygger APK på Pi5 (arm64, aapt2-override) och publicerar rullande latest-release Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kf4MAbZ3c3B4Xy5XfuGsCP
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package eu.brassepc.fitnessdroid.data
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class GraphQlException(val messages: List<String>) : Exception(messages.joinToString("; "))
|
||||
|
||||
/**
|
||||
* Minimal GraphQL-klient över OkHttp. Samma request-form som webbklienten
|
||||
* (gymTrackerApi.js): POST {query, variables} + "Authorization: Bearer <jwt>".
|
||||
*/
|
||||
class GraphQlClient(private val urlProvider: suspend () -> String) {
|
||||
|
||||
private val http = OkHttpClient.Builder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
suspend fun execute(
|
||||
query: String,
|
||||
variables: JsonObject = buildJsonObject {},
|
||||
token: String? = null,
|
||||
): JsonObject = withContext(Dispatchers.IO) {
|
||||
val body = buildJsonObject {
|
||||
put("query", query)
|
||||
put("variables", variables)
|
||||
}
|
||||
val request = Request.Builder()
|
||||
.url(urlProvider())
|
||||
.post(body.toString().toRequestBody("application/json".toMediaType()))
|
||||
.apply { if (token != null) header("Authorization", "Bearer $token") }
|
||||
.build()
|
||||
|
||||
http.newCall(request).execute().use { response ->
|
||||
val text = response.body?.string().orEmpty()
|
||||
val root = runCatching { json.parseToJsonElement(text).jsonObject }.getOrNull()
|
||||
?: throw GraphQlException(listOf("HTTP ${response.code}: oväntat svar från servern"))
|
||||
|
||||
root["errors"]?.jsonArray?.takeIf { it.isNotEmpty() }?.let { errors ->
|
||||
throw GraphQlException(
|
||||
errors.map {
|
||||
it.jsonObject["message"]?.jsonPrimitive?.content ?: "Okänt fel"
|
||||
}
|
||||
)
|
||||
}
|
||||
root["data"]?.jsonObject
|
||||
?: throw GraphQlException(listOf("HTTP ${response.code}: tomt svar från servern"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user