use string tag in domain layer
This commit is contained in:
+18
-20
@@ -1,11 +1,13 @@
|
||||
package ru.shadowsparky.vbox.backend.data
|
||||
|
||||
import app.cash.sqldelight.coroutines.asFlow
|
||||
import app.cash.sqldelight.coroutines.mapToList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.shadowsparky.backend.data.Logger
|
||||
import ru.shadowsparky.vbox.backend.AppDatabase
|
||||
import ru.shadowsparky.vbox.server.Saved_movie
|
||||
import ru.shadowsparky.vbox.shared.di.factory.DispatcherProvider
|
||||
import ru.shadowsparky.vbox.shared.domain.RemoteEvent
|
||||
import ru.shadowsparky.vbox.shared.domain.RemoteEventHandler
|
||||
@@ -23,26 +25,22 @@ class BackendSavedMovieRepository(
|
||||
) : SavedMovieRepository {
|
||||
|
||||
override fun getAll(): Flow<List<VideoDetails>> {
|
||||
val request = db.saved_movieQueries.selectByUserId(userId)
|
||||
return flow {
|
||||
emit(request.executeAsList().parse().reversed())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun List<Saved_movie>.parse(): List<VideoDetails> =
|
||||
withContext(dispatcherProvider.io) {
|
||||
mapNotNull {
|
||||
val movie = db.movieQueries.selectMovie(it.movie_id)
|
||||
.executeAsOneOrNull() ?: return@mapNotNull null
|
||||
VideoDetails(
|
||||
movie.movie_id,
|
||||
movie.poster_url,
|
||||
movie.description,
|
||||
movie.title,
|
||||
(movie.flags and SERIAL_FLAG) != 0
|
||||
)
|
||||
return db.saved_movieQueries
|
||||
.selectDetailsByUserId(userId)
|
||||
.asFlow()
|
||||
.mapToList(dispatcherProvider.io)
|
||||
.map { list ->
|
||||
list.map { movie ->
|
||||
VideoDetails(
|
||||
id = movie.movie_id,
|
||||
poster = movie.poster_url,
|
||||
desc = movie.description,
|
||||
title = movie.title,
|
||||
isSerial = (movie.flags and SERIAL_FLAG) != 0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun save(details: VideoDetails) {
|
||||
logger.debug(TAG, "save(${details.id}) ${details.title}")
|
||||
|
||||
+24
-9
@@ -3,10 +3,14 @@ package ru.shadowsparky.vbox.backend.data.tags
|
||||
import app.cash.sqldelight.coroutines.asFlow
|
||||
import app.cash.sqldelight.coroutines.mapToList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.annotation.Factory
|
||||
import org.koin.core.annotation.Named
|
||||
import ru.shadowsparky.domain.DispatcherProvider
|
||||
import ru.shadowsparky.http.domain.BadRequestException
|
||||
import ru.shadowsparky.vbox.backend.AppDatabase
|
||||
import ru.shadowsparky.vbox.shared.domain.EventType
|
||||
import ru.shadowsparky.vbox.shared.domain.RemoteEvent
|
||||
@@ -46,19 +50,30 @@ class BackendMovieTagRepository(
|
||||
list.map { row -> TagInfo(id = row.id, tag = row.tag) }
|
||||
}
|
||||
|
||||
override fun queryMovies(tagId: Long): Flow<Set<Long>> = dbQueries
|
||||
.getMoviesByTagId(tagId, userId)
|
||||
.asFlow()
|
||||
.mapToList(dispatcherProvider.io)
|
||||
.map { list -> list.toSet() }
|
||||
override fun queryMovies(tag: String): Flow<Set<Long>> {
|
||||
return flow {
|
||||
val id = findTagId(tag)
|
||||
emitAll(
|
||||
dbQueries.getMoviesByTagId(id, userId)
|
||||
.asFlow()
|
||||
.mapToList(dispatcherProvider.io)
|
||||
.map { list -> list.toSet() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun link(movieId: Long, tagId: Long) {
|
||||
dbQueries.linkMovieTag(movieId, tagId, userId).await()
|
||||
override suspend fun link(movieId: Long, tag: String) {
|
||||
dbQueries.linkMovieTag(movieId, findTagId(tag), userId).await()
|
||||
movieEventHandler.notify(RemoteEvent.OnMovieTag(userId, movieId))
|
||||
}
|
||||
|
||||
override suspend fun unlink(movieId: Long, tagId: Long) {
|
||||
dbQueries.unlinkMovieTag(movieId, tagId, userId).await()
|
||||
override suspend fun unlink(movieId: Long, tag: String) {
|
||||
dbQueries.unlinkMovieTag(movieId, findTagId(tag), userId).await()
|
||||
movieEventHandler.notify(RemoteEvent.OnMovieTag(userId, movieId))
|
||||
}
|
||||
|
||||
private suspend fun findTagId(tag: String): Long = withContext(dispatcherProvider.io) {
|
||||
db.tagsQueries.findTagId(tag, userId).executeAsOneOrNull()
|
||||
?: throw BadRequestException("tag $tag not found for user with id $userId")
|
||||
}
|
||||
}
|
||||
|
||||
+4
-12
@@ -4,7 +4,6 @@ import app.cash.sqldelight.coroutines.asFlow
|
||||
import app.cash.sqldelight.coroutines.mapToList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.annotation.Factory
|
||||
import org.koin.core.annotation.Named
|
||||
import ru.shadowsparky.vbox.backend.AppDatabase
|
||||
@@ -53,20 +52,13 @@ class BackendUserTagRepository(
|
||||
userTagEventHandler.notify(RemoteEvent.OnUserTag(userId))
|
||||
}
|
||||
|
||||
override suspend fun edit(id: Long, newTag: String) {
|
||||
checkOwner(id)
|
||||
queries.updateTagText(newTag, id).await()
|
||||
override suspend fun edit(oldTag: String, newTag: String) {
|
||||
queries.updateTagText(newTag, oldTag, userId).await()
|
||||
userTagEventHandler.notify(RemoteEvent.OnUserTag(userId))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
checkOwner(id)
|
||||
queries.deleteTagById(id).await()
|
||||
override suspend fun delete(tag: String) {
|
||||
queries.deleteTagById(tag, userId).await()
|
||||
userTagEventHandler.notify(RemoteEvent.OnUserTag(userId))
|
||||
}
|
||||
|
||||
private suspend fun checkOwner(id: Long) = withContext(dispatcherProvider.io) {
|
||||
val ownerId = queries.getUserIdByTagId(id).executeAsOneOrNull()
|
||||
check(ownerId == userId) { "tag owner mismatch. owner: $ownerId, current user: $userId" }
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -7,6 +7,7 @@ import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.delete
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.post
|
||||
import io.ktor.server.util.getOrFail
|
||||
import kotlinx.coroutines.flow.first
|
||||
import ru.shadowsparky.vbox.backend.data.tags.MovieTagRepositoryFactory
|
||||
import ru.shadowsparky.vbox.backend.data.tags.UserTagRepositoryFactory
|
||||
@@ -41,12 +42,12 @@ private fun Route.setupUserTag(userTagFactory: UserTagRepositoryFactory) {
|
||||
post(UserTagRepository.EDIT) {
|
||||
val userTag = userTagFactory.create(call.obtainUserId())
|
||||
val edit = call.receive<EditRequest>()
|
||||
userTag.edit(edit.id, edit.newTag)
|
||||
userTag.edit(edit.oldTag, edit.newTag)
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
delete(UserTagRepository.DELETE) {
|
||||
val userTag = userTagFactory.create(call.obtainUserId())
|
||||
userTag.delete(call.receive<DeleteRequest>().id)
|
||||
userTag.delete(call.receive<DeleteRequest>().tag)
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
@@ -54,26 +55,26 @@ private fun Route.setupUserTag(userTagFactory: UserTagRepositoryFactory) {
|
||||
private fun Route.setupMovieTag(movieTagFactory: MovieTagRepositoryFactory) {
|
||||
get(MovieTagRepository.QUERY) {
|
||||
val movieTag = movieTagFactory.create(call.obtainUserId())
|
||||
val movieId = call.parameters[MovieTagRepository.MOVIE_ID]?.toLong()
|
||||
val tags = movieTag.query(checkNotNull(movieId) { "movieId required" }).first()
|
||||
val movieId = call.parameters.getOrFail<Long>(MovieTagRepository.MOVIE_ID)
|
||||
val tags = movieTag.query(movieId).first()
|
||||
call.respond(TagResponse(tags))
|
||||
}
|
||||
get(MovieTagRepository.QUERY_MOVIES) {
|
||||
val movieTag = movieTagFactory.create(call.obtainUserId())
|
||||
val tagId = call.parameters[MovieTagRepository.TAG_ID]?.toLong()
|
||||
val ids = movieTag.queryMovies(checkNotNull(tagId) { "tagId required" }).first()
|
||||
val tagName = call.parameters.getOrFail(MovieTagRepository.TAG_NAME)
|
||||
val ids = movieTag.queryMovies(tagName).first()
|
||||
call.respond(LinkedMovies(ids))
|
||||
}
|
||||
post(MovieTagRepository.LINK) {
|
||||
val movieTag = movieTagFactory.create(call.obtainUserId())
|
||||
val link = call.receive<LinkRequest>()
|
||||
movieTag.link(link.movieId, link.tagId)
|
||||
movieTag.link(link.movieId, link.tag)
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
delete(MovieTagRepository.UNLINK) {
|
||||
val movieTag = movieTagFactory.create(call.obtainUserId())
|
||||
val link = call.receive<LinkRequest>()
|
||||
movieTag.unlink(link.movieId, link.tagId)
|
||||
movieTag.unlink(link.movieId, link.tag)
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,3 +11,15 @@ addSavedMovie:
|
||||
|
||||
removeSavedMovie:
|
||||
DELETE FROM "saved_movie" WHERE user_id = ? AND movie_id = ?;
|
||||
|
||||
selectDetailsByUserId:
|
||||
SELECT
|
||||
m.movie_id,
|
||||
m.poster_url,
|
||||
m.description,
|
||||
m.title,
|
||||
m.flags
|
||||
FROM saved_movie AS sm
|
||||
JOIN movie AS m ON sm.movie_id = m.movie_id
|
||||
WHERE sm.user_id = ?
|
||||
ORDER BY sm.saved_movie_id DESC;
|
||||
|
||||
@@ -3,11 +3,6 @@ SELECT id, user_id, tag
|
||||
FROM user_tag
|
||||
WHERE user_id = ?;
|
||||
|
||||
getUserIdByTagId:
|
||||
SELECT user_id
|
||||
FROM user_tag
|
||||
WHERE id = ?;
|
||||
|
||||
insertUserTag:
|
||||
INSERT INTO user_tag (user_id, tag)
|
||||
VALUES (?, ?);
|
||||
@@ -15,11 +10,15 @@ VALUES (?, ?);
|
||||
updateTagText:
|
||||
UPDATE user_tag
|
||||
SET tag = ?
|
||||
WHERE id = ?;
|
||||
WHERE tag = ? AND user_id = ?;
|
||||
|
||||
deleteTagById:
|
||||
DELETE FROM user_tag
|
||||
WHERE id = ?;
|
||||
WHERE tag = ? AND user_id = ?;
|
||||
|
||||
findTagId:
|
||||
SELECT id FROM user_tag
|
||||
WHERE tag = ? AND user_id = ?;
|
||||
|
||||
getTagsForMovie:
|
||||
SELECT ut.id, ut.tag
|
||||
|
||||
Reference in New Issue
Block a user