mirror of https://github.com/usbharu/Hideout.git
commit
b3a9be96c5
|
@ -67,3 +67,4 @@ tasks.register("run") {
|
|||
springBoot {
|
||||
mainClass = "dev.usbharu.hideout.SpringApplicationKt"
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: "postgres"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
POSTGRES_DB: "hideout"
|
|
@ -86,7 +86,6 @@ dependencies {
|
|||
implementation(libs.bundles.owl.broker)
|
||||
implementation(libs.bundles.spring.boot.oauth2)
|
||||
implementation(libs.bundles.spring.boot.data.mongodb)
|
||||
implementation(libs.bundles.spring.boot.data.mongodb)
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
|
|
|
@ -63,6 +63,9 @@ class RegisterLocalActorApplicationService(
|
|||
id = UserDetailId(idGenerateService.generateId()),
|
||||
actorId = actor.id,
|
||||
password = userDetailDomainService.hashPassword(command.password),
|
||||
autoAcceptFolloweeFollowRequest = false,
|
||||
lastMigration = null,
|
||||
homeTimelineId = null
|
||||
)
|
||||
userDetailRepository.save(userDetail)
|
||||
return actor.url
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
interface DomainEventSubscriber {
|
||||
fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>)
|
||||
}
|
||||
|
||||
typealias DomainEventConsumer<T> = suspend (DomainEvent<T>) -> Unit
|
|
@ -0,0 +1,3 @@
|
|||
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||
|
||||
interface Subscriber
|
|
@ -0,0 +1,11 @@
|
|||
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||
|
||||
import org.springframework.boot.ApplicationArguments
|
||||
import org.springframework.boot.ApplicationRunner
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class SubscriberRunner(subscribers: List<Subscriber>) : ApplicationRunner {
|
||||
override fun run(args: ApplicationArguments?) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||
|
||||
import dev.usbharu.hideout.core.domain.event.post.PostEvent
|
||||
import dev.usbharu.hideout.core.domain.event.post.PostEventBody
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class TimelinePostCreateSubscriber(domainEventSubscriber: DomainEventSubscriber) : Subscriber {
|
||||
init {
|
||||
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) {
|
||||
val post = it.body.getPost()
|
||||
val actor = it.body.getActor()
|
||||
|
||||
logger.info("New Post! : {}", post)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(TimelinePostCreateSubscriber::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||
|
||||
import dev.usbharu.hideout.core.application.shared.DomainEventCommandExecutor
|
||||
import dev.usbharu.hideout.core.application.shared.UserDetailGettableCommandExecutor
|
||||
import dev.usbharu.hideout.core.application.timeline.AddTimelineRelationship
|
||||
import dev.usbharu.hideout.core.application.timeline.UserAddTimelineRelationshipApplicationService
|
||||
import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent
|
||||
import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class TimelineRelationshipFollowSubscriber(
|
||||
private val userAddTimelineRelationshipApplicationService: UserAddTimelineRelationshipApplicationService,
|
||||
private val idGenerateService: IdGenerateService,
|
||||
private val userDetailRepository: UserDetailRepository,
|
||||
domainEventSubscriber: DomainEventSubscriber
|
||||
) : Subscriber {
|
||||
|
||||
init {
|
||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.FOLLOW.eventName) {
|
||||
val relationship = it.body.getRelationship()
|
||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw Exception()
|
||||
if (userDetail.homeTimelineId == null) {
|
||||
logger.warn("Home timeline for ${relationship.actorId} is not found")
|
||||
return@subscribe
|
||||
}
|
||||
userAddTimelineRelationshipApplicationService.execute(
|
||||
AddTimelineRelationship(
|
||||
TimelineRelationship(
|
||||
TimelineRelationshipId(idGenerateService.generateId()),
|
||||
userDetail.homeTimelineId,
|
||||
relationship.targetActorId,
|
||||
Visible.FOLLOWERS
|
||||
)
|
||||
), DomainEventCommandExecutor("", object : UserDetailGettableCommandExecutor {
|
||||
override val userDetailId: Long
|
||||
get() = userDetail.id.id
|
||||
override val executor: String
|
||||
get() = userDetail.id.id.toString()
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(TimelineRelationshipFollowSubscriber::class.java)
|
||||
}
|
||||
|
||||
}
|
|
@ -23,3 +23,8 @@ interface CommandExecutor {
|
|||
interface UserDetailGettableCommandExecutor : CommandExecutor {
|
||||
val userDetailId: Long
|
||||
}
|
||||
|
||||
data class DomainEventCommandExecutor(
|
||||
override val executor: String,
|
||||
val commandExecutor: CommandExecutor?
|
||||
) : CommandExecutor
|
|
@ -0,0 +1,7 @@
|
|||
package dev.usbharu.hideout.core.application.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||
|
||||
data class AddTimelineRelationship(
|
||||
val timelineRelationship: TimelineRelationship
|
||||
)
|
|
@ -0,0 +1,26 @@
|
|||
package dev.usbharu.hideout.core.application.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||
import dev.usbharu.hideout.core.application.shared.CommandExecutor
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class UserAddTimelineRelationshipApplicationService(
|
||||
private val timelineRelationshipRepository: TimelineRelationshipRepository,
|
||||
transaction: Transaction
|
||||
) :
|
||||
AbstractApplicationService<AddTimelineRelationship, Unit>(
|
||||
transaction, logger
|
||||
) {
|
||||
override suspend fun internalExecute(command: AddTimelineRelationship, executor: CommandExecutor) {
|
||||
timelineRelationshipRepository.save(command.timelineRelationship)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(UserAddTimelineRelationshipApplicationService::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
@ConfigurationProperties("hideout.timeline.default")
|
||||
data class DefaultTimelineStoreConfig(
|
||||
val actorPostsCount: Int = 500
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
class FlywayConfig {
|
||||
@Bean
|
||||
fun cleanMigrateStrategy(): FlywayMigrationStrategy {
|
||||
return FlywayMigrationStrategy { migrate ->
|
||||
migrate.repair()
|
||||
migrate.migrate()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
|||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
class ActorDomainEventFactory(private val actor: Actor) {
|
||||
fun createEvent(actorEvent: ActorEvent): DomainEvent {
|
||||
fun createEvent(actorEvent: ActorEvent): DomainEvent<ActorEventBody> {
|
||||
return DomainEvent.create(
|
||||
actorEvent.eventName,
|
||||
ActorEventBody(actor),
|
||||
|
|
|
@ -21,7 +21,9 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
|||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) {
|
||||
fun createEvent(actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent): DomainEvent {
|
||||
fun createEvent(
|
||||
actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent
|
||||
): DomainEvent<ActorInstanceRelationshipEventBody> {
|
||||
return DomainEvent.create(
|
||||
actorInstanceRelationshipEvent.eventName,
|
||||
ActorInstanceRelationshipEventBody(actorInstanceRelationship)
|
||||
|
|
|
@ -21,7 +21,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
|||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
class InstanceEventFactory(private val instance: Instance) {
|
||||
fun createEvent(event: InstanceEvent): DomainEvent {
|
||||
fun createEvent(event: InstanceEvent): DomainEvent<InstanceEventBody> {
|
||||
return DomainEvent.create(
|
||||
event.eventName,
|
||||
InstanceEventBody(instance)
|
||||
|
|
|
@ -22,7 +22,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
|||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
class PostDomainEventFactory(private val post: Post, private val actor: Actor? = null) {
|
||||
fun createEvent(postEvent: PostEvent): DomainEvent {
|
||||
fun createEvent(postEvent: PostEvent): DomainEvent<PostEventBody> {
|
||||
return DomainEvent.create(
|
||||
postEvent.eventName,
|
||||
PostEventBody(post, actor)
|
||||
|
@ -30,7 +30,10 @@ class PostDomainEventFactory(private val post: Post, private val actor: Actor? =
|
|||
}
|
||||
}
|
||||
|
||||
class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor))
|
||||
class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor)) {
|
||||
fun getPost(): Post = toMap()["post"] as Post
|
||||
fun getActor(): Actor? = toMap()["actor"] as Actor?
|
||||
}
|
||||
|
||||
enum class PostEvent(val eventName: String) {
|
||||
DELETE("PostDelete"),
|
||||
|
|
|
@ -21,11 +21,15 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
|||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
class RelationshipEventFactory(private val relationship: Relationship) {
|
||||
fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent =
|
||||
fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent<RelationshipEventBody> =
|
||||
DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship))
|
||||
}
|
||||
|
||||
class RelationshipEventBody(relationship: Relationship) : DomainEventBody(mapOf("relationship" to relationship))
|
||||
class RelationshipEventBody(relationship: Relationship) : DomainEventBody(mapOf("relationship" to relationship)) {
|
||||
fun getRelationship(): Relationship {
|
||||
return toMap()["relationship"] as Relationship
|
||||
}
|
||||
}
|
||||
|
||||
enum class RelationshipEvent(val eventName: String) {
|
||||
FOLLOW("RelationshipFollow"),
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package dev.usbharu.hideout.core.domain.event.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
|
||||
class TimelineEventFactory(private val timeline: Timeline) {
|
||||
fun createEvent(timelineEvent: TimelineEvent): DomainEvent<TimelineEventBody> =
|
||||
DomainEvent.create(timelineEvent.eventName, TimelineEventBody(timeline))
|
||||
}
|
||||
|
||||
class TimelineEventBody(timeline: Timeline) : DomainEventBody(mapOf("timeline" to timeline))
|
||||
|
||||
enum class TimelineEvent(val eventName: String) {
|
||||
CHANGE_VISIBILITY("ChangeVisibility")
|
||||
}
|
|
@ -21,7 +21,7 @@ import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.*
|
|||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
|
||||
import java.net.URI
|
||||
import java.time.Instant
|
||||
|
|
|
@ -21,4 +21,5 @@ interface ActorRepository {
|
|||
suspend fun delete(actor: Actor)
|
||||
suspend fun findById(id: ActorId): Actor?
|
||||
suspend fun findByNameAndDomain(name: String, domain: String): Actor?
|
||||
suspend fun findAllById(actorIds: List<ActorId>): List<Actor>
|
||||
}
|
||||
|
|
|
@ -20,5 +20,5 @@ enum class Role {
|
|||
LOCAL,
|
||||
MODERATOR,
|
||||
ADMINISTRATOR,
|
||||
REMOTE;
|
||||
REMOTE
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package dev.usbharu.hideout.core.domain.model.emoji
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import java.net.URI
|
||||
import java.time.Instant
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
|
||||
|
||||
class FilterName(name: String) {
|
||||
|
||||
|
||||
val name = name.take(LENGTH)
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
|
||||
interface FilterRepository {
|
||||
suspend fun save(filter: Filter): Filter
|
||||
suspend fun delete(filter: Filter)
|
||||
|
||||
suspend fun findByFilterKeywordId(filterKeywordId: FilterKeywordId): Filter?
|
||||
suspend fun findByFilterId(filterId: FilterId): Filter?
|
||||
|
||||
suspend fun findByUserDetailId(userDetailId: UserDetailId): List<Filter>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package dev.usbharu.hideout.core.domain.model.followtimeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
|
||||
class FollowTimeline(val userDetailId: UserDetailId, val timelineId: TimelineId)
|
|
@ -0,0 +1,6 @@
|
|||
package dev.usbharu.hideout.core.domain.model.followtimeline
|
||||
|
||||
interface FollowTimelineRepository {
|
||||
suspend fun save(followTimeline: FollowTimeline): FollowTimeline
|
||||
suspend fun delete(followTimeline: FollowTimeline)
|
||||
}
|
|
@ -22,6 +22,7 @@ import dev.usbharu.hideout.core.domain.model.actor.Actor
|
|||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Role
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post.Companion.Action.*
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
|
||||
|
@ -32,6 +33,7 @@ import java.time.Instant
|
|||
class Post(
|
||||
val id: PostId,
|
||||
actorId: ActorId,
|
||||
val instanceId: InstanceId,
|
||||
overview: PostOverview?,
|
||||
content: PostContent,
|
||||
val createdAt: Instant,
|
||||
|
@ -227,6 +229,7 @@ class Post(
|
|||
return Post(
|
||||
id = id,
|
||||
actorId = actorId,
|
||||
instanceId = instanceId,
|
||||
overview = overview,
|
||||
content = PostContent(this.content.text, this.content.content, emojis),
|
||||
createdAt = createdAt,
|
||||
|
@ -244,11 +247,35 @@ class Post(
|
|||
)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Post(" +
|
||||
"id=$id, " +
|
||||
"createdAt=$createdAt, " +
|
||||
"url=$url, " +
|
||||
"repostId=$repostId, " +
|
||||
"replyId=$replyId, " +
|
||||
"apId=$apId, " +
|
||||
"actorId=$actorId, " +
|
||||
"visibility=$visibility, " +
|
||||
"visibleActors=$visibleActors, " +
|
||||
"content=$content, " +
|
||||
"overview=$overview, " +
|
||||
"sensitive=$sensitive, " +
|
||||
"text='$text', " +
|
||||
"emojiIds=$emojiIds, " +
|
||||
"mediaIds=$mediaIds, " +
|
||||
"deleted=$deleted, " +
|
||||
"hide=$hide, " +
|
||||
"moveTo=$moveTo" +
|
||||
")"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("LongParameterList")
|
||||
fun create(
|
||||
id: PostId,
|
||||
actorId: ActorId,
|
||||
instanceId: InstanceId,
|
||||
overview: PostOverview? = null,
|
||||
content: PostContent,
|
||||
createdAt: Instant,
|
||||
|
@ -277,6 +304,7 @@ class Post(
|
|||
val post = Post(
|
||||
id = id,
|
||||
actorId = actorId,
|
||||
instanceId = instanceId,
|
||||
overview = overview,
|
||||
content = content,
|
||||
createdAt = createdAt,
|
||||
|
|
|
@ -17,11 +17,19 @@
|
|||
package dev.usbharu.hideout.core.domain.model.post
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
|
||||
interface PostRepository {
|
||||
suspend fun save(post: Post): Post
|
||||
suspend fun saveAll(posts: List<Post>): List<Post>
|
||||
suspend fun findById(id: PostId): Post?
|
||||
suspend fun findByActorId(id: ActorId): List<Post>
|
||||
suspend fun findAllById(ids: List<PostId>): List<Post>
|
||||
suspend fun findByActorId(id: ActorId, page: Page? = null): PaginationList<Post, PostId>
|
||||
suspend fun delete(post: Post)
|
||||
suspend fun findByActorIdAndVisibilityInList(
|
||||
actorId: ActorId,
|
||||
visibilityList: List<Visibility>,
|
||||
of: Page? = null
|
||||
): PaginationList<Post, PostId>
|
||||
}
|
||||
|
|
|
@ -22,4 +22,17 @@ interface RelationshipRepository {
|
|||
suspend fun save(relationship: Relationship): Relationship
|
||||
suspend fun delete(relationship: Relationship)
|
||||
suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): Relationship?
|
||||
suspend fun findByTargetId(
|
||||
targetId: ActorId,
|
||||
option: FindRelationshipOption? = null,
|
||||
inverseOption: FindRelationshipOption? = null
|
||||
): List<Relationship>
|
||||
}
|
||||
|
||||
data class FindRelationshipOption(
|
||||
val follow: Boolean? = null,
|
||||
val block: Boolean? = null,
|
||||
val mute: Boolean? = null,
|
||||
val followRequest: Boolean? = null,
|
||||
val muteFollowRequest: Boolean? = null
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.shared
|
||||
package dev.usbharu.hideout.core.domain.model.support.domain
|
||||
|
||||
@JvmInline
|
||||
value class Domain(val domain: String) {
|
|
@ -0,0 +1,46 @@
|
|||
package dev.usbharu.hideout.core.domain.model.support.page
|
||||
|
||||
sealed class Page {
|
||||
abstract val maxId: Long?
|
||||
abstract val sinceId: Long?
|
||||
abstract val minId: Long?
|
||||
abstract val limit: Int?
|
||||
|
||||
data class PageByMaxId(
|
||||
override val maxId: Long?,
|
||||
override val sinceId: Long?,
|
||||
override val limit: Int?
|
||||
) : Page() {
|
||||
override val minId: Long? = null
|
||||
}
|
||||
|
||||
data class PageByMinId(
|
||||
override val maxId: Long?,
|
||||
override val minId: Long?,
|
||||
override val limit: Int?
|
||||
) : Page() {
|
||||
override val sinceId: Long? = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(
|
||||
maxId: Long? = null,
|
||||
sinceId: Long? = null,
|
||||
minId: Long? = null,
|
||||
limit: Int? = null
|
||||
): Page =
|
||||
if (minId != null) {
|
||||
PageByMinId(
|
||||
maxId,
|
||||
minId,
|
||||
limit
|
||||
)
|
||||
} else {
|
||||
PageByMaxId(
|
||||
maxId,
|
||||
sinceId,
|
||||
limit
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package dev.usbharu.hideout.core.domain.model.support.page
|
||||
|
||||
class PaginationList<T, ID>(list: List<T>, val next: ID?, val prev: ID?) : List<T> by list
|
|
@ -0,0 +1,22 @@
|
|||
package dev.usbharu.hideout.core.domain.model.support.postdetail
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
|
||||
data class PostDetail(
|
||||
val post: Post,
|
||||
val reply: Post? = null,
|
||||
val repost: Post? = null,
|
||||
val postActor: Actor,
|
||||
val replyActor: Actor? = null,
|
||||
val repostActor: Actor? = null
|
||||
) {
|
||||
init {
|
||||
require(post.replyId == reply?.id)
|
||||
require(post.repostId == repost?.id)
|
||||
|
||||
require(post.actorId == postActor.id)
|
||||
require(reply?.actorId == replyActor?.id)
|
||||
require(repost?.actorId == repostActor?.id)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectId
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||
import java.time.Instant
|
||||
|
||||
data class TimelineObjectDetail(
|
||||
val id: TimelineObjectId,
|
||||
val postId: PostId,
|
||||
val timelineUserDetail: UserDetail,
|
||||
val post: Post,
|
||||
val postActor: Actor,
|
||||
val replyPost: Post?,
|
||||
val replyPostActor: Actor?,
|
||||
val repostPost: Post?,
|
||||
val repostPostActor: Actor?,
|
||||
val isPureRepost: Boolean,
|
||||
val lastUpdateAt: Instant,
|
||||
val hasMediaInRepost: Boolean,
|
||||
val warnFilter: List<TimelineObjectWarnFilter>
|
||||
) {
|
||||
companion object {
|
||||
fun of(
|
||||
timelineObject: TimelineObject,
|
||||
timelineUserDetail: UserDetail,
|
||||
post: Post,
|
||||
postActor: Actor,
|
||||
replyPost: Post?,
|
||||
replyPostActor: Actor?,
|
||||
repostPost: Post?,
|
||||
repostPostActor: Actor?,
|
||||
warnFilter: List<TimelineObjectWarnFilter>
|
||||
): TimelineObjectDetail {
|
||||
return TimelineObjectDetail(
|
||||
timelineObject.id,
|
||||
post.id,
|
||||
timelineUserDetail,
|
||||
post,
|
||||
postActor,
|
||||
replyPost,
|
||||
replyPostActor,
|
||||
repostPost,
|
||||
repostPostActor,
|
||||
timelineObject.isPureRepost,
|
||||
timelineObject.lastUpdatedAt,
|
||||
timelineObject.hasMediaInRepost,
|
||||
warnFilter
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEvent
|
||||
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEventFactory
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
|
||||
|
||||
class Timeline(
|
||||
val id: TimelineId,
|
||||
val userDetailId: UserDetailId,
|
||||
name: TimelineName,
|
||||
visibility: TimelineVisibility,
|
||||
val isSystem: Boolean
|
||||
) : DomainEventStorable() {
|
||||
var visibility = visibility
|
||||
private set
|
||||
|
||||
fun setVisibility(visibility: TimelineVisibility, userDetail: UserDetail) {
|
||||
check(isSystem.not())
|
||||
require(userDetailId == userDetail.id)
|
||||
this.visibility = visibility
|
||||
addDomainEvent(TimelineEventFactory(this).createEvent(TimelineEvent.CHANGE_VISIBILITY))
|
||||
}
|
||||
|
||||
var name = name
|
||||
private set
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
@JvmInline
|
||||
value class TimelineId(val value: Long)
|
|
@ -0,0 +1,4 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
@JvmInline
|
||||
value class TimelineName(val value: String)
|
|
@ -0,0 +1,10 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
interface TimelineRepository {
|
||||
suspend fun save(timeline: Timeline): Timeline
|
||||
suspend fun delete(timeline: Timeline)
|
||||
|
||||
suspend fun findByIds(ids: List<TimelineId>): List<Timeline>
|
||||
|
||||
suspend fun findById(id: TimelineId): Timeline?
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
enum class TimelineVisibility {
|
||||
PRIVATE,
|
||||
UNLISTED,
|
||||
PUBLIC
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timelineobject
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterResult
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostContent
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import java.time.Instant
|
||||
|
||||
class TimelineObject(
|
||||
val id: TimelineObjectId,
|
||||
val userDetailId: UserDetailId,
|
||||
val timelineId: TimelineId,
|
||||
val postId: PostId,
|
||||
val postActorId: ActorId,
|
||||
val postCreatedAt: Instant,
|
||||
val replyId: PostId?,
|
||||
val replyActorId: ActorId?,
|
||||
val repostId: PostId?,
|
||||
val repostActorId: ActorId?,
|
||||
visibility: Visibility,
|
||||
isPureRepost: Boolean,
|
||||
mediaIds: List<MediaId>,
|
||||
emojiIds: List<EmojiId>,
|
||||
visibleActors: List<ActorId>,
|
||||
hasMediaInRepost: Boolean,
|
||||
lastUpdatedAt: Instant,
|
||||
var warnFilters: List<TimelineObjectWarnFilter>,
|
||||
|
||||
) {
|
||||
var isPureRepost = isPureRepost
|
||||
private set
|
||||
var visibleActors = visibleActors
|
||||
private set
|
||||
var hasMediaInRepost = hasMediaInRepost
|
||||
private set
|
||||
val hasMedia
|
||||
get() = mediaIds.isNotEmpty()
|
||||
|
||||
var lastUpdatedAt = lastUpdatedAt
|
||||
private set
|
||||
var visibility = visibility
|
||||
private set
|
||||
var mediaIds = mediaIds
|
||||
private set
|
||||
var emojiIds = emojiIds
|
||||
private set
|
||||
|
||||
fun updateWith(post: Post, filterResults: List<FilterResult>) {
|
||||
visibleActors = post.visibleActors.toList()
|
||||
visibility = post.visibility
|
||||
mediaIds = post.mediaIds.toList()
|
||||
emojiIds = post.emojiIds.toList()
|
||||
lastUpdatedAt = Instant.now()
|
||||
isPureRepost =
|
||||
post.repostId != null && post.replyId == null && post.text.isEmpty() && post.overview?.overview.isNullOrEmpty()
|
||||
warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) }
|
||||
}
|
||||
|
||||
fun updateWith(post: Post, repost: Post, filterResults: List<FilterResult>) {
|
||||
require(repost.id == post.repostId)
|
||||
require(repostId == post.repostId)
|
||||
|
||||
updateWith(post, filterResults)
|
||||
hasMediaInRepost = repost.mediaIds.isNotEmpty()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(
|
||||
timelineObjectId: TimelineObjectId,
|
||||
timeline: Timeline,
|
||||
post: Post,
|
||||
replyActorId: ActorId?,
|
||||
filterResults: List<FilterResult>
|
||||
): TimelineObject {
|
||||
return TimelineObject(
|
||||
id = timelineObjectId,
|
||||
userDetailId = timeline.userDetailId,
|
||||
timelineId = timeline.id,
|
||||
postId = post.id,
|
||||
postActorId = post.actorId,
|
||||
postCreatedAt = post.createdAt,
|
||||
replyId = post.replyId,
|
||||
replyActorId = replyActorId,
|
||||
repostId = null,
|
||||
repostActorId = null,
|
||||
visibility = post.visibility,
|
||||
isPureRepost = true,
|
||||
mediaIds = post.mediaIds,
|
||||
emojiIds = post.emojiIds,
|
||||
visibleActors = post.visibleActors.toList(),
|
||||
hasMediaInRepost = false,
|
||||
lastUpdatedAt = Instant.now(),
|
||||
warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) }
|
||||
)
|
||||
}
|
||||
|
||||
fun create(
|
||||
timelineObjectId: TimelineObjectId,
|
||||
timeline: Timeline,
|
||||
post: Post,
|
||||
replyActorId: ActorId?,
|
||||
repost: Post,
|
||||
filterResults: List<FilterResult>
|
||||
): TimelineObject {
|
||||
require(post.repostId == repost.id)
|
||||
|
||||
return TimelineObject(
|
||||
id = timelineObjectId,
|
||||
userDetailId = timeline.userDetailId,
|
||||
timelineId = timeline.id,
|
||||
postId = post.id,
|
||||
postActorId = post.actorId,
|
||||
postCreatedAt = post.createdAt,
|
||||
replyId = post.replyId,
|
||||
replyActorId = replyActorId,
|
||||
repostId = repost.id,
|
||||
repostActorId = repost.actorId,
|
||||
visibility = post.visibility,
|
||||
isPureRepost = repost.mediaIds.isEmpty() &&
|
||||
repost.overview == null &&
|
||||
repost.content == PostContent.empty &&
|
||||
repost.replyId == null,
|
||||
mediaIds = post.mediaIds,
|
||||
emojiIds = post.emojiIds,
|
||||
visibleActors = post.visibleActors.toList(),
|
||||
hasMediaInRepost = repost.mediaIds.isNotEmpty(),
|
||||
lastUpdatedAt = Instant.now(),
|
||||
warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timelineobject
|
||||
|
||||
@JvmInline
|
||||
value class TimelineObjectId(val value: Long)
|
|
@ -0,0 +1,5 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timelineobject
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterId
|
||||
|
||||
class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String)
|
|
@ -0,0 +1,18 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timelinerelationship
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
|
||||
class TimelineRelationship(
|
||||
val id: TimelineRelationshipId,
|
||||
val timelineId: TimelineId,
|
||||
val actorId: ActorId,
|
||||
val visible: Visible
|
||||
)
|
||||
|
||||
enum class Visible {
|
||||
PUBLIC,
|
||||
UNLISTED,
|
||||
FOLLOWERS,
|
||||
DIRECT
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timelinerelationship
|
||||
|
||||
@JvmInline
|
||||
value class TimelineRelationshipId(val value: Long)
|
|
@ -0,0 +1,10 @@
|
|||
package dev.usbharu.hideout.core.domain.model.timelinerelationship
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
|
||||
interface TimelineRelationshipRepository {
|
||||
suspend fun save(timelineRelationship: TimelineRelationship): TimelineRelationship
|
||||
suspend fun delete(timelineRelationship: TimelineRelationship)
|
||||
|
||||
suspend fun findByActorId(actorId: ActorId): List<TimelineRelationship>
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package dev.usbharu.hideout.core.domain.model.userdetails
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import java.time.Instant
|
||||
|
||||
class UserDetail private constructor(
|
||||
|
@ -25,6 +26,7 @@ class UserDetail private constructor(
|
|||
var password: UserDetailHashedPassword,
|
||||
var autoAcceptFolloweeFollowRequest: Boolean,
|
||||
var lastMigration: Instant? = null,
|
||||
val homeTimelineId: TimelineId?
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
@ -45,13 +47,15 @@ class UserDetail private constructor(
|
|||
password: UserDetailHashedPassword,
|
||||
autoAcceptFolloweeFollowRequest: Boolean = false,
|
||||
lastMigration: Instant? = null,
|
||||
homeTimelineId: TimelineId? = null
|
||||
): UserDetail {
|
||||
return UserDetail(
|
||||
id,
|
||||
actorId,
|
||||
password,
|
||||
autoAcceptFolloweeFollowRequest,
|
||||
lastMigration
|
||||
lastMigration,
|
||||
homeTimelineId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,4 +21,5 @@ interface UserDetailRepository {
|
|||
suspend fun delete(userDetail: UserDetail)
|
||||
suspend fun findByActorId(actorId: Long): UserDetail?
|
||||
suspend fun findById(id: Long): UserDetail?
|
||||
suspend fun findAllById(idList: List<UserDetailId>): List<UserDetail>
|
||||
}
|
||||
|
|
|
@ -28,23 +28,23 @@ import java.util.*
|
|||
* @property body ドメインイベントのボディ
|
||||
* @property collectable trueで同じドメインイベント名でをまとめる
|
||||
*/
|
||||
data class DomainEvent(
|
||||
data class DomainEvent<out T : DomainEventBody>(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val occurredOn: Instant,
|
||||
val body: DomainEventBody,
|
||||
val body: T,
|
||||
val collectable: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
fun create(name: String, body: DomainEventBody, collectable: Boolean = false): DomainEvent =
|
||||
DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body, collectable)
|
||||
fun <T : DomainEventBody> create(name: String, body: T, collectable: Boolean = false): DomainEvent<T> =
|
||||
DomainEvent<T>(UUID.randomUUID().toString(), name, Instant.now(), body, collectable)
|
||||
|
||||
fun reconstruct(
|
||||
fun <T : DomainEventBody> reconstruct(
|
||||
id: String,
|
||||
name: String,
|
||||
occurredOn: Instant,
|
||||
body: DomainEventBody,
|
||||
body: T,
|
||||
collectable: Boolean
|
||||
): DomainEvent = DomainEvent(id, name, occurredOn, body, collectable)
|
||||
): DomainEvent<T> = DomainEvent(id, name, occurredOn, body, collectable)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@
|
|||
package dev.usbharu.hideout.core.domain.shared.domainevent
|
||||
|
||||
@Suppress("UnnecessaryAbstractClass")
|
||||
abstract class DomainEventBody(val map: Map<String, Any?>) {
|
||||
abstract class DomainEventBody(private val map: Map<String, Any?>) {
|
||||
fun toMap(): Map<String, Any?> = map
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package dev.usbharu.hideout.core.domain.shared.domainevent
|
||||
|
||||
interface DomainEventPublisher {
|
||||
suspend fun publishEvent(domainEvent: DomainEvent)
|
||||
suspend fun publishEvent(domainEvent: DomainEvent<*>)
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ package dev.usbharu.hideout.core.domain.shared.domainevent
|
|||
|
||||
@Suppress("UnnecessaryAbstractClass")
|
||||
abstract class DomainEventStorable {
|
||||
private val domainEvents: MutableList<DomainEvent> = mutableListOf()
|
||||
private val domainEvents: MutableList<DomainEvent<*>> = mutableListOf()
|
||||
|
||||
protected fun addDomainEvent(domainEvent: DomainEvent) {
|
||||
protected fun addDomainEvent(domainEvent: DomainEvent<*>) {
|
||||
domainEvents.add(domainEvent)
|
||||
}
|
||||
|
||||
fun clearDomainEvents() = domainEvents.clear()
|
||||
|
||||
fun getDomainEvents(): List<DomainEvent> = domainEvents.toList()
|
||||
fun getDomainEvents(): List<DomainEvent<*>> = domainEvents.toList()
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package dev.usbharu.hideout.core.domain.shared.domainevent
|
||||
|
||||
interface DomainEventSubscriber {
|
||||
fun subscribe(eventName: String, domainEventConsumer: DomainEventConsumer)
|
||||
}
|
||||
|
||||
typealias DomainEventConsumer = (DomainEvent) -> Unit
|
|
@ -0,0 +1,7 @@
|
|||
package dev.usbharu.hideout.core.external.timeline
|
||||
|
||||
data class ReadTimelineOption(
|
||||
val mediaOnly: Boolean = false,
|
||||
val local: Boolean = false,
|
||||
val remote: Boolean = false,
|
||||
)
|
27
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/timeline/TimelineStore.kt
vendored
Normal file
27
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/timeline/TimelineStore.kt
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package dev.usbharu.hideout.core.external.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||
|
||||
interface TimelineStore {
|
||||
suspend fun addPost(post: Post)
|
||||
suspend fun updatePost(post: Post)
|
||||
suspend fun removePost(post: Post)
|
||||
suspend fun addTimelineRelationship(timelineRelationship: TimelineRelationship)
|
||||
suspend fun removeTimelineRelationship(timelineRelationship: TimelineRelationship)
|
||||
|
||||
suspend fun updateTimelineRelationship(timelineRelationship: TimelineRelationship)
|
||||
suspend fun addTimeline(timeline: Timeline, timelineRelationshipList: List<TimelineRelationship>)
|
||||
suspend fun removeTimeline(timeline: Timeline)
|
||||
|
||||
suspend fun readTimeline(
|
||||
timeline: Timeline,
|
||||
option: ReadTimelineOption? = null,
|
||||
page: Page? = null
|
||||
): PaginationList<TimelineObjectDetail, PostId>
|
||||
}
|
|
@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.model.actor.*
|
|||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.springframework.stereotype.Component
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposed
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.post.*
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
|
@ -29,6 +30,7 @@ class PostResultRowMapper : ResultRowMapper<Post> {
|
|||
return Post(
|
||||
id = PostId(resultRow[Posts.id]),
|
||||
actorId = ActorId(resultRow[Posts.actorId]),
|
||||
instanceId = InstanceId(resultRow[Posts.instanceId]),
|
||||
overview = resultRow[Posts.overview]?.let { PostOverview(it) },
|
||||
content = PostContent(resultRow[Posts.text], resultRow[Posts.content], emptyList()),
|
||||
createdAt = resultRow[Posts.createdAt],
|
||||
|
|
|
@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
|||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.javatime.CurrentTimestamp
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.*
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
||||
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
||||
|
@ -98,6 +98,18 @@ class ExposedActorRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun findAllById(actorIds: List<ActorId>): List<Actor> {
|
||||
return query {
|
||||
Actors
|
||||
.leftJoin(ActorsAlsoKnownAs, onColumn = { id }, otherColumn = { actorId })
|
||||
.selectAll()
|
||||
.where {
|
||||
Actors.id inList actorIds.map { it.id }
|
||||
}
|
||||
.let(actorQueryMapper::map)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import dev.usbharu.hideout.core.domain.model.filter.Filter
|
|||
import dev.usbharu.hideout.core.domain.model.filter.FilterId
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterKeywordId
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
@ -72,6 +73,10 @@ class ExposedFilterRepository(private val filterQueryMapper: QueryMapper<Filter>
|
|||
return filterQueryMapper.map(where).firstOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByUserDetailId(userDetailId: UserDetailId): List<Filter> {
|
||||
return Filters.selectAll().where { Filters.userId eq userDetailId.id }.let(filterQueryMapper::map)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ExposedFilterRepository::class.java)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
|||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.post.*
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
||||
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
||||
|
@ -160,14 +162,29 @@ class ExposedPostRepository(
|
|||
.first()
|
||||
}
|
||||
|
||||
override suspend fun findByActorId(id: ActorId): List<Post> = query {
|
||||
override suspend fun findAllById(ids: List<PostId>): List<Post> {
|
||||
return query {
|
||||
Posts
|
||||
.selectAll()
|
||||
.where {
|
||||
actorId eq id.id
|
||||
Posts.id inList ids.map { it.id }
|
||||
}
|
||||
.let(postQueryMapper::map)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByActorId(id: ActorId, page: Page?): PaginationList<Post, PostId> = PaginationList(
|
||||
query {
|
||||
Posts
|
||||
.selectAll()
|
||||
.where {
|
||||
actorId eq actorId
|
||||
}
|
||||
.let(postQueryMapper::map)
|
||||
},
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
override suspend fun delete(post: Post) {
|
||||
query {
|
||||
|
@ -178,6 +195,25 @@ class ExposedPostRepository(
|
|||
update(post)
|
||||
}
|
||||
|
||||
override suspend fun findByActorIdAndVisibilityInList(
|
||||
actorId: ActorId,
|
||||
visibilityList: List<Visibility>,
|
||||
of: Page?
|
||||
): PaginationList<Post, PostId> {
|
||||
return PaginationList(
|
||||
query {
|
||||
Posts
|
||||
.selectAll()
|
||||
.where {
|
||||
Posts.actorId eq actorId.id and (visibility inList visibilityList.map { it.name })
|
||||
}
|
||||
.let(postQueryMapper::map)
|
||||
},
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java)
|
||||
}
|
||||
|
@ -186,6 +222,7 @@ class ExposedPostRepository(
|
|||
object Posts : Table("posts") {
|
||||
val id = long("id")
|
||||
val actorId = long("actor_id").references(Actors.id)
|
||||
val instanceId = long("instance_id").references(Instance.id)
|
||||
val overview = varchar("overview", PostOverview.LENGTH).nullable()
|
||||
val content = varchar("content", PostContent.CONTENT_LENGTH)
|
||||
val text = varchar("text", PostContent.TEXT_LENGTH)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.FindRelationshipOption
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
|
||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||
|
@ -66,11 +67,45 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve
|
|||
}.singleOrNull()?.toRelationships()
|
||||
}
|
||||
|
||||
override suspend fun findByTargetId(
|
||||
targetId: ActorId,
|
||||
option: FindRelationshipOption?,
|
||||
inverseOption: FindRelationshipOption?
|
||||
): List<Relationship> {
|
||||
val query1 = Relationships.selectAll().where { Relationships.actorId eq targetId.id }
|
||||
inverseOption.apply(query1)
|
||||
// todo 逆のほうがいいかも
|
||||
val query = query1.alias("INV").selectAll().where {
|
||||
Relationships.targetActorId eq targetId.id
|
||||
}
|
||||
option.apply(query)
|
||||
|
||||
return query.map(ResultRow::toRelationships)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ExposedRelationshipRepository::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun FindRelationshipOption?.apply(query: Query) {
|
||||
if (this?.follow != null) {
|
||||
query.andWhere { Relationships.following eq this@apply.follow }
|
||||
}
|
||||
if (this?.mute != null) {
|
||||
query.andWhere { Relationships.muting eq this@apply.mute }
|
||||
}
|
||||
if (this?.block != null) {
|
||||
query.andWhere { Relationships.blocking eq this@apply.block }
|
||||
}
|
||||
if (this?.followRequest != null) {
|
||||
query.andWhere { Relationships.followRequesting eq this@apply.followRequest }
|
||||
}
|
||||
if (this?.muteFollowRequest != null) {
|
||||
query.andWhere { Relationships.mutingFollowRequest eq this@apply.muteFollowRequest }
|
||||
}
|
||||
}
|
||||
|
||||
fun ResultRow.toRelationships(): Relationship = Relationship(
|
||||
actorId = ActorId(this[Relationships.actorId]),
|
||||
targetActorId = ActorId(this[Relationships.targetActorId]),
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
class ExposedTimelineRelationshipRepository : AbstractRepository(), TimelineRelationshipRepository {
|
||||
override val logger: Logger
|
||||
get() = Companion.logger
|
||||
|
||||
override suspend fun save(timelineRelationship: TimelineRelationship): TimelineRelationship {
|
||||
query {
|
||||
TimelineRelationships.insert {
|
||||
it[id] = timelineRelationship.id.value
|
||||
it[timelineId] = timelineRelationship.timelineId.value
|
||||
it[actorId] = timelineRelationship.actorId.id
|
||||
it[visible] = timelineRelationship.visible.name
|
||||
}
|
||||
}
|
||||
return timelineRelationship
|
||||
}
|
||||
|
||||
override suspend fun delete(timelineRelationship: TimelineRelationship) {
|
||||
query {
|
||||
TimelineRelationships.deleteWhere {
|
||||
TimelineRelationships.id eq timelineRelationship.id.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByActorId(actorId: ActorId): List<TimelineRelationship> {
|
||||
return query {
|
||||
TimelineRelationships.selectAll().where {
|
||||
TimelineRelationships.actorId eq actorId.id
|
||||
}.map { it.toTimelineRelationship() }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ExposedTimelineRelationshipRepository::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun ResultRow.toTimelineRelationship(): TimelineRelationship {
|
||||
return TimelineRelationship(
|
||||
TimelineRelationshipId(this[TimelineRelationships.id]),
|
||||
TimelineId(this[TimelineRelationships.timelineId]),
|
||||
ActorId(this[TimelineRelationships.actorId]),
|
||||
Visible.valueOf(this[TimelineRelationships.visible])
|
||||
)
|
||||
}
|
||||
|
||||
object TimelineRelationships : Table("timeline_relationships") {
|
||||
val id = long("id")
|
||||
val timelineId = long("timeline_id").references(Timelines.id)
|
||||
val actorId = long("actor_id").references(Actors.id)
|
||||
val visible = varchar("visible", 100)
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.*
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
class ExposedTimelineRepository(override val domainEventPublisher: DomainEventPublisher) :
|
||||
TimelineRepository,
|
||||
AbstractRepository(),
|
||||
DomainEventPublishableRepository<Timeline> {
|
||||
override suspend fun save(timeline: Timeline): Timeline {
|
||||
query {
|
||||
Timelines.insert {
|
||||
it[id] = timeline.id.value
|
||||
it[userDetailId] = timeline.userDetailId.id
|
||||
it[name] = timeline.name.value
|
||||
it[visibility] = timeline.visibility.name
|
||||
it[isSystem] = timeline.isSystem
|
||||
}
|
||||
}
|
||||
update(timeline)
|
||||
return timeline
|
||||
}
|
||||
|
||||
override suspend fun delete(timeline: Timeline) {
|
||||
query {
|
||||
Timelines.deleteWhere {
|
||||
Timelines.id eq timeline.id.value
|
||||
}
|
||||
}
|
||||
update(timeline)
|
||||
}
|
||||
|
||||
override suspend fun findByIds(ids: List<TimelineId>): List<Timeline> {
|
||||
return query {
|
||||
Timelines.selectAll().where { Timelines.id inList ids.map { it.value } }.map { it.toTimeline() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findById(id: TimelineId): Timeline? {
|
||||
return query {
|
||||
Timelines.selectAll().where { Timelines.id eq id.value }.firstOrNull()?.toTimeline()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ExposedTimelineRepository::class.java.name)
|
||||
}
|
||||
|
||||
override val logger: Logger
|
||||
get() = Companion.logger
|
||||
}
|
||||
|
||||
fun ResultRow.toTimeline(): Timeline {
|
||||
return Timeline(
|
||||
TimelineId(this[Timelines.id]),
|
||||
UserDetailId(this[Timelines.userDetailId]),
|
||||
TimelineName(this[Timelines.name]),
|
||||
TimelineVisibility.valueOf(this[Timelines.visibility]),
|
||||
this[Timelines.isSystem]
|
||||
)
|
||||
}
|
||||
|
||||
object Timelines : Table("timelines") {
|
||||
val id = long("id")
|
||||
val userDetailId = long("user_detail_id").references(UserDetails.id)
|
||||
val name = varchar("name", 300)
|
||||
val visibility = varchar("visibility", 100)
|
||||
val isSystem = bool("is_system")
|
||||
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
|
@ -64,13 +65,7 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
|
|||
.selectAll().where { UserDetails.actorId eq actorId }
|
||||
.singleOrNull()
|
||||
?.let {
|
||||
UserDetail.create(
|
||||
UserDetailId(it[UserDetails.id]),
|
||||
ActorId(it[UserDetails.actorId]),
|
||||
UserDetailHashedPassword(it[UserDetails.password]),
|
||||
it[UserDetails.autoAcceptFolloweeFollowRequest],
|
||||
it[UserDetails.lastMigration]
|
||||
)
|
||||
userDetail(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,15 +74,29 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
|
|||
.selectAll().where { UserDetails.id eq id }
|
||||
.singleOrNull()
|
||||
?.let {
|
||||
UserDetail.create(
|
||||
userDetail(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findAllById(idList: List<UserDetailId>): List<UserDetail> {
|
||||
return query {
|
||||
UserDetails
|
||||
.selectAll()
|
||||
.where { UserDetails.id inList idList.map { it.id } }
|
||||
.map {
|
||||
userDetail(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun userDetail(it: ResultRow) = UserDetail.create(
|
||||
UserDetailId(it[UserDetails.id]),
|
||||
ActorId(it[UserDetails.actorId]),
|
||||
UserDetailHashedPassword(it[UserDetails.password]),
|
||||
it[UserDetails.autoAcceptFolloweeFollowRequest],
|
||||
it[UserDetails.lastMigration]
|
||||
it[UserDetails.lastMigration],
|
||||
it[UserDetails.homeTimelineId]?.let { it1 -> TimelineId(it1) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java)
|
||||
|
@ -100,5 +109,6 @@ object UserDetails : Table("user_details") {
|
|||
val password = varchar("password", 255)
|
||||
val autoAcceptFolloweeFollowRequest = bool("auto_accept_followee_follow_request")
|
||||
val lastMigration = timestamp("last_migration").nullable()
|
||||
val homeTimelineId = long("home_timeline_id").references(Timelines.id).nullable()
|
||||
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package dev.usbharu.hideout.core.infrastructure.factory
|
|||
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.domain.model.actor.*
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||
import org.springframework.stereotype.Component
|
||||
import java.net.URI
|
||||
|
|
|
@ -52,6 +52,7 @@ class PostFactoryImpl(
|
|||
return Post.create(
|
||||
id = PostId(id),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
overview = overview,
|
||||
content = postContentFactoryImpl.create(content),
|
||||
createdAt = Instant.now(),
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.mongorepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterId
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectId
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import dev.usbharu.hideout.core.infrastructure.timeline.InternalTimelineObjectOption
|
||||
import dev.usbharu.hideout.core.infrastructure.timeline.InternalTimelineObjectRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.time.Instant
|
||||
|
||||
@Repository
|
||||
class MongoInternalTimelineObjectRepository(
|
||||
private val springDataMongoTimelineObjectRepository: SpringDataMongoTimelineObjectRepository,
|
||||
private val mongoTemplate: MongoTemplate
|
||||
) :
|
||||
InternalTimelineObjectRepository {
|
||||
override suspend fun save(timelineObject: TimelineObject): TimelineObject {
|
||||
springDataMongoTimelineObjectRepository.save(SpringDataMongoTimelineObject.of(timelineObject))
|
||||
return timelineObject
|
||||
}
|
||||
|
||||
override suspend fun saveAll(timelineObjectList: List<TimelineObject>): List<TimelineObject> {
|
||||
springDataMongoTimelineObjectRepository.saveAll(timelineObjectList.map { SpringDataMongoTimelineObject.of(it) })
|
||||
.collect()
|
||||
return timelineObjectList
|
||||
}
|
||||
|
||||
override suspend fun findByPostId(postId: PostId): List<TimelineObject> {
|
||||
return springDataMongoTimelineObjectRepository.findByPostId(postId.id).map { it.toTimelineObject() }.toList()
|
||||
}
|
||||
|
||||
override suspend fun deleteByPostId(postId: PostId) {
|
||||
springDataMongoTimelineObjectRepository.deleteByPostId(postId.id)
|
||||
}
|
||||
|
||||
override suspend fun deleteByTimelineIdAndActorId(timelineId: TimelineId, actorId: ActorId) {
|
||||
springDataMongoTimelineObjectRepository.deleteByTimelineIdAndPostActorId(timelineId.value, actorId.id)
|
||||
}
|
||||
|
||||
override suspend fun deleteByTimelineId(timelineId: TimelineId) {
|
||||
springDataMongoTimelineObjectRepository.deleteByTimelineId(timelineId.value)
|
||||
}
|
||||
|
||||
override suspend fun findByTimelineId(
|
||||
timelineId: TimelineId,
|
||||
internalTimelineObjectOption: InternalTimelineObjectOption?,
|
||||
page: Page?
|
||||
): PaginationList<TimelineObject, PostId> {
|
||||
val query = Query()
|
||||
|
||||
if (page?.minId != null) {
|
||||
query.with(Sort.by(Sort.Direction.ASC, "postCreatedAt"))
|
||||
page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) }
|
||||
page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) }
|
||||
} else {
|
||||
query.with(Sort.by(Sort.Direction.DESC, "postCreatedAt"))
|
||||
page?.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) }
|
||||
page?.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) }
|
||||
}
|
||||
|
||||
page?.limit?.let { query.limit(it) }
|
||||
|
||||
val timelineObjects =
|
||||
mongoTemplate.find(query, SpringDataMongoTimelineObject::class.java).map { it.toTimelineObject() }
|
||||
|
||||
return PaginationList(
|
||||
timelineObjects,
|
||||
timelineObjects.lastOrNull()?.postId,
|
||||
timelineObjects.firstOrNull()?.postId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Document
|
||||
data class SpringDataMongoTimelineObject(
|
||||
val id: Long,
|
||||
val userDetailId: Long,
|
||||
val timelineId: Long,
|
||||
val postId: Long,
|
||||
val postActorId: Long,
|
||||
val postCreatedAt: Long,
|
||||
val replyId: Long?,
|
||||
val replyActorId: Long?,
|
||||
val repostId: Long?,
|
||||
val repostActorId: Long?,
|
||||
val visibility: Visibility,
|
||||
val isPureRepost: Boolean,
|
||||
val mediaIds: List<Long>,
|
||||
val emojiIds: List<Long>,
|
||||
val visibleActors: List<Long>,
|
||||
val hasMediaInRepost: Boolean,
|
||||
val lastUpdatedAt: Long,
|
||||
val warnFilters: List<SpringDataMongoTimelineObjectWarnFilter>
|
||||
) {
|
||||
|
||||
fun toTimelineObject(): TimelineObject {
|
||||
return TimelineObject(
|
||||
TimelineObjectId(id),
|
||||
UserDetailId(userDetailId),
|
||||
TimelineId(timelineId),
|
||||
PostId(postId),
|
||||
ActorId(postActorId),
|
||||
Instant.ofEpochSecond(postCreatedAt),
|
||||
replyId?.let { PostId(it) },
|
||||
replyActorId?.let { ActorId(it) },
|
||||
repostId?.let { PostId(it) },
|
||||
repostActorId?.let { ActorId(it) },
|
||||
visibility,
|
||||
isPureRepost,
|
||||
mediaIds.map { MediaId(it) },
|
||||
emojiIds.map { EmojiId(it) },
|
||||
visibleActors.map { ActorId(it) },
|
||||
hasMediaInRepost,
|
||||
Instant.ofEpochSecond(lastUpdatedAt),
|
||||
warnFilters.map { it.toTimelineObjectWarnFilter() }
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(timelineObject: TimelineObject): SpringDataMongoTimelineObject {
|
||||
return SpringDataMongoTimelineObject(
|
||||
timelineObject.id.value,
|
||||
timelineObject.userDetailId.id,
|
||||
timelineObject.timelineId.value,
|
||||
timelineObject.postId.id,
|
||||
timelineObject.postActorId.id,
|
||||
timelineObject.postCreatedAt.epochSecond,
|
||||
timelineObject.replyId?.id,
|
||||
timelineObject.replyActorId?.id,
|
||||
timelineObject.repostId?.id,
|
||||
timelineObject.repostActorId?.id,
|
||||
timelineObject.visibility,
|
||||
timelineObject.isPureRepost,
|
||||
timelineObject.mediaIds.map { it.id },
|
||||
timelineObject.emojiIds.map { it.emojiId },
|
||||
timelineObject.visibleActors.map { it.id },
|
||||
timelineObject.hasMediaInRepost,
|
||||
timelineObject.lastUpdatedAt.epochSecond,
|
||||
timelineObject.warnFilters.map { SpringDataMongoTimelineObjectWarnFilter.of(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class SpringDataMongoTimelineObjectWarnFilter(
|
||||
val filterId: Long,
|
||||
val matchedKeyword: String
|
||||
) {
|
||||
|
||||
fun toTimelineObjectWarnFilter(): TimelineObjectWarnFilter {
|
||||
return TimelineObjectWarnFilter(
|
||||
FilterId(filterId),
|
||||
matchedKeyword
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(timelineObjectWarnFilter: TimelineObjectWarnFilter): SpringDataMongoTimelineObjectWarnFilter {
|
||||
return SpringDataMongoTimelineObjectWarnFilter(
|
||||
timelineObjectWarnFilter.filterId.id,
|
||||
timelineObjectWarnFilter.matchedKeyword
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SpringDataMongoTimelineObjectRepository : CoroutineCrudRepository<SpringDataMongoTimelineObject, Long> {
|
||||
fun findByPostId(postId: Long): Flow<SpringDataMongoTimelineObject>
|
||||
|
||||
suspend fun deleteByPostId(postId: Long)
|
||||
|
||||
suspend fun deleteByTimelineIdAndPostActorId(timelineId: Long, postActorId: Long)
|
||||
|
||||
suspend fun deleteByTimelineId(timelineId: Long)
|
||||
|
||||
suspend fun findByTimelineId(timelineId: TimelineId): Flow<SpringDataMongoTimelineObject>
|
||||
}
|
|
@ -24,7 +24,7 @@ import org.springframework.stereotype.Component
|
|||
@Component
|
||||
class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: ApplicationEventPublisher) :
|
||||
DomainEventPublisher {
|
||||
override suspend fun publishEvent(domainEvent: DomainEvent) {
|
||||
override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
|
||||
applicationEventPublisher.publishEvent(domainEvent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.springframework.domainevent
|
||||
|
||||
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventConsumer
|
||||
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventSubscriber
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
import org.springframework.context.event.EventListener
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class SpringFrameworkDomainEventSubscriber : DomainEventSubscriber {
|
||||
|
||||
val map = mutableMapOf<String, MutableList<DomainEventConsumer<*>>>()
|
||||
|
||||
override fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>) {
|
||||
map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>)
|
||||
}
|
||||
|
||||
@EventListener
|
||||
suspend fun onDomainEventPublished(domainEvent: DomainEvent<*>) {
|
||||
map[domainEvent.name]?.forEach {
|
||||
try {
|
||||
it.invoke(domainEvent)
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilteredPost
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineVisibility
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectId
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
||||
import dev.usbharu.hideout.core.external.timeline.TimelineStore
|
||||
import java.time.Instant
|
||||
|
||||
abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateService) : TimelineStore {
|
||||
override suspend fun addPost(post: Post) {
|
||||
val timelineList = getTimelines(post.actorId)
|
||||
|
||||
val repost = post.repostId?.let { getPost(it) }
|
||||
val replyActorId = post.replyId?.let { getPost(it)?.actorId }
|
||||
|
||||
val timelineObjectList = timelineList.mapNotNull {
|
||||
createTimelineObject(post, replyActorId, repost, it)
|
||||
}
|
||||
|
||||
insertTimelineObject(timelineObjectList)
|
||||
}
|
||||
|
||||
protected abstract suspend fun getTimelines(actorId: ActorId): List<Timeline>
|
||||
|
||||
protected abstract suspend fun getTimeline(timelineId: TimelineId): Timeline?
|
||||
|
||||
protected suspend fun createTimelineObject(
|
||||
post: Post,
|
||||
replyActorId: ActorId?,
|
||||
repost: Post?,
|
||||
timeline: Timeline
|
||||
): TimelineObject? {
|
||||
if (post.visibility == Visibility.DIRECT) {
|
||||
return null
|
||||
}
|
||||
if (timeline.visibility == TimelineVisibility.PUBLIC && post.visibility != Visibility.PUBLIC) {
|
||||
return null
|
||||
}
|
||||
if (timeline.visibility == TimelineVisibility.UNLISTED && (post.visibility != Visibility.PUBLIC || post.visibility != Visibility.UNLISTED)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val filters = getFilters(timeline.userDetailId)
|
||||
|
||||
val applyFilters = applyFilters(post, filters)
|
||||
|
||||
if (repost != null) {
|
||||
return TimelineObject.create(
|
||||
TimelineObjectId(idGenerateService.generateId()),
|
||||
timeline,
|
||||
post,
|
||||
replyActorId,
|
||||
repost,
|
||||
applyFilters.filterResults
|
||||
)
|
||||
}
|
||||
|
||||
return TimelineObject.create(
|
||||
TimelineObjectId(idGenerateService.generateId()),
|
||||
timeline,
|
||||
post,
|
||||
replyActorId,
|
||||
applyFilters.filterResults
|
||||
)
|
||||
}
|
||||
|
||||
protected abstract suspend fun getFilters(userDetailId: UserDetailId): List<Filter>
|
||||
|
||||
protected abstract suspend fun getNewerFilters(userDetailId: UserDetailId, lastUpdateAt: Instant): List<Filter>
|
||||
|
||||
protected abstract suspend fun applyFilters(post: Post, filters: List<Filter>): FilteredPost
|
||||
|
||||
protected abstract suspend fun getPost(postId: PostId): Post?
|
||||
|
||||
protected abstract suspend fun insertTimelineObject(timelineObjectList: List<TimelineObject>)
|
||||
|
||||
protected abstract suspend fun updateTimelineObject(timelineObjectList: List<TimelineObject>)
|
||||
|
||||
protected abstract suspend fun getTimelineObjectByPostId(postId: PostId): List<TimelineObject>
|
||||
|
||||
protected abstract suspend fun removeTimelineObject(postId: PostId)
|
||||
|
||||
protected abstract suspend fun removeTimelineObject(timelineId: TimelineId, actorId: ActorId)
|
||||
|
||||
protected abstract suspend fun removeTimelineObject(timelineId: TimelineId)
|
||||
|
||||
protected abstract suspend fun getPostsByTimelineRelationshipList(timelineRelationshipList: List<TimelineRelationship>): List<Post>
|
||||
|
||||
protected abstract suspend fun getPostsByPostId(postIds: List<PostId>): List<Post>
|
||||
|
||||
protected abstract suspend fun getTimelineObject(
|
||||
timelineId: TimelineId,
|
||||
readTimelineOption: ReadTimelineOption?,
|
||||
page: Page?
|
||||
): PaginationList<TimelineObject, PostId>
|
||||
|
||||
override suspend fun updatePost(post: Post) {
|
||||
val timelineObjectByPostId = getTimelineObjectByPostId(post.id)
|
||||
|
||||
val repost = post.repostId?.let { getPost(it) }
|
||||
|
||||
val timelineObjectList = if (repost != null) {
|
||||
timelineObjectByPostId.map {
|
||||
val filters = getFilters(it.userDetailId)
|
||||
val applyFilters = applyFilters(post, filters)
|
||||
it.updateWith(post, repost, applyFilters.filterResults)
|
||||
it
|
||||
}
|
||||
} else {
|
||||
timelineObjectByPostId.map {
|
||||
val filters = getFilters(it.userDetailId)
|
||||
val applyFilters = applyFilters(post, filters)
|
||||
it.updateWith(post, applyFilters.filterResults)
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
updateTimelineObject(timelineObjectList)
|
||||
}
|
||||
|
||||
protected abstract suspend fun getActorPost(actorId: ActorId, visibilityList: List<Visibility>): List<Post>
|
||||
|
||||
override suspend fun removePost(post: Post) {
|
||||
removeTimelineObject(post.id)
|
||||
}
|
||||
|
||||
override suspend fun addTimelineRelationship(timelineRelationship: TimelineRelationship) {
|
||||
val visibilityList = visibilities(timelineRelationship)
|
||||
val postList = getActorPost(timelineRelationship.actorId, visibilityList)
|
||||
val timeline = getTimeline(timelineRelationship.timelineId) ?: return
|
||||
val timelineObjects = postList.mapNotNull { post ->
|
||||
val repost = post.repostId?.let { getPost(it) }
|
||||
val replyActorId = post.replyId?.let { getPost(it)?.actorId }
|
||||
createTimelineObject(post, replyActorId, repost, timeline)
|
||||
}
|
||||
|
||||
insertTimelineObject(timelineObjects)
|
||||
}
|
||||
|
||||
protected fun visibilities(timelineRelationship: TimelineRelationship): List<Visibility> {
|
||||
val visibilityList = when (timelineRelationship.visible) {
|
||||
Visible.PUBLIC -> {
|
||||
listOf(Visibility.PUBLIC)
|
||||
}
|
||||
|
||||
Visible.UNLISTED -> {
|
||||
listOf(Visibility.PUBLIC, Visibility.UNLISTED)
|
||||
}
|
||||
|
||||
Visible.FOLLOWERS -> {
|
||||
listOf(Visibility.PUBLIC, Visibility.UNLISTED, Visibility.FOLLOWERS)
|
||||
}
|
||||
|
||||
Visible.DIRECT -> {
|
||||
listOf(Visibility.PUBLIC, Visibility.UNLISTED, Visibility.FOLLOWERS, Visibility.DIRECT)
|
||||
}
|
||||
}
|
||||
return visibilityList
|
||||
}
|
||||
|
||||
override suspend fun removeTimelineRelationship(timelineRelationship: TimelineRelationship) {
|
||||
removeTimelineObject(timelineRelationship.timelineId, timelineRelationship.actorId)
|
||||
}
|
||||
|
||||
override suspend fun updateTimelineRelationship(timelineRelationship: TimelineRelationship) {
|
||||
removeTimelineRelationship(timelineRelationship)
|
||||
addTimelineRelationship(timelineRelationship)
|
||||
}
|
||||
|
||||
override suspend fun addTimeline(timeline: Timeline, timelineRelationshipList: List<TimelineRelationship>) {
|
||||
val postList = getPostsByTimelineRelationshipList(timelineRelationshipList)
|
||||
|
||||
val timelineObjectList = postList.mapNotNull { post ->
|
||||
val repost = post.repostId?.let { getPost(it) }
|
||||
val replyActorId = post.replyId?.let { getPost(it)?.actorId }
|
||||
createTimelineObject(post, replyActorId, repost, timeline)
|
||||
}
|
||||
|
||||
insertTimelineObject(timelineObjectList)
|
||||
}
|
||||
|
||||
override suspend fun removeTimeline(timeline: Timeline) {
|
||||
removeTimelineObject(timeline.id)
|
||||
}
|
||||
|
||||
override suspend fun readTimeline(
|
||||
timeline: Timeline,
|
||||
option: ReadTimelineOption?,
|
||||
page: Page?
|
||||
): PaginationList<TimelineObjectDetail, PostId> {
|
||||
val timelineObjectList = getTimelineObject(timeline.id, option, page)
|
||||
val lastUpdatedAt = timelineObjectList.minBy { it.lastUpdatedAt }.lastUpdatedAt
|
||||
|
||||
val newerFilters = getNewerFilters(timeline.userDetailId, lastUpdatedAt)
|
||||
|
||||
val posts =
|
||||
getPostsByPostId(
|
||||
timelineObjectList.map {
|
||||
it.postId
|
||||
} + timelineObjectList.mapNotNull { it.repostId } + timelineObjectList.mapNotNull { it.replyId }
|
||||
)
|
||||
|
||||
val userDetails = getUserDetails(timelineObjectList.map { it.userDetailId })
|
||||
|
||||
val actors =
|
||||
getActors(
|
||||
timelineObjectList.map {
|
||||
it.postActorId
|
||||
} + timelineObjectList.mapNotNull { it.repostActorId } + timelineObjectList.mapNotNull { it.replyActorId }
|
||||
)
|
||||
|
||||
val postMap = posts.associate { post ->
|
||||
post.id to applyFilters(post, newerFilters)
|
||||
}
|
||||
|
||||
return PaginationList(
|
||||
timelineObjectList.mapNotNull<TimelineObject, TimelineObjectDetail> {
|
||||
val timelineUserDetail = userDetails[it.userDetailId] ?: return@mapNotNull null
|
||||
val actor = actors[it.postActorId] ?: return@mapNotNull null
|
||||
val post = postMap[it.postId] ?: return@mapNotNull null
|
||||
val reply = postMap[it.replyId]
|
||||
val replyActor = actors[it.replyActorId]
|
||||
val repost = postMap[it.repostId]
|
||||
val repostActor = actors[it.repostActorId]
|
||||
TimelineObjectDetail.of(
|
||||
timelineObject = it,
|
||||
timelineUserDetail = timelineUserDetail,
|
||||
post = post.post,
|
||||
postActor = actor,
|
||||
replyPost = reply?.post,
|
||||
replyPostActor = replyActor,
|
||||
repostPost = repost?.post,
|
||||
repostPostActor = repostActor,
|
||||
warnFilter = it.warnFilters + post.filterResults.map {
|
||||
TimelineObjectWarnFilter(
|
||||
it.filter.id,
|
||||
it.matchedKeyword
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
timelineObjectList.lastOrNull()?.postId,
|
||||
timelineObjectList.firstOrNull()?.postId
|
||||
)
|
||||
}
|
||||
|
||||
abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
|
||||
|
||||
abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterContext
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilteredPost
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
|
||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
||||
import org.springframework.stereotype.Component
|
||||
import java.time.Instant
|
||||
|
||||
@Component
|
||||
open class DefaultTimelineStore(
|
||||
private val timelineRepository: TimelineRepository,
|
||||
private val timelineRelationshipRepository: TimelineRelationshipRepository,
|
||||
private val filterRepository: FilterRepository,
|
||||
private val postRepository: PostRepository,
|
||||
private val filterDomainService: FilterDomainService,
|
||||
idGenerateService: IdGenerateService,
|
||||
private val defaultTimelineStoreConfig: DefaultTimelineStoreConfig,
|
||||
private val internalTimelineObjectRepository: InternalTimelineObjectRepository,
|
||||
private val userDetailRepository: UserDetailRepository,
|
||||
private val actorRepository: ActorRepository
|
||||
) : AbstractTimelineStore(idGenerateService) {
|
||||
override suspend fun getTimelines(actorId: ActorId): List<Timeline> {
|
||||
return timelineRepository.findByIds(
|
||||
timelineRelationshipRepository
|
||||
.findByActorId(
|
||||
actorId
|
||||
).map { it.timelineId }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getTimeline(timelineId: TimelineId): Timeline? {
|
||||
return timelineRepository.findById(timelineId)
|
||||
}
|
||||
|
||||
override suspend fun getFilters(userDetailId: UserDetailId): List<Filter> {
|
||||
return filterRepository.findByUserDetailId(userDetailId)
|
||||
}
|
||||
|
||||
override suspend fun getNewerFilters(userDetailId: UserDetailId, lastUpdateAt: Instant): List<Filter> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun applyFilters(post: Post, filters: List<Filter>): FilteredPost {
|
||||
return filterDomainService.apply(post, FilterContext.HOME, filters)
|
||||
}
|
||||
|
||||
override suspend fun getPost(postId: PostId): Post? {
|
||||
return postRepository.findById(postId)
|
||||
}
|
||||
|
||||
override suspend fun insertTimelineObject(timelineObjectList: List<TimelineObject>) {
|
||||
internalTimelineObjectRepository.saveAll(timelineObjectList)
|
||||
}
|
||||
|
||||
override suspend fun updateTimelineObject(timelineObjectList: List<TimelineObject>) {
|
||||
internalTimelineObjectRepository.saveAll(timelineObjectList)
|
||||
}
|
||||
|
||||
override suspend fun getTimelineObjectByPostId(postId: PostId): List<TimelineObject> {
|
||||
return internalTimelineObjectRepository.findByPostId(postId)
|
||||
}
|
||||
|
||||
override suspend fun removeTimelineObject(postId: PostId) {
|
||||
internalTimelineObjectRepository.deleteByPostId(postId)
|
||||
}
|
||||
|
||||
override suspend fun removeTimelineObject(timelineId: TimelineId, actorId: ActorId) {
|
||||
internalTimelineObjectRepository.deleteByTimelineIdAndActorId(timelineId, actorId)
|
||||
}
|
||||
|
||||
override suspend fun removeTimelineObject(timelineId: TimelineId) {
|
||||
internalTimelineObjectRepository.deleteByTimelineId(timelineId)
|
||||
}
|
||||
|
||||
override suspend fun getPostsByTimelineRelationshipList(timelineRelationshipList: List<TimelineRelationship>): List<Post> {
|
||||
return timelineRelationshipList.flatMap { getActorPost(it.actorId, visibilities(it)) }
|
||||
}
|
||||
|
||||
override suspend fun getPostsByPostId(postIds: List<PostId>): List<Post> {
|
||||
return postRepository.findAllById(postIds)
|
||||
}
|
||||
|
||||
override suspend fun getTimelineObject(
|
||||
timelineId: TimelineId,
|
||||
readTimelineOption: ReadTimelineOption?,
|
||||
page: Page?
|
||||
): PaginationList<TimelineObject, PostId> {
|
||||
return internalTimelineObjectRepository.findByTimelineId(
|
||||
timelineId,
|
||||
InternalTimelineObjectOption(
|
||||
readTimelineOption?.local,
|
||||
readTimelineOption?.remote,
|
||||
readTimelineOption?.mediaOnly
|
||||
),
|
||||
page
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getActorPost(actorId: ActorId, visibilityList: List<Visibility>): List<Post> {
|
||||
return postRepository.findByActorIdAndVisibilityInList(
|
||||
actorId,
|
||||
visibilityList,
|
||||
Page.of(limit = defaultTimelineStoreConfig.actorPostsCount)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor> {
|
||||
return actorRepository.findAllById(actorIds).associateBy { it.id }
|
||||
}
|
||||
|
||||
override suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> {
|
||||
return userDetailRepository.findAllById(userDetailIdList).associateBy { it.id }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
||||
|
||||
interface InternalTimelineObjectRepository {
|
||||
suspend fun save(timelineObject: TimelineObject): TimelineObject
|
||||
|
||||
suspend fun saveAll(timelineObjectList: List<TimelineObject>): List<TimelineObject>
|
||||
|
||||
suspend fun findByPostId(postId: PostId): List<TimelineObject>
|
||||
|
||||
suspend fun deleteByPostId(postId: PostId)
|
||||
|
||||
suspend fun deleteByTimelineIdAndActorId(timelineId: TimelineId, actorId: ActorId)
|
||||
|
||||
suspend fun deleteByTimelineId(timelineId: TimelineId)
|
||||
suspend fun findByTimelineId(
|
||||
timelineId: TimelineId,
|
||||
internalTimelineObjectOption: InternalTimelineObjectOption? = null,
|
||||
page: Page? = null
|
||||
): PaginationList<TimelineObject, PostId>
|
||||
}
|
||||
|
||||
data class InternalTimelineObjectOption(
|
||||
val localOnly: Boolean? = null,
|
||||
val remoteOnly: Boolean? = null,
|
||||
val mediaOnly: Boolean? = null
|
||||
)
|
|
@ -26,17 +26,15 @@ spring:
|
|||
default-property-inclusion: always
|
||||
datasource:
|
||||
driver-class-name: org.postgresql.Driver
|
||||
url: "jdbc:postgresql:hideout3"
|
||||
url: "jdbc:postgresql:hideout"
|
||||
username: "postgres"
|
||||
password: ""
|
||||
password: "password"
|
||||
data:
|
||||
mongodb:
|
||||
auto-index-creation: true
|
||||
host: localhost
|
||||
port: 27017
|
||||
database: hideout
|
||||
# username: hideoutuser
|
||||
# password: hideoutpass
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 40MB
|
||||
|
|
|
@ -44,7 +44,7 @@ create table if not exists actors
|
|||
created_at timestamp not null,
|
||||
key_id varchar(1000) not null,
|
||||
"following" varchar(1000) null,
|
||||
followers varchar(1000) null,
|
||||
"followers" varchar(1000) null,
|
||||
"instance" bigint not null,
|
||||
locked boolean not null,
|
||||
following_count int null,
|
||||
|
@ -53,9 +53,11 @@ create table if not exists actors
|
|||
last_post_at timestamp null default null,
|
||||
last_update_at timestamp not null,
|
||||
suspend boolean not null,
|
||||
move_to bigint null default null,
|
||||
"move_to" bigint null default null,
|
||||
emojis varchar(3000) not null default '',
|
||||
deleted boolean not null default false,
|
||||
"icon" bigint null,
|
||||
"banner" bigint null,
|
||||
unique ("name", "domain"),
|
||||
constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict,
|
||||
constraint fk_actors_actors__move_to foreign key ("move_to") references actors (id) on delete restrict on update restrict
|
||||
|
@ -63,8 +65,8 @@ create table if not exists actors
|
|||
|
||||
create table if not exists actor_alsoknownas
|
||||
(
|
||||
actor_id bigint not null,
|
||||
also_known_as bigint not null,
|
||||
"actor_id" bigint not null,
|
||||
"also_known_as" bigint not null,
|
||||
constraint fk_actor_alsoknownas_actors__actor_id foreign key ("actor_id") references actors (id) on delete cascade on update cascade,
|
||||
constraint fk_actor_alsoknownas_actors__also_known_as foreign key ("also_known_as") references actors (id) on delete cascade on update cascade
|
||||
);
|
||||
|
@ -91,6 +93,13 @@ create table if not exists media
|
|||
mime_type varchar(255) not null,
|
||||
description varchar(4000) null
|
||||
);
|
||||
|
||||
alter table actors
|
||||
add constraint fk_actors_media__icon foreign key ("icon") references media (id) on delete cascade on update cascade;
|
||||
alter table actors
|
||||
add constraint fk_actors_media__banner foreign key ("banner") references media (id) on delete cascade on update cascade;
|
||||
|
||||
|
||||
create table if not exists posts
|
||||
(
|
||||
id bigint primary key,
|
||||
|
@ -170,13 +179,14 @@ create table if not exists relationships
|
|||
unique (actor_id, target_actor_id)
|
||||
);
|
||||
|
||||
insert into instance (id, name, description, url, icon_url, shared_inbox, software, version, is_blocked, is_muted,
|
||||
insert into instance (id, "name", description, url, icon_url, shared_inbox, software, version, is_blocked, is_muted,
|
||||
moderation_note, created_at)
|
||||
values (0, 'system', '', '', '', null, '', '', false, false, '', current_timestamp);
|
||||
|
||||
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
|
||||
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
||||
last_post_at, last_update_at, suspend, move_to, emojis)
|
||||
insert into actors (id, "name", "domain", screen_name, description, inbox, outbox, url, public_key, private_key,
|
||||
created_at,
|
||||
key_id, "following", "followers", "instance", locked, following_count, followers_count, posts_count,
|
||||
last_post_at, last_update_at, suspend, "move_to", emojis)
|
||||
values (0, '', '', '', '', '', '', '', '', null, current_timestamp, '', null, null, 0, true, null, null, 0, null,
|
||||
current_timestamp, false, null, '');
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package dev.usbharu.hideout.core.domain.model.actor
|
|||
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
|
||||
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.net.URI
|
||||
|
|
|
@ -24,7 +24,8 @@ class FilterTest {
|
|||
actorId = ActorId(1),
|
||||
password = UserDetailHashedPassword(""),
|
||||
autoAcceptFolloweeFollowRequest = false,
|
||||
lastMigration = null
|
||||
lastMigration = null,
|
||||
null
|
||||
)
|
||||
|
||||
assertDoesNotThrow {
|
||||
|
|
|
@ -312,6 +312,7 @@ class PostTest {
|
|||
Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
overview = null,
|
||||
content = PostContent.empty,
|
||||
createdAt = Instant.now(),
|
||||
|
@ -327,7 +328,6 @@ class PostTest {
|
|||
hide = false,
|
||||
moveTo = null,
|
||||
actor = actor
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -339,6 +339,7 @@ class PostTest {
|
|||
val post = Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
overview = null,
|
||||
content = PostContent.empty,
|
||||
createdAt = Instant.now(),
|
||||
|
@ -366,6 +367,7 @@ class PostTest {
|
|||
val post = Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
overview = null,
|
||||
content = PostContent.empty,
|
||||
createdAt = Instant.now(),
|
||||
|
@ -396,6 +398,7 @@ class PostTest {
|
|||
Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
overview = null,
|
||||
content = PostContent.empty,
|
||||
createdAt = Instant.now(),
|
||||
|
@ -425,6 +428,7 @@ class PostTest {
|
|||
Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
content = PostContent.empty,
|
||||
createdAt = Instant.now(),
|
||||
visibility = Visibility.PUBLIC,
|
||||
|
@ -447,6 +451,7 @@ class PostTest {
|
|||
val post = Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
content = PostContent("aaa", "aaa", emojiIds),
|
||||
createdAt = Instant.now(),
|
||||
visibility = Visibility.PUBLIC,
|
||||
|
@ -472,6 +477,7 @@ class PostTest {
|
|||
val post = Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
content = PostContent("aaa", "aaa", emojiIds),
|
||||
createdAt = Instant.now(),
|
||||
visibility = Visibility.PUBLIC,
|
||||
|
@ -510,6 +516,7 @@ class PostTest {
|
|||
val post = Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
content = PostContent("aaa", "aaa", emojiIds),
|
||||
createdAt = Instant.now(),
|
||||
visibility = Visibility.PUBLIC,
|
||||
|
@ -536,6 +543,7 @@ class PostTest {
|
|||
val post = Post.create(
|
||||
id = PostId(1),
|
||||
actorId = actor.id,
|
||||
instanceId = actor.instance,
|
||||
content = PostContent("aaa", "aaa", emojiIds),
|
||||
createdAt = Instant.now(),
|
||||
visibility = Visibility.PUBLIC,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dev.usbharu.hideout.core.domain.model.post
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -13,6 +14,7 @@ object TestPostFactory {
|
|||
fun create(
|
||||
id: Long = generateId(),
|
||||
actorId: Long = 1,
|
||||
instanceId: Long = 1,
|
||||
overview: String? = null,
|
||||
content: String = "This is test content",
|
||||
createdAt: Instant = Instant.now(),
|
||||
|
@ -31,20 +33,21 @@ object TestPostFactory {
|
|||
return Post(
|
||||
PostId(id),
|
||||
ActorId(actorId),
|
||||
instanceId = InstanceId(instanceId),
|
||||
overview = overview?.let { PostOverview(it) },
|
||||
content = PostContent(content, content, emptyList()),
|
||||
createdAt = createdAt,
|
||||
visibility = visibility,
|
||||
url = url,
|
||||
repostId = repostId?.let { PostId(it) },
|
||||
replyId?.let { PostId(it) },
|
||||
replyId = replyId?.let { PostId(it) },
|
||||
sensitive = sensitive,
|
||||
apId = apId,
|
||||
deleted = deleted,
|
||||
mediaIds.map { MediaId(it) },
|
||||
visibleActors.map { ActorId(it) }.toSet(),
|
||||
mediaIds = mediaIds.map { MediaId(it) },
|
||||
visibleActors = visibleActors.map { ActorId(it) }.toSet(),
|
||||
hide = hide,
|
||||
moveTo?.let { PostId(it) }
|
||||
moveTo = moveTo?.let { PostId(it) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,18 @@ repositories {
|
|||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
all {
|
||||
exclude("org.springframework.boot", "spring-boot-starter-logging")
|
||||
exclude("ch.qos.logback", "logback-classic")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
||||
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
||||
|
||||
implementation("dev.usbharu:hideout-core:0.0.1")
|
||||
|
||||
|
@ -75,3 +83,4 @@ sourceSets.main {
|
|||
"$buildDir/generated/sources/mastodon/src/main/kotlin"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import dev.usbharu.hideout.mastodon.query.StatusQuery
|
|||
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.andWhere
|
||||
import org.jetbrains.exposed.sql.leftJoin
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.net.URI
|
||||
|
@ -120,7 +121,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
val map = Posts
|
||||
.leftJoin(PostsMedia)
|
||||
.leftJoin(Actors)
|
||||
.leftJoin(Media)
|
||||
.leftJoin(Media,{PostsMedia.mediaId},{Media.id})
|
||||
.selectAll()
|
||||
.where { Posts.id eq id }
|
||||
.groupBy { it[Posts.id] }
|
||||
|
|
Loading…
Reference in New Issue