130: Split API and database models r=msfjarvis a=msfjarvis

- Introduces a new `SavedPost` table stripped down to only the necessary fields for persistence
- Deletes the old `LobstersPost` table
- Switches `LobstersItem` to take in a `SavedPost` for rendering
- Replaces moshi-metadata-reflect with moshi-ksp

Fixes #118 

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
bors[bot] 2021-02-28 15:29:33 +00:00 committed by GitHub
commit f10e2b79ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 195 additions and 200 deletions

1
.github/ci-gradle.properties vendored Normal file
View file

@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

View file

@ -10,12 +10,17 @@ jobs:
test-pr:
runs-on: macOS-latest
steps:
- uses: actions/setup-java@d202f5dbf7256730fb690ec59f6381650114feb2
with:
java-version: '11'
- name: Checkout repository
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
- uses: burrunan/gradle-cache-action@03c71a8ba93d670980695505f48f49daf43704a6
name: Run unit tests
with:

6
.idea/kotlinScripting.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinScriptingSettings">
<option name="suppressDefinitionsCheck" value="true" />
</component>
</project>

View file

@ -1,12 +1,12 @@
plugins {
kotlin("jvm")
id("com.google.devtools.ksp") version "1.4.30-1.0.0-alpha04"
`lobsters-plugin`
}
dependencies {
api(Dependencies.ThirdParty.Retrofit.lib)
implementation(project(":database"))
implementation(Dependencies.ThirdParty.Moshi.moshiMetadataReflect)
ksp(Dependencies.ThirdParty.Moshi.ksp)
implementation(Dependencies.ThirdParty.Retrofit.moshi)
testImplementation(Dependencies.Kotlin.Coroutines.core)
testImplementation(Dependencies.Testing.junit)

View file

@ -1,6 +1,6 @@
package dev.msfjarvis.lobsters.data.api
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.model.LobstersPost
import retrofit2.http.GET
import retrofit2.http.Query

View file

@ -1,10 +1,12 @@
package dev.msfjarvis.lobsters.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class KeybaseSignature(
@Json(name = "kb_username")
val kbUsername: String,
@Json(name = "sig_hash")
val sigHash: String
val sigHash: String,
)

View file

@ -0,0 +1,26 @@
package dev.msfjarvis.lobsters.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class LobstersPost(
@Json(name = "short_id")
val shortId: String,
@Json(name = "short_id_url")
val shortIdUrl: String,
@Json(name = "created_at")
val createdAt: String,
val title: String,
val url: String,
val score: Long,
val flags: Long,
@Json(name = "comment_count")
val commentCount: Long,
val description: String,
@Json(name = "comments_url")
val commentsUrl: String,
@Json(name = "submitter_user")
val submitterUser: Submitter,
val tags: List<String>,
)

View file

@ -1,7 +1,9 @@
package dev.msfjarvis.lobsters.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class Submitter(
val username: String,
@Json(name = "created_at")
@ -21,5 +23,5 @@ class Submitter(
@Json(name = "twitter_username")
val twitterUsername: String? = null,
@Json(name = "keybase_signatures")
val keybaseSignatures: List<KeybaseSignature> = emptyList()
val keybaseSignatures: List<KeybaseSignature> = emptyList(),
)

View file

@ -2,7 +2,6 @@ package dev.msfjarvis.lobsters.data.api
import com.squareup.moshi.Moshi
import dev.msfjarvis.lobsters.util.TestUtils
import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
import kotlinx.coroutines.runBlocking
import mockwebserver3.Dispatcher
import mockwebserver3.MockResponse
@ -24,7 +23,6 @@ class LobstersApiTest {
private val webServer = MockWebServer()
private val apiData = TestUtils.getJson("hottest.json")
private val moshi = Moshi.Builder()
.add(MetadataKotlinJsonAdapterFactory())
.build()
private val okHttp = OkHttpClient.Builder()
.build()
@ -63,7 +61,7 @@ class LobstersApiTest {
fun `no moderator posts in test data`() = runBlocking {
val posts = apiClient.getHottestPosts(1)
val moderatorPosts = posts.asSequence()
.filter { it.submitter_user.isModerator }
.filter { it.submitterUser.isModerator }
.toSet()
assertTrue(moderatorPosts.isEmpty())
}

View file

@ -54,7 +54,6 @@ dependencies {
implementation(Dependencies.Kotlin.Coroutines.android)
implementation(Dependencies.ThirdParty.accompanist)
implementation(Dependencies.ThirdParty.Moshi.lib)
implementation(Dependencies.ThirdParty.Moshi.moshiMetadataReflect)
implementation(Dependencies.ThirdParty.Retrofit.moshi)
implementation(Dependencies.ThirdParty.SQLDelight.androidDriver)
testImplementation(Dependencies.Testing.junit)

View file

@ -2,8 +2,8 @@ package dev.msfjarvis.lobsters.data.remote
import androidx.paging.PagingSource
import androidx.paging.PagingState
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
import dev.msfjarvis.lobsters.model.LobstersPost
import javax.inject.Inject
class LobstersPagingSource @Inject constructor(

View file

@ -1,7 +1,8 @@
package dev.msfjarvis.lobsters.data.repo
import dev.msfjarvis.lobsters.data.api.LobstersApi
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.database.LobstersDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@ -13,7 +14,7 @@ class LobstersRepository constructor(
private val lobstersDatabase: LobstersDatabase,
) {
private val savedPostsCache: MutableMap<String, LobstersPost> = mutableMapOf()
private val savedPostsCache: MutableMap<String, SavedPost> = mutableMapOf()
private val _isCacheReady = MutableStateFlow(false)
val isCacheReady = _isCacheReady.asStateFlow()
@ -21,7 +22,7 @@ class LobstersRepository constructor(
return savedPostsCache.containsKey(postId)
}
fun getAllPostsFromCache(): List<LobstersPost> {
fun getAllPostsFromCache(): List<SavedPost> {
return savedPostsCache.values.toList()
}
@ -31,29 +32,29 @@ class LobstersRepository constructor(
suspend fun updateCache() {
if (_isCacheReady.value) return
val posts = getAllPosts()
val posts = getSavedPosts()
posts.forEach {
savedPostsCache[it.short_id] = it
savedPostsCache[it.shortId] = it
}
_isCacheReady.value = true
}
private suspend fun getAllPosts(): List<LobstersPost> = withContext(Dispatchers.IO) {
return@withContext lobstersDatabase.postQueries.selectAllPosts().executeAsList()
private suspend fun getSavedPosts(): List<SavedPost> = withContext(Dispatchers.IO) {
return@withContext lobstersDatabase.savedPostQueries.selectAllPosts().executeAsList()
}
suspend fun addPost(post: LobstersPost) = withContext(Dispatchers.IO) {
if (!savedPostsCache.containsKey(post.short_id)) {
savedPostsCache.putIfAbsent(post.short_id, post)
lobstersDatabase.postQueries.insertOrReplacePost(post)
suspend fun addPost(post: SavedPost) = withContext(Dispatchers.IO) {
if (!savedPostsCache.containsKey(post.shortId)) {
savedPostsCache.putIfAbsent(post.shortId, post)
lobstersDatabase.savedPostQueries.insertOrReplacePost(post)
}
}
suspend fun removePost(post: LobstersPost) = withContext(Dispatchers.IO) {
if (savedPostsCache.containsKey(post.short_id)) {
savedPostsCache.remove(post.short_id)
lobstersDatabase.postQueries.deletePost(post.short_id)
suspend fun removePost(post: SavedPost) = withContext(Dispatchers.IO) {
if (savedPostsCache.containsKey(post.shortId)) {
savedPostsCache.remove(post.shortId)
lobstersDatabase.savedPostQueries.deletePost(post.shortId)
}
}
}

View file

@ -1,7 +1,6 @@
package dev.msfjarvis.lobsters.injection
import android.content.Context
import com.squareup.moshi.JsonAdapter
import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import dagger.Module
@ -10,10 +9,8 @@ import dagger.Reusable
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.database.LobstersDatabase
import dev.msfjarvis.lobsters.model.Submitter
import dev.msfjarvis.lobsters.model.SubmitterAdapter
import dev.msfjarvis.lobsters.model.TagsAdapter
import javax.inject.Singleton
@ -21,12 +18,6 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Reusable
fun providesSubmitterAdapter(jsonAdapter: JsonAdapter<Submitter>): SubmitterAdapter {
return SubmitterAdapter(jsonAdapter)
}
@Provides
@Reusable
fun providesTagsAdapter(): TagsAdapter {
@ -43,12 +34,11 @@ object DatabaseModule {
@Singleton
fun providesLobstersDatabase(
sqlDriver: SqlDriver,
submitterAdapter: SubmitterAdapter,
tagsAdapter: TagsAdapter
): LobstersDatabase {
return LobstersDatabase(
sqlDriver,
LobstersPost.Adapter(submitterAdapter, tagsAdapter)
SavedPost.Adapter(tagsAdapter),
)
}
}

View file

@ -1,15 +1,11 @@
package dev.msfjarvis.lobsters.injection
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import dagger.Module
import dagger.Provides
import dagger.Reusable
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.msfjarvis.lobsters.model.Submitter
import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
@Module
@InstallIn(SingletonComponent::class)
@ -18,14 +14,6 @@ object MoshiModule {
@Reusable
fun provideMoshi(): Moshi {
return Moshi.Builder()
.add(MetadataKotlinJsonAdapterFactory())
.build()
}
@OptIn(ExperimentalStdlibApi::class)
@Provides
@Reusable
fun provideSubmitterJsonAdapter(moshi: Moshi): JsonAdapter<Submitter> {
return moshi.adapter()
}
}

View file

@ -11,8 +11,10 @@ import androidx.compose.ui.Modifier
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.items
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.ui.urllauncher.LocalUrlLauncher
import dev.msfjarvis.lobsters.util.toDbModel
@Composable
fun HottestPosts(
@ -20,7 +22,7 @@ fun HottestPosts(
listState: LazyListState,
isPostSaved: (String) -> Boolean,
modifier: Modifier = Modifier,
saveAction: (LobstersPost) -> Unit,
saveAction: (SavedPost) -> Unit,
) {
val urlLauncher = LocalUrlLauncher.current
@ -33,13 +35,15 @@ fun HottestPosts(
) {
items(posts) { item ->
if (item != null) {
var isSaved by remember(item.short_id) { mutableStateOf(isPostSaved(item.short_id)) }
@Suppress("NAME_SHADOWING")
val item = item.toDbModel()
var isSaved by remember(item.shortId) { mutableStateOf(isPostSaved(item.shortId)) }
LobstersItem(
post = item,
isSaved = isSaved,
onClick = { urlLauncher.launch(item.url.ifEmpty { item.comments_url }) },
onLongClick = { urlLauncher.launch(item.comments_url) },
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
onLongClick = { urlLauncher.launch(item.commentsUrl) },
onSaveButtonClick = {
isSaved = isSaved.not()
saveAction.invoke(item)

View file

@ -29,43 +29,26 @@ import coil.transform.CircleCropTransformation
import dev.chrisbanes.accompanist.coil.CoilImage
import dev.msfjarvis.lobsters.R
import dev.msfjarvis.lobsters.data.api.LobstersApi
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.model.Submitter
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.ui.theme.LobstersTheme
import dev.msfjarvis.lobsters.ui.theme.titleColor
import dev.msfjarvis.lobsters.util.IconResource
val TEST_POST = LobstersPost(
"zqyydb",
"https://lobste.rs/s/zqyydb",
"2020-09-21T07:11:14.000-05:00",
"k2k20 hackathon report: Bob Beck on LibreSSL progress",
"https://undeadly.org/cgi?action=article;sid=20200921105847",
4,
0,
0,
"",
"https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
Submitter(
"Vigdis",
"2017-02-27T21:08:14.000-06:00",
false,
"Alleycat for the fun, sys/net admin for a living and OpenBSD contributions for the pleasure. (Not so) French dude in Montreal\r\n\r\nhttps://chown.me",
false,
76,
"/avatars/Vigdis-100.png",
"sevan",
null,
null,
emptyList(),
),
listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"),
val TEST_POST = SavedPost(
shortId = "zqyydb",
title = "k2k20 hackathon report: Bob Beck on LibreSSL progress",
url = "https://undeadly.org/cgi?action=article;sid=20200921105847",
createdAt = "2020-09-21T07:11:14.000-05:00",
commentsUrl = "https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
submitterName = "Vigdis",
submitterAvatarUrl = "/avatars/Vigdis-100.png",
tags = listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"),
)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LobstersItem(
post: LobstersPost,
post: SavedPost,
isSaved: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
@ -99,10 +82,10 @@ fun LobstersItem(
)
Row {
CoilImage(
data = "${LobstersApi.BASE_URL}/${post.submitter_user.avatarUrl}",
data = "${LobstersApi.BASE_URL}/${post.submitterAvatarUrl}",
contentDescription = stringResource(
R.string.avatar_content_description,
post.submitter_user.username
post.submitterName
),
fadeIn = true,
requestBuilder = {
@ -113,7 +96,7 @@ fun LobstersItem(
.padding(4.dp),
)
Text(
text = stringResource(id = R.string.submitted_by, post.submitter_user.username),
text = stringResource(id = R.string.submitted_by, post.submitterName),
modifier = Modifier
.padding(4.dp),
)

View file

@ -6,16 +6,16 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.ui.urllauncher.LocalUrlLauncher
import dev.msfjarvis.lobsters.util.asZonedDateTime
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SavedPosts(
posts: List<LobstersPost>,
posts: List<SavedPost>,
modifier: Modifier = Modifier,
saveAction: (LobstersPost) -> Unit,
saveAction: (SavedPost) -> Unit,
) {
val listState = rememberLazyListState()
val urlLauncher = LocalUrlLauncher.current
@ -27,7 +27,7 @@ fun SavedPosts(
state = listState,
modifier = Modifier.then(modifier),
) {
val grouped = posts.groupBy { it.created_at.asZonedDateTime().month }
val grouped = posts.groupBy { it.createdAt.asZonedDateTime().month }
grouped.forEach { (month, posts) ->
stickyHeader {
MonthHeader(month = month)
@ -36,8 +36,8 @@ fun SavedPosts(
LobstersItem(
post = item,
isSaved = true,
onClick = { urlLauncher.launch(item.url.ifEmpty { item.comments_url }) },
onLongClick = { urlLauncher.launch(item.comments_url) },
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
onLongClick = { urlLauncher.launch(item.commentsUrl) },
onSaveButtonClick = { saveAction.invoke(item) },
)
}

View file

@ -6,7 +6,7 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.msfjarvis.lobsters.data.local.LobstersPost
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.data.remote.LobstersPagingSource
import dev.msfjarvis.lobsters.data.repo.LobstersRepository
import javax.inject.Inject
@ -21,7 +21,7 @@ class LobstersViewModel @Inject constructor(
private val lobstersRepository: LobstersRepository,
private val pagingSource: LobstersPagingSource,
) : ViewModel() {
private val _savedPosts = MutableStateFlow<List<LobstersPost>>(emptyList())
private val _savedPosts = MutableStateFlow<List<SavedPost>>(emptyList())
val savedPosts = _savedPosts.asStateFlow()
val posts = Pager(PagingConfig(25)) {
pagingSource
@ -35,9 +35,9 @@ class LobstersViewModel @Inject constructor(
}.launchIn(viewModelScope)
}
fun toggleSave(post: LobstersPost) {
fun toggleSave(post: SavedPost) {
viewModelScope.launch {
val isSaved = lobstersRepository.isPostSaved(post.short_id)
val isSaved = lobstersRepository.isPostSaved(post.shortId)
if (isSaved) removeSavedPost(post) else savePost(post)
}
}
@ -46,14 +46,14 @@ class LobstersViewModel @Inject constructor(
return lobstersRepository.isPostSaved(postId)
}
private fun savePost(post: LobstersPost) {
private fun savePost(post: SavedPost) {
viewModelScope.launch {
lobstersRepository.addPost(post)
_savedPosts.value = lobstersRepository.getAllPostsFromCache()
}
}
private fun removeSavedPost(post: LobstersPost) {
private fun removeSavedPost(post: SavedPost) {
viewModelScope.launch {
lobstersRepository.removePost(post)
_savedPosts.value = lobstersRepository.getAllPostsFromCache()

View file

@ -0,0 +1,20 @@
package dev.msfjarvis.lobsters.util
import dev.msfjarvis.lobsters.data.local.SavedPost
import dev.msfjarvis.lobsters.model.LobstersPost
/**
* Convert a [LobstersPost] object returned by the API into a [SavedPost] for persistence.
*/
fun LobstersPost.toDbModel(): SavedPost {
return SavedPost(
shortId = shortId,
title = title,
url = url,
createdAt = createdAt,
commentsUrl = commentsUrl,
submitterName = submitterUser.username,
submitterAvatarUrl = submitterUser.avatarUrl,
tags = tags,
)
}

View file

@ -1,3 +1,12 @@
plugins {
`lobsters-plugin`
}
subprojects {
configurations.configureEach {
resolutionStrategy {
// Retrofit depends on a very old version of Moshi that causes moshi-ksp to fail
force(Dependencies.ThirdParty.Moshi.lib)
}
}
}

View file

@ -71,7 +71,7 @@ object Dependencies {
private const val version = "1.11.0"
const val lib = "com.squareup.moshi:moshi:$version"
const val moshiMetadataReflect = "dev.zacsweers.moshix:moshi-metadata-reflect:0.9.1"
const val ksp = "dev.zacsweers.moshix:moshi-ksp:0.9.1"
}
object Retrofit {

View file

@ -5,8 +5,6 @@ plugins {
}
dependencies {
implementation(Dependencies.ThirdParty.Moshi.lib)
implementation(Dependencies.ThirdParty.Moshi.moshiMetadataReflect)
testImplementation(Dependencies.Kotlin.Coroutines.core)
testImplementation(Dependencies.ThirdParty.SQLDelight.jvmDriver)
testImplementation(Dependencies.Testing.junit)

View file

@ -1,16 +0,0 @@
package dev.msfjarvis.lobsters.model
import com.squareup.moshi.JsonAdapter
import com.squareup.sqldelight.ColumnAdapter
class SubmitterAdapter(private val submitterJsonAdapter: JsonAdapter<Submitter>) :
ColumnAdapter<Submitter, String> {
override fun decode(databaseValue: String): Submitter {
return submitterJsonAdapter.fromJson(databaseValue)!!
}
override fun encode(value: Submitter): String {
return submitterJsonAdapter.toJson(value)
}
}

View file

@ -1,45 +0,0 @@
import dev.msfjarvis.lobsters.model.Submitter;
import java.lang.Boolean;
import kotlin.collections.List;
CREATE TABLE IF NOT EXISTS LobstersPost(
short_id TEXT NOT NULL PRIMARY KEY,
short_id_url TEXT NOT NULL,
created_at TEXT NOT NULL,
title TEXT NOT NULL,
url TEXT NOT NULL,
score INTEGER NOT NULL,
flags INTEGER NOT NULL,
comment_count INTEGER NOT NULL,
description TEXT NOT NULL,
comments_url TEXT NOT NULL,
submitter_user TEXT as Submitter NOT NULL,
tags TEXT as List<String> NOT NULL
);
selectPost:
SELECT *
FROM LobstersPost
WHERE short_id = ?;
selectAllPosts:
SELECT *
FROM LobstersPost;
insertOrReplacePost:
INSERT OR REPLACE
INTO LobstersPost
VALUES ?;
deletePost:
DELETE
FROM LobstersPost
WHERE short_id = ?;
deleteAllPosts:
DELETE
FROM LobstersPost;
selectCount:
SELECT COUNT(*)
FROM LobstersPost;

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

@ -1,13 +1,8 @@
package dev.msfjarvis.lobsters.data.local
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
import dev.msfjarvis.lobsters.database.LobstersDatabase
import dev.msfjarvis.lobsters.model.Submitter
import dev.msfjarvis.lobsters.model.SubmitterAdapter
import dev.msfjarvis.lobsters.model.TagsAdapter
import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
@ -16,19 +11,17 @@ import org.junit.Test
@OptIn(ExperimentalStdlibApi::class)
class SqlDelightQueriesTest {
private lateinit var postQueries: PostQueries
private lateinit var postQueries: SavedPostQueries
@Before
fun setUp() {
val moshi = Moshi.Builder().add(MetadataKotlinJsonAdapterFactory()).build()
val submitterJsonAdapter = moshi.adapter<Submitter>()
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
LobstersDatabase.Schema.create(driver)
val database = LobstersDatabase(
driver,
LobstersPost.Adapter(SubmitterAdapter(submitterJsonAdapter), TagsAdapter())
SavedPost.Adapter(TagsAdapter()),
)
postQueries = database.postQueries
postQueries = database.savedPostQueries
}
@Test
@ -63,7 +56,7 @@ class SqlDelightQueriesTest {
postQueries.insertOrReplacePost(post)
// Create a new post and try replacing it
val newPost = post.copy(comment_count = 100)
val newPost = post.copy(submitterName = "Fake name")
postQueries.insertOrReplacePost(newPost)
// Check post count
@ -71,8 +64,8 @@ class SqlDelightQueriesTest {
assertEquals(1, postsCount)
// Check if post is updated
val postFromDb = postQueries.selectPost(post.short_id).executeAsOne()
assertEquals(100, postFromDb.comment_count)
val postFromDb = postQueries.selectPost(post.shortId).executeAsOne()
assertEquals("Fake name", postFromDb.submitterName)
}
@Test
@ -84,7 +77,7 @@ class SqlDelightQueriesTest {
postQueries.insertOrReplacePost(post)
val postFromDb = postQueries.selectAllPosts().executeAsOne()
assertEquals("test_id_1", postFromDb.short_id)
assertEquals("test_id_1", postFromDb.shortId)
}
@Test
@ -97,9 +90,9 @@ class SqlDelightQueriesTest {
val postsFromDb = postQueries.selectAllPosts().executeAsList()
// Check if all posts have correct short_id
// Check if all posts have correct shortId
for (i in 1..5) {
assertEquals("test_id_$i", postsFromDb[i - 1].short_id)
assertEquals("test_id_$i", postsFromDb[i - 1].shortId)
}
}
@ -116,8 +109,8 @@ class SqlDelightQueriesTest {
// Check if size is 2, and only the correct post is deleted
assertEquals(2, postsFromDB.size)
assertEquals("test_id_1", postsFromDB[0].short_id)
assertEquals("test_id_3", postsFromDB[1].short_id)
assertEquals("test_id_1", postsFromDB[0].shortId)
assertEquals("test_id_3", postsFromDB[1].shortId)
}
@Test
@ -136,32 +129,18 @@ class SqlDelightQueriesTest {
}
private fun createTestData(count: Int): ArrayList<LobstersPost> {
val posts = arrayListOf<LobstersPost>()
private fun createTestData(count: Int): ArrayList<SavedPost> {
val posts = arrayListOf<SavedPost>()
for (i in 1..count) {
val submitter = Submitter(
username = "test_user_$i",
val post = SavedPost(
shortId = "test_id_$i",
createdAt = "0",
about = "test",
avatarUrl = "test_avatar_url",
invitedByUser = "test_user",
isAdmin = false,
isModerator = false
)
val post = LobstersPost(
short_id = "test_id_$i",
short_id_url = "test_id_url",
created_at = "0",
title = "test",
url = "test_url",
score = 0,
flags = 0,
comment_count = 0,
description = "test",
comments_url = "test_comments_url",
submitter_user = submitter,
commentsUrl = "test_comments_url",
submitterName = "test_user_$i",
submitterAvatarUrl = "test_avatar_url",
tags = listOf(),
)

View file

@ -1,3 +1,9 @@
rootProject.name = "Claw"
include(":app", ":api", ":database")
enableFeaturePreview("GRADLE_METADATA")
pluginManagement {
repositories {
google()
gradlePluginPortal()
}
}