database: initial commit

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2021-06-03 00:41:58 +05:30
parent fcfcbfbf92
commit ce3dd8b7e7
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
13 changed files with 314 additions and 0 deletions

8
.idea/artifacts/database_jvm.xml generated Normal file
View file

@ -0,0 +1,8 @@
<component name="ArtifactManager">
<artifact type="jar" name="database-jvm">
<output-path>$PROJECT_DIR$/database/build/libs</output-path>
<root id="archive" name="database-jvm.jar">
<element id="module-output" name="Claw.database.jvmMain" />
</root>
</artifact>
</component>

1
database/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

42
database/build.gradle.kts Normal file
View file

@ -0,0 +1,42 @@
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.squareup.sqldelight") version "1.5.0"
}
kotlin {
android()
jvm("desktop") { compilations.all { kotlinOptions.jvmTarget = "11" } }
sourceSets {
val commonMain by getting
val commonTest by getting
val androidMain by getting {
dependencies { implementation(libs.thirdparty.sqldelight.androidDriver) }
}
val androidTest by getting
val desktopMain by getting { dependencies { implementation(libs.thirdparty.sqldelight.jvmDriver) } }
val desktopTest by getting {
dependencies {
implementation(libs.kotlin.coroutines.core)
implementation(kotlin("test-junit"))
}
}
}
}
android {
compileSdkVersion(30)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(23)
targetSdkVersion(30)
consumerProguardFiles("consumer-rules.pro")
}
}
configure<com.squareup.sqldelight.gradle.SqlDelightExtension> {
database("LobstersDatabase") {
packageName = "dev.msfjarvis.lobsters.database"
sourceFolders = listOf("sqldelight")
}
}

View file

@ -0,0 +1 @@
-keep class dev.msfjarvis.lobsters.model.** { *; }

View file

@ -0,0 +1,4 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.msfjarvis.lobsters.database"
/>

View file

@ -0,0 +1,12 @@
package dev.msfjarvis.lobsters.data.local
import android.content.Context
import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import dev.msfjarvis.lobsters.database.LobstersDatabase
actual class DriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(LobstersDatabase.Schema, context, LobstersDatabaseName)
}
}

View file

@ -0,0 +1,18 @@
package dev.msfjarvis.lobsters.data.local
import com.squareup.sqldelight.db.SqlDriver
import dev.msfjarvis.lobsters.data.model.TagsAdapter
import dev.msfjarvis.lobsters.database.LobstersDatabase
internal const val LobstersDatabaseName = "SavedPosts.db"
expect class DriverFactory {
fun createDriver(): SqlDriver
}
private fun getTagsAdapter() = TagsAdapter()
fun createDatabase(driverFactory: DriverFactory): LobstersDatabase {
val driver = driverFactory.createDriver()
return LobstersDatabase(driver, SavedPost.Adapter(getTagsAdapter()))
}

View file

@ -0,0 +1,17 @@
package dev.msfjarvis.lobsters.data.model
import com.squareup.sqldelight.ColumnAdapter
class TagsAdapter : ColumnAdapter<List<String>, String> {
override fun decode(databaseValue: String): List<String> {
return databaseValue.split(SEPARATOR)
}
override fun encode(value: List<String>): String {
return value.joinToString(SEPARATOR)
}
private companion object {
private const val SEPARATOR = ","
}
}

View file

@ -0,0 +1,39 @@
import kotlin.collections.List;
CREATE TABLE IF NOT EXISTS SavedPost(
shortId TEXT NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
url TEXT NOT NULL,
createdAt TEXT NOT NULL,
commentsUrl TEXT NOT NULL,
submitterName TEXT NOT NULL,
submitterAvatarUrl TEXT NOT NULL,
tags TEXT AS List<String> NOT NULL
);
insertOrReplacePost:
INSERT OR REPLACE
INTO SavedPost
VALUES ?;
selectAllPosts:
SELECT *
FROM SavedPost;
selectCount:
SELECT COUNT(*)
FROM SavedPost;
deleteAllPosts:
DELETE
FROM SavedPost;
deletePost:
DELETE
FROM SavedPost
WHERE shortId = ?;
selectPost:
SELECT *
FROM SavedPost
WHERE shortId = ?;

View file

@ -0,0 +1,13 @@
package dev.msfjarvis.lobsters.data.local
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
import dev.msfjarvis.lobsters.database.LobstersDatabase
actual class DriverFactory {
actual fun createDriver(): SqlDriver {
val driver: SqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
LobstersDatabase.Schema.create(driver)
return driver
}
}

View file

@ -0,0 +1,153 @@
package dev.msfjarvis.lobsters.data.local
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
import dev.msfjarvis.lobsters.data.model.TagsAdapter
import dev.msfjarvis.lobsters.database.LobstersDatabase
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.coroutines.runBlocking
import org.junit.Before
@OptIn(ExperimentalStdlibApi::class)
class SqlDelightQueriesTest {
private lateinit var postQueries: SavedPostQueries
@Before
fun setUp() {
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
LobstersDatabase.Schema.create(driver)
val database =
LobstersDatabase(
driver,
SavedPost.Adapter(TagsAdapter()),
)
postQueries = database.savedPostQueries
}
@Test
fun selectCount() = runBlocking {
val posts = createTestData(5)
posts.forEach { postQueries.insertOrReplacePost(it) }
val postCount = postQueries.selectCount().executeAsOne()
assertEquals(5, postCount)
}
@Test
fun insertIntoDatabase() = runBlocking {
// Get 5 posts
val posts = createTestData(5)
// Insert posts into DB
posts.forEach { postQueries.insertOrReplacePost(it) }
// Check post count
val postsCount = postQueries.selectCount().executeAsOne()
assertEquals(5, postsCount)
}
@Test
fun replaceFromDatabase() = runBlocking {
// Get 1 post
val post = createTestData(1)[0]
// Insert post into DB
postQueries.insertOrReplacePost(post)
// Create a new post and try replacing it
val newPost = post.copy(submitterName = "Fake name")
postQueries.insertOrReplacePost(newPost)
// Check post count
val postsCount = postQueries.selectCount().executeAsOne()
assertEquals(1, postsCount)
// Check if post is updated
val postFromDb = postQueries.selectPost(post.shortId).executeAsOne()
assertEquals("Fake name", postFromDb.submitterName)
}
@Test
fun selectPost() = runBlocking {
// Get 1 post
val post = createTestData(1)[0]
// Insert post into DB
postQueries.insertOrReplacePost(post)
val postFromDb = postQueries.selectAllPosts().executeAsOne()
assertEquals("test_id_1", postFromDb.shortId)
}
@Test
fun selectAllPosts() = runBlocking {
// Get 5 post
val posts = createTestData(5)
// Insert posts into DB
posts.forEach { postQueries.insertOrReplacePost(it) }
val postsFromDb = postQueries.selectAllPosts().executeAsList()
// Check if all posts have correct shortId
for (i in 1..5) {
assertEquals("test_id_$i", postsFromDb[i - 1].shortId)
}
}
@Test
fun deletePost() = runBlocking {
// Create 3 posts and insert them to DB
val posts = createTestData(3)
posts.forEach { postQueries.insertOrReplacePost(it) }
// Delete 2nd post
postQueries.deletePost("test_id_2")
val postsFromDB = postQueries.selectAllPosts().executeAsList()
// Check if size is 2, and only the correct post is deleted
assertEquals(2, postsFromDB.size)
assertEquals("test_id_1", postsFromDB[0].shortId)
assertEquals("test_id_3", postsFromDB[1].shortId)
}
@Test
fun deleteAllPost() = runBlocking {
// Create 5 posts and insert them to DB
val posts = createTestData(5)
posts.forEach { postQueries.insertOrReplacePost(it) }
// Delete all posts
postQueries.deleteAllPosts()
val postsCount = postQueries.selectCount().executeAsOne()
// Check if db is empty
assertEquals(0, postsCount)
}
private fun createTestData(count: Int): ArrayList<SavedPost> {
val posts = arrayListOf<SavedPost>()
for (i in 1..count) {
val post =
SavedPost(
shortId = "test_id_$i",
createdAt = "0",
title = "test",
url = "test_url",
commentsUrl = "test_comments_url",
submitterName = "test_user_$i",
submitterAvatarUrl = "test_avatar_url",
tags = listOf(),
)
posts.add(post)
}
return posts
}
}

View file

@ -3,6 +3,7 @@ coroutines = "1.5.0"
kotlin = "1.5.10"
moshix = "0.11.2"
retrofit = "2.9.0"
sqldelight = "1.5.0"
[libraries]
@ -17,5 +18,8 @@ thirdparty-moshix-metadatareflect = { module = "dev.zacsweers.moshix:moshi-metad
thirdparty-retrofit-lib = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
thirdparty-retrofit-moshiConverter = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
thirdparty-sqldelight-jvmDriver = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
thirdparty-sqldelight-androidDriver = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
testing-kotlintest-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
testing-mockWebServer = "com.squareup.okhttp3:mockwebserver3-junit4:5.0.0-alpha.2"

View file

@ -19,4 +19,6 @@ include(":api")
include(":common")
include(":database")
include(":desktop")