git.iko.soy/parseable-android
on icon help sign in
198 lines 5.9 kB history raw
1package com.parseable.android.data.model
3import kotlinx.serialization.SerialName
4import kotlinx.serialization.Serializable
5import kotlinx.serialization.json.JsonElement
6import kotlinx.serialization.json.JsonObject
8/**
9 * Server connection configuration stored in DataStore.
10 */
11data class ServerConfig(
12 val serverUrl: String,
13 val username: String,
14 val password: String,
15 val useTls: Boolean = true,
18/**
19 * Represents a log stream entry returned by GET /api/v1/logstream
20 */
21@Serializable
22data class LogStream(
23 val name: String,
26/**
27 * Query request body sent to POST /api/v1/query
28 */
29@Serializable
30data class QueryRequest(
31 val query: String,
32 val startTime: String,
33 val endTime: String,
36/**
37 * Stream stats from GET /api/v1/logstream/{name}/stats
38 */
39@Serializable
40data class StreamStats(
41 val ingestion: IngestionStats? = null,
42 val storage: StorageStats? = null,
43 val stream: String? = null,
44 val time: String? = null,
47@Serializable
48data class IngestionStats(
49 val count: Long? = null,
50 val size: String? = null,
51 val format: String? = null,
52 @SerialName("lifetime_count")
53 val lifetimeCount: Long? = null,
54 @SerialName("lifetime_size")
55 val lifetimeSize: String? = null,
56 @SerialName("deleted_count")
57 val deletedCount: Long? = null,
58 @SerialName("deleted_size")
59 val deletedSize: String? = null,
62@Serializable
63data class StorageStats(
64 val size: String? = null,
65 val format: String? = null,
66 @SerialName("lifetime_size")
67 val lifetimeSize: String? = null,
68 @SerialName("deleted_size")
69 val deletedSize: String? = null,
72/**
73 * Schema field info from GET /api/v1/logstream/{name}/schema
74 */
75@Serializable
76data class StreamSchema(
77 val fields: List<SchemaField> = emptyList(),
80@Serializable
81data class SchemaField(
82 val name: String,
83 @SerialName("data_type")
84 val dataType: JsonElement? = null,
85 val nullable: Boolean? = null,
86 val dict_id: Long? = null,
87 val dict_is_ordered: Boolean? = null,
88 val metadata: JsonObject? = null,
91/**
92 * Retention config from GET /api/v1/logstream/{name}/retention
93 */
94@Serializable
95data class RetentionConfig(
96 val description: String? = null,
97 val duration: String? = null,
98 val action: String? = null,
101/**
102 * About info from GET /api/v1/about
103 */
104@Serializable
105data class AboutInfo(
106 val version: String? = null,
107 val commit: String? = null,
108 @SerialName("deploymentId")
109 val deploymentId: String? = null,
110 val license: JsonObject? = null,
111 val mode: String? = null,
112 val staging: String? = null,
113 val store: JsonObject? = null,
114 @SerialName("grpcPort")
115 val grpcPort: Int? = null,
116 val analytics: JsonObject? = null,
117 @SerialName("queryEngine")
118 val queryEngine: String? = null,
121/**
122 * Alert summary returned by GET /api/v1/alerts.
123 *
124 * The global alerts endpoint returns summary objects with fields like `title`, `state`,
125 * `severity`, and `datasets`. The per-stream endpoint uses `name` and `alerts[]` nesting.
126 * Both shapes are supported via optional fields.
127 */
128@Serializable
129data class Alert(
130 val id: String? = null,
131 val version: String? = null,
132 // Per-stream alert format uses "name"; global summary uses "title"
133 val name: String? = null,
134 val title: String? = null,
135 val message: String? = null,
136 val rule: JsonObject? = null,
137 val targets: List<JsonObject> = emptyList(),
138 // Per-stream alert format
139 val stream: String? = null,
140 val enabled: Boolean? = null,
141 // Global summary fields
142 val state: String? = null,
143 val severity: String? = null,
144 @SerialName("alertType")
145 val alertType: String? = null,
146 @SerialName("notificationState")
147 val notificationState: String? = null,
148 val created: String? = null,
149 @SerialName("lastTriggeredAt")
150 val lastTriggeredAt: String? = null,
151 val tags: List<String> = emptyList(),
152 val datasets: List<String> = emptyList(),
153) {
154 /** Best-effort display name from either response format. */
155 val displayName: String get() = title ?: name ?: "Unnamed Alert"
157 /** Whether the alert is active, derived from either response format. */
158 val isEnabled: Boolean get() = when {
159 state != null -> state != "Disabled"
160 enabled != null -> enabled
161 else -> true
162 }
164 /** Stream(s) this alert is configured for. */
165 val streamDisplay: String? get() = when {
166 datasets.isNotEmpty() -> datasets.joinToString(", ")
167 stream != null -> stream
168 else -> null
169 }
172/**
173 * Result wrapper for API calls
174 */
175sealed class ApiResult<out T> {
176 data class Success<T>(val data: T) : ApiResult<T>()
177 data class Error(val message: String, val code: Int = 0) : ApiResult<Nothing>() {
178 val isUnauthorized: Boolean get() = code == 401
179 val isNotFound: Boolean get() = code == 404
180 val isServerError: Boolean get() = code in 500..599
181 val isNetworkError: Boolean get() = code == 0
183 val userMessage: String get() = when {
184 code == 401 -> "Session expired. Please log in again."
185 code == 403 -> "Permission denied."
186 code == 404 -> "Resource not found. It may have been deleted."
187 code == 429 -> "Too many requests. Please wait and try again."
188 code in 500..599 -> "Server error ($code). Please try again later."
189 code == 0 -> when {
190 message.contains("timeout", ignoreCase = true) -> "Connection timed out. Check your network."
191 message.contains("Unable to resolve host", ignoreCase = true) -> "No internet connection."
192 message.contains("Connection refused", ignoreCase = true) -> "Server is unreachable."
193 else -> "Network error: $message"
194 }
195 else -> message
196 }
197 }