mirror of https://github.com/usbharu/Hideout.git
commit
b3a9be96c5
|
@ -67,3 +67,4 @@ tasks.register("run") {
|
||||||
springBoot {
|
springBoot {
|
||||||
mainClass = "dev.usbharu.hideout.SpringApplicationKt"
|
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.owl.broker)
|
||||||
implementation(libs.bundles.spring.boot.oauth2)
|
implementation(libs.bundles.spring.boot.oauth2)
|
||||||
implementation(libs.bundles.spring.boot.data.mongodb)
|
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-actuator")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||||
|
|
|
@ -63,6 +63,9 @@ class RegisterLocalActorApplicationService(
|
||||||
id = UserDetailId(idGenerateService.generateId()),
|
id = UserDetailId(idGenerateService.generateId()),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
password = userDetailDomainService.hashPassword(command.password),
|
password = userDetailDomainService.hashPassword(command.password),
|
||||||
|
autoAcceptFolloweeFollowRequest = false,
|
||||||
|
lastMigration = null,
|
||||||
|
homeTimelineId = null
|
||||||
)
|
)
|
||||||
userDetailRepository.save(userDetail)
|
userDetailRepository.save(userDetail)
|
||||||
return actor.url
|
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 {
|
interface UserDetailGettableCommandExecutor : CommandExecutor {
|
||||||
val userDetailId: Long
|
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
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||||
|
|
||||||
class ActorDomainEventFactory(private val actor: Actor) {
|
class ActorDomainEventFactory(private val actor: Actor) {
|
||||||
fun createEvent(actorEvent: ActorEvent): DomainEvent {
|
fun createEvent(actorEvent: ActorEvent): DomainEvent<ActorEventBody> {
|
||||||
return DomainEvent.create(
|
return DomainEvent.create(
|
||||||
actorEvent.eventName,
|
actorEvent.eventName,
|
||||||
ActorEventBody(actor),
|
ActorEventBody(actor),
|
||||||
|
|
|
@ -21,7 +21,9 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||||
|
|
||||||
class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) {
|
class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) {
|
||||||
fun createEvent(actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent): DomainEvent {
|
fun createEvent(
|
||||||
|
actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent
|
||||||
|
): DomainEvent<ActorInstanceRelationshipEventBody> {
|
||||||
return DomainEvent.create(
|
return DomainEvent.create(
|
||||||
actorInstanceRelationshipEvent.eventName,
|
actorInstanceRelationshipEvent.eventName,
|
||||||
ActorInstanceRelationshipEventBody(actorInstanceRelationship)
|
ActorInstanceRelationshipEventBody(actorInstanceRelationship)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||||
|
|
||||||
class InstanceEventFactory(private val instance: Instance) {
|
class InstanceEventFactory(private val instance: Instance) {
|
||||||
fun createEvent(event: InstanceEvent): DomainEvent {
|
fun createEvent(event: InstanceEvent): DomainEvent<InstanceEventBody> {
|
||||||
return DomainEvent.create(
|
return DomainEvent.create(
|
||||||
event.eventName,
|
event.eventName,
|
||||||
InstanceEventBody(instance)
|
InstanceEventBody(instance)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||||
|
|
||||||
class PostDomainEventFactory(private val post: Post, private val actor: Actor? = null) {
|
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(
|
return DomainEvent.create(
|
||||||
postEvent.eventName,
|
postEvent.eventName,
|
||||||
PostEventBody(post, actor)
|
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) {
|
enum class PostEvent(val eventName: String) {
|
||||||
DELETE("PostDelete"),
|
DELETE("PostDelete"),
|
||||||
|
|
|
@ -21,11 +21,15 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||||
|
|
||||||
class RelationshipEventFactory(private val relationship: Relationship) {
|
class RelationshipEventFactory(private val relationship: Relationship) {
|
||||||
fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent =
|
fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent<RelationshipEventBody> =
|
||||||
DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship))
|
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) {
|
enum class RelationshipEvent(val eventName: String) {
|
||||||
FOLLOW("RelationshipFollow"),
|
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.emoji.EmojiId
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
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.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 dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
|
@ -21,4 +21,5 @@ interface ActorRepository {
|
||||||
suspend fun delete(actor: Actor)
|
suspend fun delete(actor: Actor)
|
||||||
suspend fun findById(id: ActorId): Actor?
|
suspend fun findById(id: ActorId): Actor?
|
||||||
suspend fun findByNameAndDomain(name: String, domain: String): 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,
|
LOCAL,
|
||||||
MODERATOR,
|
MODERATOR,
|
||||||
ADMINISTRATOR,
|
ADMINISTRATOR,
|
||||||
REMOTE;
|
REMOTE
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.emoji
|
package dev.usbharu.hideout.core.domain.model.emoji
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
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.net.URI
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
package dev.usbharu.hideout.core.domain.model.filter
|
||||||
|
|
||||||
|
|
||||||
class FilterName(name: String) {
|
class FilterName(name: String) {
|
||||||
|
|
||||||
|
|
||||||
val name = name.take(LENGTH)
|
val name = name.take(LENGTH)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
package dev.usbharu.hideout.core.domain.model.filter
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||||
|
|
||||||
interface FilterRepository {
|
interface FilterRepository {
|
||||||
suspend fun save(filter: Filter): Filter
|
suspend fun save(filter: Filter): Filter
|
||||||
suspend fun delete(filter: Filter)
|
suspend fun delete(filter: Filter)
|
||||||
|
|
||||||
suspend fun findByFilterKeywordId(filterKeywordId: FilterKeywordId): Filter?
|
suspend fun findByFilterKeywordId(filterKeywordId: FilterKeywordId): Filter?
|
||||||
suspend fun findByFilterId(filterId: FilterId): 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.ActorId
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Role
|
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.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.media.MediaId
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post.Companion.Action.*
|
import dev.usbharu.hideout.core.domain.model.post.Post.Companion.Action.*
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
|
||||||
|
@ -32,6 +33,7 @@ import java.time.Instant
|
||||||
class Post(
|
class Post(
|
||||||
val id: PostId,
|
val id: PostId,
|
||||||
actorId: ActorId,
|
actorId: ActorId,
|
||||||
|
val instanceId: InstanceId,
|
||||||
overview: PostOverview?,
|
overview: PostOverview?,
|
||||||
content: PostContent,
|
content: PostContent,
|
||||||
val createdAt: Instant,
|
val createdAt: Instant,
|
||||||
|
@ -227,6 +229,7 @@ class Post(
|
||||||
return Post(
|
return Post(
|
||||||
id = id,
|
id = id,
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
|
instanceId = instanceId,
|
||||||
overview = overview,
|
overview = overview,
|
||||||
content = PostContent(this.content.text, this.content.content, emojis),
|
content = PostContent(this.content.text, this.content.content, emojis),
|
||||||
createdAt = createdAt,
|
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 {
|
companion object {
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
fun create(
|
fun create(
|
||||||
id: PostId,
|
id: PostId,
|
||||||
actorId: ActorId,
|
actorId: ActorId,
|
||||||
|
instanceId: InstanceId,
|
||||||
overview: PostOverview? = null,
|
overview: PostOverview? = null,
|
||||||
content: PostContent,
|
content: PostContent,
|
||||||
createdAt: Instant,
|
createdAt: Instant,
|
||||||
|
@ -277,6 +304,7 @@ class Post(
|
||||||
val post = Post(
|
val post = Post(
|
||||||
id = id,
|
id = id,
|
||||||
actorId = actorId,
|
actorId = actorId,
|
||||||
|
instanceId = instanceId,
|
||||||
overview = overview,
|
overview = overview,
|
||||||
content = content,
|
content = content,
|
||||||
createdAt = createdAt,
|
createdAt = createdAt,
|
||||||
|
|
|
@ -17,11 +17,19 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.post
|
package dev.usbharu.hideout.core.domain.model.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
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 {
|
interface PostRepository {
|
||||||
suspend fun save(post: Post): Post
|
suspend fun save(post: Post): Post
|
||||||
suspend fun saveAll(posts: List<Post>): List<Post>
|
suspend fun saveAll(posts: List<Post>): List<Post>
|
||||||
suspend fun findById(id: PostId): 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 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 save(relationship: Relationship): Relationship
|
||||||
suspend fun delete(relationship: Relationship)
|
suspend fun delete(relationship: Relationship)
|
||||||
suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): 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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.shared
|
package dev.usbharu.hideout.core.domain.model.support.domain
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class Domain(val domain: String) {
|
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
|
package dev.usbharu.hideout.core.domain.model.userdetails
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
class UserDetail private constructor(
|
class UserDetail private constructor(
|
||||||
|
@ -25,6 +26,7 @@ class UserDetail private constructor(
|
||||||
var password: UserDetailHashedPassword,
|
var password: UserDetailHashedPassword,
|
||||||
var autoAcceptFolloweeFollowRequest: Boolean,
|
var autoAcceptFolloweeFollowRequest: Boolean,
|
||||||
var lastMigration: Instant? = null,
|
var lastMigration: Instant? = null,
|
||||||
|
val homeTimelineId: TimelineId?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
@ -45,13 +47,15 @@ class UserDetail private constructor(
|
||||||
password: UserDetailHashedPassword,
|
password: UserDetailHashedPassword,
|
||||||
autoAcceptFolloweeFollowRequest: Boolean = false,
|
autoAcceptFolloweeFollowRequest: Boolean = false,
|
||||||
lastMigration: Instant? = null,
|
lastMigration: Instant? = null,
|
||||||
|
homeTimelineId: TimelineId? = null
|
||||||
): UserDetail {
|
): UserDetail {
|
||||||
return UserDetail(
|
return UserDetail(
|
||||||
id,
|
id,
|
||||||
actorId,
|
actorId,
|
||||||
password,
|
password,
|
||||||
autoAcceptFolloweeFollowRequest,
|
autoAcceptFolloweeFollowRequest,
|
||||||
lastMigration
|
lastMigration,
|
||||||
|
homeTimelineId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,5 @@ interface UserDetailRepository {
|
||||||
suspend fun delete(userDetail: UserDetail)
|
suspend fun delete(userDetail: UserDetail)
|
||||||
suspend fun findByActorId(actorId: Long): UserDetail?
|
suspend fun findByActorId(actorId: Long): UserDetail?
|
||||||
suspend fun findById(id: 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 body ドメインイベントのボディ
|
||||||
* @property collectable trueで同じドメインイベント名でをまとめる
|
* @property collectable trueで同じドメインイベント名でをまとめる
|
||||||
*/
|
*/
|
||||||
data class DomainEvent(
|
data class DomainEvent<out T : DomainEventBody>(
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val occurredOn: Instant,
|
val occurredOn: Instant,
|
||||||
val body: DomainEventBody,
|
val body: T,
|
||||||
val collectable: Boolean = false
|
val collectable: Boolean = false
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun create(name: String, body: DomainEventBody, collectable: Boolean = false): DomainEvent =
|
fun <T : DomainEventBody> create(name: String, body: T, collectable: Boolean = false): DomainEvent<T> =
|
||||||
DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body, collectable)
|
DomainEvent<T>(UUID.randomUUID().toString(), name, Instant.now(), body, collectable)
|
||||||
|
|
||||||
fun reconstruct(
|
fun <T : DomainEventBody> reconstruct(
|
||||||
id: String,
|
id: String,
|
||||||
name: String,
|
name: String,
|
||||||
occurredOn: Instant,
|
occurredOn: Instant,
|
||||||
body: DomainEventBody,
|
body: T,
|
||||||
collectable: Boolean
|
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
|
package dev.usbharu.hideout.core.domain.shared.domainevent
|
||||||
|
|
||||||
@Suppress("UnnecessaryAbstractClass")
|
@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
|
fun toMap(): Map<String, Any?> = map
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package dev.usbharu.hideout.core.domain.shared.domainevent
|
package dev.usbharu.hideout.core.domain.shared.domainevent
|
||||||
|
|
||||||
interface DomainEventPublisher {
|
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")
|
@Suppress("UnnecessaryAbstractClass")
|
||||||
abstract class DomainEventStorable {
|
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)
|
domainEvents.add(domainEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearDomainEvents() = domainEvents.clear()
|
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.emoji.EmojiId
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
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.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 dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposed
|
package dev.usbharu.hideout.core.infrastructure.exposed
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
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.domain.model.post.*
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
|
@ -29,6 +30,7 @@ class PostResultRowMapper : ResultRowMapper<Post> {
|
||||||
return Post(
|
return Post(
|
||||||
id = PostId(resultRow[Posts.id]),
|
id = PostId(resultRow[Posts.id]),
|
||||||
actorId = ActorId(resultRow[Posts.actorId]),
|
actorId = ActorId(resultRow[Posts.actorId]),
|
||||||
|
instanceId = InstanceId(resultRow[Posts.instanceId]),
|
||||||
overview = resultRow[Posts.overview]?.let { PostOverview(it) },
|
overview = resultRow[Posts.overview]?.let { PostOverview(it) },
|
||||||
content = PostContent(resultRow[Posts.text], resultRow[Posts.content], emptyList()),
|
content = PostContent(resultRow[Posts.text], resultRow[Posts.content], emptyList()),
|
||||||
createdAt = resultRow[Posts.createdAt],
|
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.CustomEmojiRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
|
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.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.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.javatime.CurrentTimestamp
|
import org.jetbrains.exposed.sql.javatime.CurrentTimestamp
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.*
|
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.domainevent.DomainEventPublisher
|
||||||
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
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 {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java)
|
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.FilterId
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterKeywordId
|
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.filter.FilterRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
@ -72,6 +73,10 @@ class ExposedFilterRepository(private val filterQueryMapper: QueryMapper<Filter>
|
||||||
return filterQueryMapper.map(where).firstOrNull()
|
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 {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(ExposedFilterRepository::class.java)
|
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.actor.ActorId
|
||||||
import dev.usbharu.hideout.core.domain.model.post.*
|
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.domainevent.DomainEventPublisher
|
||||||
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
import dev.usbharu.hideout.core.infrastructure.exposed.QueryMapper
|
||||||
|
@ -160,14 +162,29 @@ class ExposedPostRepository(
|
||||||
.first()
|
.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByActorId(id: ActorId): List<Post> = query {
|
override suspend fun findAllById(ids: List<PostId>): List<Post> {
|
||||||
|
return query {
|
||||||
Posts
|
Posts
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where {
|
.where {
|
||||||
actorId eq id.id
|
Posts.id inList ids.map { it.id }
|
||||||
}
|
}
|
||||||
.let(postQueryMapper::map)
|
.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) {
|
override suspend fun delete(post: Post) {
|
||||||
query {
|
query {
|
||||||
|
@ -178,6 +195,25 @@ class ExposedPostRepository(
|
||||||
update(post)
|
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 {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java)
|
private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java)
|
||||||
}
|
}
|
||||||
|
@ -186,6 +222,7 @@ class ExposedPostRepository(
|
||||||
object Posts : Table("posts") {
|
object Posts : Table("posts") {
|
||||||
val id = long("id")
|
val id = long("id")
|
||||||
val actorId = long("actor_id").references(Actors.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 overview = varchar("overview", PostOverview.LENGTH).nullable()
|
||||||
val content = varchar("content", PostContent.CONTENT_LENGTH)
|
val content = varchar("content", PostContent.CONTENT_LENGTH)
|
||||||
val text = varchar("text", PostContent.TEXT_LENGTH)
|
val text = varchar("text", PostContent.TEXT_LENGTH)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
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.Relationship
|
||||||
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||||
|
@ -66,11 +67,45 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve
|
||||||
}.singleOrNull()?.toRelationships()
|
}.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 {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(ExposedRelationshipRepository::class.java)
|
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(
|
fun ResultRow.toRelationships(): Relationship = Relationship(
|
||||||
actorId = ActorId(this[Relationships.actorId]),
|
actorId = ActorId(this[Relationships.actorId]),
|
||||||
targetActorId = ActorId(this[Relationships.targetActorId]),
|
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
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
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.UserDetail
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||||
|
@ -64,13 +65,7 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
|
||||||
.selectAll().where { UserDetails.actorId eq actorId }
|
.selectAll().where { UserDetails.actorId eq actorId }
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
?.let {
|
?.let {
|
||||||
UserDetail.create(
|
userDetail(it)
|
||||||
UserDetailId(it[UserDetails.id]),
|
|
||||||
ActorId(it[UserDetails.actorId]),
|
|
||||||
UserDetailHashedPassword(it[UserDetails.password]),
|
|
||||||
it[UserDetails.autoAcceptFolloweeFollowRequest],
|
|
||||||
it[UserDetails.lastMigration]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,15 +74,29 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
|
||||||
.selectAll().where { UserDetails.id eq id }
|
.selectAll().where { UserDetails.id eq id }
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
?.let {
|
?.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]),
|
UserDetailId(it[UserDetails.id]),
|
||||||
ActorId(it[UserDetails.actorId]),
|
ActorId(it[UserDetails.actorId]),
|
||||||
UserDetailHashedPassword(it[UserDetails.password]),
|
UserDetailHashedPassword(it[UserDetails.password]),
|
||||||
it[UserDetails.autoAcceptFolloweeFollowRequest],
|
it[UserDetails.autoAcceptFolloweeFollowRequest],
|
||||||
it[UserDetails.lastMigration]
|
it[UserDetails.lastMigration],
|
||||||
|
it[UserDetails.homeTimelineId]?.let { it1 -> TimelineId(it1) }
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java)
|
private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java)
|
||||||
|
@ -100,5 +109,6 @@ object UserDetails : Table("user_details") {
|
||||||
val password = varchar("password", 255)
|
val password = varchar("password", 255)
|
||||||
val autoAcceptFolloweeFollowRequest = bool("auto_accept_followee_follow_request")
|
val autoAcceptFolloweeFollowRequest = bool("auto_accept_followee_follow_request")
|
||||||
val lastMigration = timestamp("last_migration").nullable()
|
val lastMigration = timestamp("last_migration").nullable()
|
||||||
|
val homeTimelineId = long("home_timeline_id").references(Timelines.id).nullable()
|
||||||
override val primaryKey: PrimaryKey = PrimaryKey(id)
|
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.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.*
|
import dev.usbharu.hideout.core.domain.model.actor.*
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
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 dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
|
@ -52,6 +52,7 @@ class PostFactoryImpl(
|
||||||
return Post.create(
|
return Post.create(
|
||||||
id = PostId(id),
|
id = PostId(id),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
overview = overview,
|
overview = overview,
|
||||||
content = postContentFactoryImpl.create(content),
|
content = postContentFactoryImpl.create(content),
|
||||||
createdAt = Instant.now(),
|
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
|
@Component
|
||||||
class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: ApplicationEventPublisher) :
|
class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: ApplicationEventPublisher) :
|
||||||
DomainEventPublisher {
|
DomainEventPublisher {
|
||||||
override suspend fun publishEvent(domainEvent: DomainEvent) {
|
override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
|
||||||
applicationEventPublisher.publishEvent(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
|
default-property-inclusion: always
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
url: "jdbc:postgresql:hideout3"
|
url: "jdbc:postgresql:hideout"
|
||||||
username: "postgres"
|
username: "postgres"
|
||||||
password: ""
|
password: "password"
|
||||||
data:
|
data:
|
||||||
mongodb:
|
mongodb:
|
||||||
auto-index-creation: true
|
auto-index-creation: true
|
||||||
host: localhost
|
host: localhost
|
||||||
port: 27017
|
port: 27017
|
||||||
database: hideout
|
database: hideout
|
||||||
# username: hideoutuser
|
|
||||||
# password: hideoutpass
|
|
||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
max-file-size: 40MB
|
max-file-size: 40MB
|
||||||
|
|
|
@ -44,7 +44,7 @@ create table if not exists actors
|
||||||
created_at timestamp not null,
|
created_at timestamp not null,
|
||||||
key_id varchar(1000) not null,
|
key_id varchar(1000) not null,
|
||||||
"following" varchar(1000) null,
|
"following" varchar(1000) null,
|
||||||
followers varchar(1000) null,
|
"followers" varchar(1000) null,
|
||||||
"instance" bigint not null,
|
"instance" bigint not null,
|
||||||
locked boolean not null,
|
locked boolean not null,
|
||||||
following_count int null,
|
following_count int null,
|
||||||
|
@ -53,9 +53,11 @@ create table if not exists actors
|
||||||
last_post_at timestamp null default null,
|
last_post_at timestamp null default null,
|
||||||
last_update_at timestamp not null,
|
last_update_at timestamp not null,
|
||||||
suspend boolean not null,
|
suspend boolean not null,
|
||||||
move_to bigint null default null,
|
"move_to" bigint null default null,
|
||||||
emojis varchar(3000) not null default '',
|
emojis varchar(3000) not null default '',
|
||||||
deleted boolean not null default false,
|
deleted boolean not null default false,
|
||||||
|
"icon" bigint null,
|
||||||
|
"banner" bigint null,
|
||||||
unique ("name", "domain"),
|
unique ("name", "domain"),
|
||||||
constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict,
|
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
|
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
|
create table if not exists actor_alsoknownas
|
||||||
(
|
(
|
||||||
actor_id bigint not null,
|
"actor_id" bigint not null,
|
||||||
also_known_as 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__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
|
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,
|
mime_type varchar(255) not null,
|
||||||
description varchar(4000) 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
|
create table if not exists posts
|
||||||
(
|
(
|
||||||
id bigint primary key,
|
id bigint primary key,
|
||||||
|
@ -170,13 +179,14 @@ create table if not exists relationships
|
||||||
unique (actor_id, target_actor_id)
|
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)
|
moderation_note, created_at)
|
||||||
values (0, 'system', '', '', '', null, '', '', false, false, '', current_timestamp);
|
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,
|
insert into actors (id, "name", "domain", screen_name, description, inbox, outbox, url, public_key, private_key,
|
||||||
key_id, following, followers, instance, locked, following_count, followers_count, posts_count,
|
created_at,
|
||||||
last_post_at, last_update_at, suspend, move_to, emojis)
|
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,
|
values (0, '', '', '', '', '', '', '', '', null, current_timestamp, '', null, null, 0, true, null, null, 0, null,
|
||||||
current_timestamp, false, 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.emoji.EmojiId
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
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 dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
|
@ -24,7 +24,8 @@ class FilterTest {
|
||||||
actorId = ActorId(1),
|
actorId = ActorId(1),
|
||||||
password = UserDetailHashedPassword(""),
|
password = UserDetailHashedPassword(""),
|
||||||
autoAcceptFolloweeFollowRequest = false,
|
autoAcceptFolloweeFollowRequest = false,
|
||||||
lastMigration = null
|
lastMigration = null,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
assertDoesNotThrow {
|
assertDoesNotThrow {
|
||||||
|
|
|
@ -312,6 +312,7 @@ class PostTest {
|
||||||
Post.create(
|
Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
overview = null,
|
overview = null,
|
||||||
content = PostContent.empty,
|
content = PostContent.empty,
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
|
@ -327,7 +328,6 @@ class PostTest {
|
||||||
hide = false,
|
hide = false,
|
||||||
moveTo = null,
|
moveTo = null,
|
||||||
actor = actor
|
actor = actor
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,6 +339,7 @@ class PostTest {
|
||||||
val post = Post.create(
|
val post = Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
overview = null,
|
overview = null,
|
||||||
content = PostContent.empty,
|
content = PostContent.empty,
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
|
@ -366,6 +367,7 @@ class PostTest {
|
||||||
val post = Post.create(
|
val post = Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
overview = null,
|
overview = null,
|
||||||
content = PostContent.empty,
|
content = PostContent.empty,
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
|
@ -396,6 +398,7 @@ class PostTest {
|
||||||
Post.create(
|
Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
overview = null,
|
overview = null,
|
||||||
content = PostContent.empty,
|
content = PostContent.empty,
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
|
@ -425,6 +428,7 @@ class PostTest {
|
||||||
Post.create(
|
Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
content = PostContent.empty,
|
content = PostContent.empty,
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
visibility = Visibility.PUBLIC,
|
visibility = Visibility.PUBLIC,
|
||||||
|
@ -447,6 +451,7 @@ class PostTest {
|
||||||
val post = Post.create(
|
val post = Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
content = PostContent("aaa", "aaa", emojiIds),
|
content = PostContent("aaa", "aaa", emojiIds),
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
visibility = Visibility.PUBLIC,
|
visibility = Visibility.PUBLIC,
|
||||||
|
@ -472,6 +477,7 @@ class PostTest {
|
||||||
val post = Post.create(
|
val post = Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
content = PostContent("aaa", "aaa", emojiIds),
|
content = PostContent("aaa", "aaa", emojiIds),
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
visibility = Visibility.PUBLIC,
|
visibility = Visibility.PUBLIC,
|
||||||
|
@ -510,6 +516,7 @@ class PostTest {
|
||||||
val post = Post.create(
|
val post = Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
content = PostContent("aaa", "aaa", emojiIds),
|
content = PostContent("aaa", "aaa", emojiIds),
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
visibility = Visibility.PUBLIC,
|
visibility = Visibility.PUBLIC,
|
||||||
|
@ -536,6 +543,7 @@ class PostTest {
|
||||||
val post = Post.create(
|
val post = Post.create(
|
||||||
id = PostId(1),
|
id = PostId(1),
|
||||||
actorId = actor.id,
|
actorId = actor.id,
|
||||||
|
instanceId = actor.instance,
|
||||||
content = PostContent("aaa", "aaa", emojiIds),
|
content = PostContent("aaa", "aaa", emojiIds),
|
||||||
createdAt = Instant.now(),
|
createdAt = Instant.now(),
|
||||||
visibility = Visibility.PUBLIC,
|
visibility = Visibility.PUBLIC,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.post
|
package dev.usbharu.hideout.core.domain.model.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
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.domain.model.media.MediaId
|
||||||
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
@ -13,6 +14,7 @@ object TestPostFactory {
|
||||||
fun create(
|
fun create(
|
||||||
id: Long = generateId(),
|
id: Long = generateId(),
|
||||||
actorId: Long = 1,
|
actorId: Long = 1,
|
||||||
|
instanceId: Long = 1,
|
||||||
overview: String? = null,
|
overview: String? = null,
|
||||||
content: String = "This is test content",
|
content: String = "This is test content",
|
||||||
createdAt: Instant = Instant.now(),
|
createdAt: Instant = Instant.now(),
|
||||||
|
@ -31,20 +33,21 @@ object TestPostFactory {
|
||||||
return Post(
|
return Post(
|
||||||
PostId(id),
|
PostId(id),
|
||||||
ActorId(actorId),
|
ActorId(actorId),
|
||||||
|
instanceId = InstanceId(instanceId),
|
||||||
overview = overview?.let { PostOverview(it) },
|
overview = overview?.let { PostOverview(it) },
|
||||||
content = PostContent(content, content, emptyList()),
|
content = PostContent(content, content, emptyList()),
|
||||||
createdAt = createdAt,
|
createdAt = createdAt,
|
||||||
visibility = visibility,
|
visibility = visibility,
|
||||||
url = url,
|
url = url,
|
||||||
repostId = repostId?.let { PostId(it) },
|
repostId = repostId?.let { PostId(it) },
|
||||||
replyId?.let { PostId(it) },
|
replyId = replyId?.let { PostId(it) },
|
||||||
sensitive = sensitive,
|
sensitive = sensitive,
|
||||||
apId = apId,
|
apId = apId,
|
||||||
deleted = deleted,
|
deleted = deleted,
|
||||||
mediaIds.map { MediaId(it) },
|
mediaIds = mediaIds.map { MediaId(it) },
|
||||||
visibleActors.map { ActorId(it) }.toSet(),
|
visibleActors = visibleActors.map { ActorId(it) }.toSet(),
|
||||||
hide = hide,
|
hide = hide,
|
||||||
moveTo?.let { PostId(it) }
|
moveTo = moveTo?.let { PostId(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,18 @@ repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
all {
|
||||||
|
exclude("org.springframework.boot", "spring-boot-starter-logging")
|
||||||
|
exclude("ch.qos.logback", "logback-classic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
||||||
|
|
||||||
implementation("dev.usbharu:hideout-core:0.0.1")
|
implementation("dev.usbharu:hideout-core:0.0.1")
|
||||||
|
|
||||||
|
@ -75,3 +83,4 @@ sourceSets.main {
|
||||||
"$buildDir/generated/sources/mastodon/src/main/kotlin"
|
"$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 dev.usbharu.hideout.mastodon.query.StatusQueryService
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import org.jetbrains.exposed.sql.andWhere
|
import org.jetbrains.exposed.sql.andWhere
|
||||||
|
import org.jetbrains.exposed.sql.leftJoin
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
@ -120,7 +121,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
||||||
val map = Posts
|
val map = Posts
|
||||||
.leftJoin(PostsMedia)
|
.leftJoin(PostsMedia)
|
||||||
.leftJoin(Actors)
|
.leftJoin(Actors)
|
||||||
.leftJoin(Media)
|
.leftJoin(Media,{PostsMedia.mediaId},{Media.id})
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where { Posts.id eq id }
|
.where { Posts.id eq id }
|
||||||
.groupBy { it[Posts.id] }
|
.groupBy { it[Posts.id] }
|
||||||
|
|
Loading…
Reference in New Issue