You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.3 KiB
144 lines
4.3 KiB
package com.timesafari.dailynotification
|
|
|
|
import androidx.room.*
|
|
import androidx.room.migration.Migration
|
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
|
|
|
/**
|
|
* SQLite schema for Daily Notification Plugin
|
|
* Implements TTL-at-fire invariant and rolling window armed design
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 1.1.0
|
|
*/
|
|
@Entity(tableName = "content_cache")
|
|
data class ContentCache(
|
|
@PrimaryKey val id: String,
|
|
val fetchedAt: Long, // epoch ms
|
|
val ttlSeconds: Int,
|
|
val payload: ByteArray, // BLOB
|
|
val meta: String? = null
|
|
)
|
|
|
|
@Entity(tableName = "schedules")
|
|
data class Schedule(
|
|
@PrimaryKey val id: String,
|
|
val kind: String, // 'fetch' or 'notify'
|
|
val cron: String? = null, // optional cron expression
|
|
val clockTime: String? = null, // optional HH:mm
|
|
val enabled: Boolean = true,
|
|
val lastRunAt: Long? = null,
|
|
val nextRunAt: Long? = null,
|
|
val jitterMs: Int = 0,
|
|
val backoffPolicy: String = "exp",
|
|
val stateJson: String? = null
|
|
)
|
|
|
|
@Entity(tableName = "callbacks")
|
|
data class Callback(
|
|
@PrimaryKey val id: String,
|
|
val kind: String, // 'http', 'local', 'queue'
|
|
val target: String, // url_or_local
|
|
val headersJson: String? = null,
|
|
val enabled: Boolean = true,
|
|
val createdAt: Long
|
|
)
|
|
|
|
@Entity(tableName = "history")
|
|
data class History(
|
|
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
|
val refId: String, // content or schedule id
|
|
val kind: String, // fetch/notify/callback
|
|
val occurredAt: Long,
|
|
val durationMs: Long? = null,
|
|
val outcome: String, // success|failure|skipped_ttl|circuit_open
|
|
val diagJson: String? = null
|
|
)
|
|
|
|
@Database(
|
|
entities = [ContentCache::class, Schedule::class, Callback::class, History::class],
|
|
version = 1,
|
|
exportSchema = false
|
|
)
|
|
@TypeConverters(Converters::class)
|
|
abstract class DailyNotificationDatabase : RoomDatabase() {
|
|
abstract fun contentCacheDao(): ContentCacheDao
|
|
abstract fun scheduleDao(): ScheduleDao
|
|
abstract fun callbackDao(): CallbackDao
|
|
abstract fun historyDao(): HistoryDao
|
|
}
|
|
|
|
@Dao
|
|
interface ContentCacheDao {
|
|
@Query("SELECT * FROM content_cache WHERE id = :id")
|
|
suspend fun getById(id: String): ContentCache?
|
|
|
|
@Query("SELECT * FROM content_cache ORDER BY fetchedAt DESC LIMIT 1")
|
|
suspend fun getLatest(): ContentCache?
|
|
|
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
suspend fun upsert(contentCache: ContentCache)
|
|
|
|
@Query("DELETE FROM content_cache WHERE fetchedAt < :cutoffTime")
|
|
suspend fun deleteOlderThan(cutoffTime: Long)
|
|
|
|
@Query("SELECT COUNT(*) FROM content_cache")
|
|
suspend fun getCount(): Int
|
|
}
|
|
|
|
@Dao
|
|
interface ScheduleDao {
|
|
@Query("SELECT * FROM schedules WHERE enabled = 1")
|
|
suspend fun getEnabled(): List<Schedule>
|
|
|
|
@Query("SELECT * FROM schedules WHERE id = :id")
|
|
suspend fun getById(id: String): Schedule?
|
|
|
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
suspend fun upsert(schedule: Schedule)
|
|
|
|
@Query("UPDATE schedules SET enabled = :enabled WHERE id = :id")
|
|
suspend fun setEnabled(id: String, enabled: Boolean)
|
|
|
|
@Query("UPDATE schedules SET lastRunAt = :lastRunAt, nextRunAt = :nextRunAt WHERE id = :id")
|
|
suspend fun updateRunTimes(id: String, lastRunAt: Long?, nextRunAt: Long?)
|
|
}
|
|
|
|
@Dao
|
|
interface CallbackDao {
|
|
@Query("SELECT * FROM callbacks WHERE enabled = 1")
|
|
suspend fun getEnabled(): List<Callback>
|
|
|
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
suspend fun upsert(callback: Callback)
|
|
|
|
@Query("DELETE FROM callbacks WHERE id = :id")
|
|
suspend fun deleteById(id: String)
|
|
}
|
|
|
|
@Dao
|
|
interface HistoryDao {
|
|
@Insert
|
|
suspend fun insert(history: History)
|
|
|
|
@Query("SELECT * FROM history WHERE occurredAt >= :since ORDER BY occurredAt DESC")
|
|
suspend fun getSince(since: Long): List<History>
|
|
|
|
@Query("DELETE FROM history WHERE occurredAt < :cutoffTime")
|
|
suspend fun deleteOlderThan(cutoffTime: Long)
|
|
|
|
@Query("SELECT COUNT(*) FROM history")
|
|
suspend fun getCount(): Int
|
|
}
|
|
|
|
class Converters {
|
|
@TypeConverter
|
|
fun fromByteArray(value: ByteArray?): String? {
|
|
return value?.let { String(it) }
|
|
}
|
|
|
|
@TypeConverter
|
|
fun toByteArray(value: String?): ByteArray? {
|
|
return value?.toByteArray()
|
|
}
|
|
}
|
|
|