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