From ee8c8d4e14a23b4980d69955a54c5f57373e7cd7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 May 2024 21:59:06 +0900 Subject: [PATCH 01/54] wip --- .../core/domain/event/actor/ActorEvent.kt | 41 ++++ .../ActorInstanceRelationshipEvent.kt | 47 ++++ .../domain/event/instance/InstanceEvent.kt | 36 +++ .../core/domain/event/post/PostEvent.kt | 39 +++ .../hideout/core/domain/model/actor/Actor2.kt | 121 +++++++++ .../domain/model/actor/Actor2Repository.kt | 23 ++ .../domain/model/actor/ActorDescription.kt | 27 +++ .../core/domain/model/actor/ActorId.kt | 24 ++ .../core/domain/model/actor/ActorKeyId.kt | 20 ++ .../core/domain/model/actor/ActorName.kt | 20 ++ .../domain/model/actor/ActorPostsCount.kt | 27 +++ .../domain/model/actor/ActorPrivateKey.kt | 20 ++ .../core/domain/model/actor/ActorPublicKey.kt | 20 ++ .../model/actor/ActorRelationshipCount.kt | 27 +++ .../domain/model/actor/ActorScreenName.kt | 28 +++ .../ActorInstanceRelationship.kt | 88 +++++++ .../domain/model/deletedActor/DeletedActor.kt | 14 +- .../model/deletedActor/DeletedActorId.kt | 20 ++ .../model/emoji/CustomEmojiRepository.kt | 1 + .../core/domain/model/emoji/EmojiId.kt | 20 ++ .../core/domain/model/instance/Instance.kt | 55 +++-- .../model/instance/InstanceDescription.kt | 20 ++ .../core/domain/model/instance/InstanceId.kt | 20 ++ .../model/instance/InstanceModerationNote.kt | 20 ++ .../domain/model/instance/InstanceName.kt | 20 ++ .../model/instance/InstanceRepository.kt | 8 +- .../domain/model/instance/InstanceSoftware.kt | 20 ++ .../domain/model/instance/InstanceVersion.kt | 20 ++ .../core/domain/model/media/MediaId.kt | 20 ++ .../hideout/core/domain/model/post/Post2.kt | 229 ++++++++++++++++++ .../core/domain/model/post/Post2Repository.kt | 23 ++ .../core/domain/model/post/PostContent.kt | 34 +++ .../hideout/core/domain/model/post/PostId.kt | 20 ++ .../core/domain/model/post/PostOverview.kt | 20 ++ .../core/domain/model/shared/Domain.kt | 20 ++ .../model/shared/domainevent/DomainEvent.kt | 37 +++ .../shared/domainevent/DomainEventBody.kt | 23 ++ .../shared/domainevent/DomainEventStorable.kt | 29 +++ .../domain/model/userdetails/UserDetail.kt | 35 ++- .../userdetails/UserDetailHashedPassword.kt | 20 ++ .../actor/RemoteActorCheckDomainService.kt | 30 +++ .../actor/local/LocalActorDomainService.kt | 25 ++ .../ActorInstanceRelationshipDomainService.kt | 21 ++ .../service/userdetail/PasswordEncoder.kt | 21 ++ .../userdetail/UserDetailDomainService.kt | 25 ++ .../DeletedActorRepositoryImpl.kt | 6 +- .../factory/Actor2FactoryImpl.kt | 66 +++++ .../factory/ActorDescriptionFactoryImpl.kt | 41 ++++ .../factory/ActorScreenNameFactoryImpl.kt | 45 ++++ .../factory/PostContentFactoryImpl.kt | 35 +++ .../infrastructure/factory/PostFactoryImpl.kt | 67 +++++ .../core/service/user/UserServiceImpl.kt | 6 +- .../DeleteLocalActorApplicationService.kt | 25 ++ .../MigrationLocalActorApplicationService.kt | 25 ++ .../core/usecase/actor/RegisterLocalActor.kt | 22 ++ .../RegisterLocalActorApplicationService.kt | 65 +++++ .../SuspendLocalActorApplicationService.kt | 28 +++ .../UnsuspendLocalActorApplicationService.kt | 23 ++ .../post/DeleteLocalPostApplicationService.kt | 30 +++ .../core/usecase/post/RegisterLocalPost.kt | 30 +++ .../RegisterLocalPostApplicationService.kt | 51 ++++ .../core/usecase/post/UpdateLocalNote.kt | 25 ++ .../post/UpdateLocalNoteApplicationService.kt | 45 ++++ 63 files changed, 2080 insertions(+), 33 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt new file mode 100644 index 00000000..62e0d9e9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.actor + +import dev.usbharu.hideout.core.domain.model.actor.Actor2 +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class ActorDomainEventFactory(private val actor: Actor2) { + fun createEvent(actorEvent: ActorEvent): DomainEvent { + return DomainEvent.create( + actorEvent.eventName, + ActorEventBody(actor) + ) + } +} + +class ActorEventBody(actor: Actor2) : DomainEventBody( + mapOf( + "actor" to actor + ) +) + +enum class ActorEvent(val eventName: String) { + update("ActorUpdate"), + delete("ActorDelete"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt new file mode 100644 index 00000000..708c2f4d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.actorinstancerelationship + +import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) { + fun createEvent(actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent): DomainEvent { + return DomainEvent.create( + actorInstanceRelationshipEvent.eventName, + ActorInstanceRelationshipEventBody(actorInstanceRelationship) + ) + } +} + +class ActorInstanceRelationshipEventBody(actorInstanceRelationship: ActorInstanceRelationship) : + DomainEventBody( + mapOf( + "actorId" to actorInstanceRelationship.actorId, + "instanceId" to actorInstanceRelationship.instanceId, + "muting" to actorInstanceRelationship.isMuting(), + "blocking" to actorInstanceRelationship.isBlocking(), + "doNotSendPrivate" to actorInstanceRelationship.isDoNotSendPrivate(), + ) + ) + +enum class ActorInstanceRelationshipEvent(val eventName: String) { + block("ActorInstanceBlock"), + mute("ActorInstanceMute"), + unmute("ActorInstanceUnmute"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt new file mode 100644 index 00000000..101c1cba --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.instance + +import dev.usbharu.hideout.core.domain.model.instance.Instance +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class InstanceEventFactory(private val instance: Instance) { + fun createEvent(event: InstanceEvent): DomainEvent { + return DomainEvent.create( + event.eventName, + InstanceEventBody(instance) + ) + } +} + +class InstanceEventBody(instance: Instance) : DomainEventBody(mapOf("instance" to instance)) + +enum class InstanceEvent(val eventName: String) { + update("InstanceUpdate") +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt new file mode 100644 index 00000000..e5c0812f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.post + +import dev.usbharu.hideout.core.domain.model.post.Post2 +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class PostDomainEventFactory(private val post: Post2) { + fun createEvent(postEvent: PostEvent): DomainEvent { + return DomainEvent.create( + postEvent.eventName, + PostEventBody(post) + ) + } +} + +class PostEventBody(post: Post2) : DomainEventBody(mapOf("post" to post)) + +enum class PostEvent(val eventName: String) { + delete("PostDelete"), + update("PostUpdate"), + create("PostCreate"), + checkUpdate("PostCheckUpdate"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt new file mode 100644 index 00000000..cc4f96ae --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.delete +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.update +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.shared.domainevent.DomainEventStorable +import java.net.URI +import java.time.Instant + +class Actor2 private constructor( + val id: ActorId, + val name: ActorName, + val domain: Domain, + screenName: ActorScreenName, + description: ActorDescription, + val inbox: URI, + val outbox: URI, + val url: URI, + val publicKey: ActorPublicKey, + val privateKey: ActorPrivateKey? = null, + val createdAt: Instant, + val keyId: ActorKeyId, + val followersEndpoint: URI, + val followingEndpoint: URI, + val instance: InstanceId, + var locked: Boolean, + var followersCount: ActorRelationshipCount?, + var followingCount: ActorRelationshipCount?, + var postsCount: ActorPostsCount, + var lastPostDate: Instant? = null, + var suspend: Boolean, +) : DomainEventStorable() { + + val emojis + get() = screenName.emojis + + + var description = description + set(value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + field = value + } + var screenName = screenName + set(value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + field = value + } + + + fun delete() { + addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) + } + + abstract class Actor2Factory { + protected suspend fun create( + id: ActorId, + name: ActorName, + domain: Domain, + screenName: ActorScreenName, + description: ActorDescription, + inbox: URI, + outbox: URI, + url: URI, + publicKey: ActorPublicKey, + privateKey: ActorPrivateKey? = null, + createdAt: Instant, + keyId: ActorKeyId, + followersEndpoint: URI, + followingEndpoint: URI, + instance: InstanceId, + locked: Boolean, + followersCount: ActorRelationshipCount, + followingCount: ActorRelationshipCount, + postsCount: ActorPostsCount, + lastPostDate: Instant? = null, + suspend: Boolean, + ): Actor2 { + return Actor2( + id = id, + name = name, + domain = domain, + screenName = screenName, + description = description, + inbox = inbox, + outbox = outbox, + url = url, + publicKey = publicKey, + privateKey = privateKey, + createdAt = createdAt, + keyId = keyId, + followersEndpoint = followersEndpoint, + followingEndpoint = followingEndpoint, + instance = instance, + locked = locked, + followersCount = followersCount, + followingCount = followingCount, + postsCount = postsCount, + lastPostDate = lastPostDate, + suspend = suspend + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt new file mode 100644 index 00000000..10c21a63 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +interface Actor2Repository { + suspend fun save(actor: Actor2): Actor2 + suspend fun deleteById(actor: ActorId) + suspend fun findById(id: ActorId): Actor2? +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt new file mode 100644 index 00000000..3050836d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId + + +class ActorDescription private constructor(private val description: String, private val emojis: List) { + abstract class ActorDescriptionFactory { + protected suspend fun create(description: String, emojis: List): ActorDescription = + ActorDescription(description, emojis) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt new file mode 100644 index 00000000..73f5a022 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorId(val id: Long) { + companion object { + val ghost = ActorId(0L) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt new file mode 100644 index 00000000..776ceda2 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorKeyId(private val keyId: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt new file mode 100644 index 00000000..77983e4e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorName(val name: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt new file mode 100644 index 00000000..ae44b411 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorPostsCount(private val postsCount: Long) { + init { + require(0 <= this.postsCount) { "Posts count must be greater than 0" } + } + + operator fun inc() = ActorPostsCount(postsCount + 1) + operator fun dec() = ActorPostsCount(postsCount - 1) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt new file mode 100644 index 00000000..a909cfa3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorPrivateKey(private val privateKey: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt new file mode 100644 index 00000000..5428c231 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorPublicKey(private val publicKey: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt new file mode 100644 index 00000000..c55be570 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorRelationshipCount(private val followersCount: Int) { + init { + require(0 <= followersCount) { "Followers count must be > 0" } + } + + operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(followersCount + 1) + operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(followersCount - 1) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt new file mode 100644 index 00000000..83ed08aa --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId + + +class ActorScreenName private constructor(val screenName: String, val emojis: List) { + + abstract class ActorScreenNameFactory { + protected suspend fun create(screenName: String, emojis: List): ActorScreenName = + ActorScreenName(screenName, emojis) + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt new file mode 100644 index 00000000..82ce5599 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actorinstancerelationship + +import dev.usbharu.hideout.core.domain.event.actorinstancerelationship.ActorInstanceRelationshipDomainEventFactory +import dev.usbharu.hideout.core.domain.event.actorinstancerelationship.ActorInstanceRelationshipEvent.* +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.shared.domainevent.DomainEventStorable + +data class ActorInstanceRelationship( + val actorId: ActorId, + val instanceId: InstanceId, + private var blocking: Boolean = false, + private var muting: Boolean = false, + private var doNotSendPrivate: Boolean = false, +) : DomainEventStorable() { + fun block(): ActorInstanceRelationship { + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(block)) + blocking = true + return this + } + + fun unblock(): ActorInstanceRelationship { + blocking = false + return this + } + + fun mute(): ActorInstanceRelationship { + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(mute)) + muting = true + return this + } + + fun unmute(): ActorInstanceRelationship { + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(unmute)) + muting = false + return this + } + + fun doNotSendPrivate(): ActorInstanceRelationship { + doNotSendPrivate = true + return this + } + + fun doSendPrivate(): ActorInstanceRelationship { + doNotSendPrivate = false + return this + } + + fun isBlocking() = blocking + + fun isMuting() = muting + + fun isDoNotSendPrivate() = doNotSendPrivate + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ActorInstanceRelationship + + if (actorId != other.actorId) return false + if (instanceId != other.instanceId) return false + + return true + } + + override fun hashCode(): Int { + var result = actorId.hashCode() + result = 31 * result + instanceId.hashCode() + return result + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt index 0455435f..61e15775 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt @@ -16,13 +16,17 @@ package dev.usbharu.hideout.core.domain.model.deletedActor +import dev.usbharu.hideout.core.domain.model.actor.ActorName +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey +import dev.usbharu.hideout.core.domain.model.shared.Domain +import java.net.URI import java.time.Instant data class DeletedActor( - val id: Long, - val name: String, - val domain: String, - val apiId: String, - val publicKey: String, + val id: DeletedActorId, + val name: ActorName, + val domain: Domain, + val apId: URI, + val publicKey: ActorPublicKey, val deletedAt: Instant, ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt new file mode 100644 index 00000000..b8cfbc98 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.deletedActor + +@JvmInline +value class DeletedActorId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt index eaac7bd5..de339d34 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt @@ -22,4 +22,5 @@ interface CustomEmojiRepository { suspend fun findById(id: Long): CustomEmoji? suspend fun delete(customEmoji: CustomEmoji) suspend fun findByNameAndDomain(name: String, domain: String): CustomEmoji? + suspend fun findByNamesAndDomain(names: List, domain: String): List } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt new file mode 100644 index 00000000..f5d537c2 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.emoji + +@JvmInline +value class EmojiId(private val emojiId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index 3d0aac30..d8ea3516 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -16,19 +16,46 @@ package dev.usbharu.hideout.core.domain.model.instance +import dev.usbharu.hideout.core.domain.event.instance.InstanceEvent +import dev.usbharu.hideout.core.domain.event.instance.InstanceEventFactory +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventStorable +import java.net.URI import java.time.Instant -data class Instance( - val id: Long, - val name: String, - val description: String, - val url: String, - val iconUrl: String, - val sharedInbox: String?, - val software: String, - val version: String, - val isBlocked: Boolean, - val isMuted: Boolean, - val moderationNote: String, - val createdAt: Instant -) +class Instance( + val id: InstanceId, + var name: InstanceName, + var description: InstanceDescription, + val url: URI, + iconUrl: URI, + var sharedInbox: URI?, + var software: InstanceSoftware, + var version: InstanceVersion, + var isBlocked: Boolean, + var isMuted: Boolean, + var moderationNote: InstanceModerationNote, + val createdAt: Instant, +) : DomainEventStorable() { + + + var iconUrl = iconUrl + set(value) { + addDomainEvent(InstanceEventFactory(this).createEvent(InstanceEvent.update)) + field = value + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Instance + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt new file mode 100644 index 00000000..8a6f2084 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceDescription(val description: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt new file mode 100644 index 00000000..de5e4277 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceId(private val instanceId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt new file mode 100644 index 00000000..6439f75c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceModerationNote(val note: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt new file mode 100644 index 00000000..5133566e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceName(val name: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt index 121e5adc..7ab21f8f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt @@ -16,10 +16,12 @@ package dev.usbharu.hideout.core.domain.model.instance +import java.net.URI + interface InstanceRepository { - suspend fun generateId(): Long + suspend fun generateId(): InstanceId suspend fun save(instance: Instance): Instance - suspend fun findById(id: Long): Instance? + suspend fun findById(id: InstanceId): Instance? suspend fun delete(instance: Instance) - suspend fun findByUrl(url: String): Instance? + suspend fun findByUrl(url: URI): Instance? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt new file mode 100644 index 00000000..30d06746 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceSoftware(val software: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt new file mode 100644 index 00000000..b8770133 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceVersion(val version: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt new file mode 100644 index 00000000..5003f164 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.media + +@JvmInline +value class MediaId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt new file mode 100644 index 00000000..f2b4f990 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory +import dev.usbharu.hideout.core.domain.event.post.PostEvent +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventStorable +import java.net.URI +import java.time.Instant + +class Post2 private constructor( + val id: PostId, + actorId: ActorId, + overview: PostOverview? = null, + content: PostContent, + val createdAt: Instant, + visibility: Visibility, + val url: URI, + val repostId: PostId?, + val replyId: PostId?, + sensitive: Boolean, + val apId: URI, + deleted: Boolean, + mediaIds: List, + visibleActors: List = emptyList(), + hide: Boolean = false, + moveTo: PostId? = null, +) : DomainEventStorable() { + + var actorId = actorId + private set + get() { + if (deleted) { + return ActorId.ghost + } + return field + } + + var visibility = visibility + set(value) { + require(value != Visibility.DIRECT) + require(field.ordinal >= value.ordinal) + + require(deleted.not()) + + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + var visibleActors = visibleActors + set(value) { + require(deleted.not()) + if (visibility == Visibility.DIRECT) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + field = field.plus(value).distinct() + } + } + + var content = content + get() { + if (hide) { + return PostContent.empty + } + return field + } + set(value) { + require(deleted.not()) + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + var overview = overview + get() { + if (hide) { + return null + } + return field + } + set(value) { + require(deleted.not()) + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + var sensitive = sensitive + set(value) { + require(deleted.not()) + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + val text + get() = content.text + + val emojiIds + get() = content.emojiIds + + var mediaIds = mediaIds + get() { + if (hide) { + return emptyList() + } + return field + } + private set + + fun addMediaIds(mediaIds: List) { + require(deleted.not()) + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + this.mediaIds = this.mediaIds.plus(mediaIds).distinct() + } + + var deleted = deleted + private set + + fun delete() { + if (deleted.not()) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.delete)) + content = PostContent.empty + overview = null + mediaIds = emptyList() + + } + deleted = true + } + + fun checkUpdate() { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.checkUpdate)) + } + + fun restore(content: PostContent, overview: PostOverview?, mediaIds: List) { + deleted = false + this.content = content + this.overview = overview + this.mediaIds = mediaIds + } + + var hide = hide + private set + + fun hide() { + hide = true + } + + fun show() { + hide = false + } + + var moveTo = moveTo + private set + + fun moveTo(moveTo: PostId) { + require(this.moveTo == null) + this.moveTo = moveTo + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Post2 + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + abstract class PostFactory { + protected fun create( + id: PostId, + actorId: ActorId, + overview: PostOverview? = null, + content: PostContent, + createdAt: Instant, + visibility: Visibility, + url: URI, + repostId: PostId?, + replyId: PostId?, + sensitive: Boolean, + apId: URI, + deleted: Boolean, + mediaIds: List, + hide: Boolean, + ): Post2 { + return Post2( + id = id, + actorId = actorId, + overview = overview, + content = content, + createdAt = createdAt, + visibility = visibility, + url = url, + repostId = repostId, + replyId = replyId, + sensitive = sensitive, + apId = apId, + deleted = deleted, + mediaIds = mediaIds, + hide = hide + ).apply { addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.create)) } + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt new file mode 100644 index 00000000..91961a6f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +interface Post2Repository { + suspend fun save(post: Post2): Post2 + suspend fun findById(id: PostId): Post2? + suspend fun deleteById(id: PostId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt new file mode 100644 index 00000000..2ed4df88 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId + +class PostContent private constructor(val text: String, val content: String, val emojiIds: List) { + + companion object { + val empty = PostContent("", "", emptyList()) + } + + abstract class PostContentFactory { + protected suspend fun create(text: String, content: String, emojiIds: List): PostContent { + return PostContent( + text, content, emojiIds + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt new file mode 100644 index 00000000..e4682ff1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +@JvmInline +value class PostId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt new file mode 100644 index 00000000..995329b1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +@JvmInline +value class PostOverview(val overview: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt new file mode 100644 index 00000000..a6aaa4c8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared + +@JvmInline +value class Domain(val domain: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt new file mode 100644 index 00000000..53c48d86 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared.domainevent + +import java.time.Instant +import java.util.* + +data class DomainEvent( + private val id: String, + private val name: String, + private val occurredOn: Instant, + private val body: DomainEventBody, +) { + companion object { + fun create(name: String, body: DomainEventBody): DomainEvent { + return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body) + } + + fun reconstruct(id: String, name: String, occurredOn: Instant, body: DomainEventBody): DomainEvent { + return DomainEvent(id, name, occurredOn, body) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt new file mode 100644 index 00000000..cb7dd4d9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared.domainevent + +abstract class DomainEventBody(val map: Map) { + fun toMap(): Map { + return map + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt new file mode 100644 index 00000000..a0da8d06 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared.domainevent + +abstract class DomainEventStorable { + private val domainEvents: MutableList = mutableListOf() + + protected fun addDomainEvent(domainEvent: DomainEvent) { + domainEvents.add(domainEvent) + } + + fun clearDomainEvents() = domainEvents.clear() + + fun getDomainEvents(): List = domainEvents.toList() +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt index aabaa0ce..19c32158 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt @@ -16,8 +16,33 @@ package dev.usbharu.hideout.core.domain.model.userdetails -data class UserDetail( - val actorId: Long, - val password: String, - val autoAcceptFolloweeFollowRequest: Boolean -) +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class UserDetail( + val actorId: ActorId, + var password: UserDetailHashedPassword, + var autoAcceptFolloweeFollowRequest: Boolean, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UserDetail + + return actorId == other.actorId + } + + override fun hashCode(): Int { + return actorId.hashCode() + } + + companion object { + fun create( + actorId: ActorId, + password: UserDetailHashedPassword, + autoAcceptFolloweeFollowRequest: Boolean = false, + ): UserDetail { + return UserDetail(actorId, password, autoAcceptFolloweeFollowRequest) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt new file mode 100644 index 00000000..f0dc4399 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.userdetails + +@JvmInline +value class UserDetailHashedPassword(val password: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt new file mode 100644 index 00000000..6c594f1b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.Actor +import org.springframework.stereotype.Service + +interface IRemoteActorCheckDomainService { + fun isRemoteActor(actor: Actor): Boolean +} + +@Service +class RemoteActorCheckDomainService(private val applicationConfig: ApplicationConfig) : IRemoteActorCheckDomainService { + override fun isRemoteActor(actor: Actor): Boolean = actor.domain == applicationConfig.url.host +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt new file mode 100644 index 00000000..677654f7 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.domain.model.actor.ActorPrivateKey +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey + +interface LocalActorDomainService { + suspend fun usernameAlreadyUse(name: String): Boolean + suspend fun generateKeyPair(): Pair +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt new file mode 100644 index 00000000..ac573d8c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actorinstancerelationship + +interface ActorInstanceRelationshipDomainService { + suspend fun block() +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt new file mode 100644 index 00000000..9ff2ee3d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.userdetail + +interface PasswordEncoder { + suspend fun encode(input: String): String +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt new file mode 100644 index 00000000..33db1331 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.userdetail + +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import org.springframework.stereotype.Service + +@Service +class UserDetailDomainService(private val passwordEncoder: PasswordEncoder) { + suspend fun hashPassword(password: String) = UserDetailHashedPassword(passwordEncoder.encode(password)) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt index 41bd924b..a6900563 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt @@ -39,7 +39,7 @@ class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() it[id] = deletedActor.id it[name] = deletedActor.name it[domain] = deletedActor.domain - it[apId] = deletedActor.apiId + it[apId] = deletedActor.apId it[publicKey] = deletedActor.publicKey it[deletedAt] = deletedActor.deletedAt } @@ -47,7 +47,7 @@ class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() DeletedActors.update({ DeletedActors.id eq deletedActor.id }) { it[name] = deletedActor.name it[domain] = deletedActor.domain - it[apId] = deletedActor.apiId + it[apId] = deletedActor.apId it[publicKey] = deletedActor.publicKey it[deletedAt] = deletedActor.deletedAt } @@ -85,7 +85,7 @@ private fun deletedActor(singleOr: ResultRow): DeletedActor { id = singleOr[DeletedActors.id], name = singleOr[DeletedActors.name], domain = singleOr[DeletedActors.domain], - apiId = singleOr[DeletedActors.publicKey], + apId = singleOr[DeletedActors.publicKey], publicKey = singleOr[DeletedActors.apId], deletedAt = singleOr[DeletedActors.deletedAt] ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt new file mode 100644 index 00000000..937744d8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.service.id.IdGenerateService +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 org.springframework.stereotype.Component +import java.net.URI +import java.time.Instant + +@Component +class Actor2FactoryImpl( + private val idGenerateService: IdGenerateService, + private val actorScreenNameFactory: ActorScreenNameFactoryImpl, + private val actorDescriptionFactory: ActorDescriptionFactoryImpl, + private val applicationConfig: ApplicationConfig, +) : Actor2.Actor2Factory() { + suspend fun createLocal( + name: String, + keyPair: Pair, + instanceId: InstanceId, + ): Actor2 { + val actorName = ActorName(name) + val userUrl = "${applicationConfig.url}/users/${actorName.name}" + return super.create( + id = ActorId(idGenerateService.generateId()), + name = actorName, + domain = Domain(applicationConfig.url.host), + screenName = actorScreenNameFactory.create(name), + description = actorDescriptionFactory.create(""), + inbox = URI.create("$userUrl/inbox"), + outbox = URI.create("$userUrl/outbox"), + url = applicationConfig.url.toURI(), + publicKey = keyPair.first, + privateKey = keyPair.second, + createdAt = Instant.now(), + keyId = ActorKeyId("$userUrl#main-key"), + followersEndpoint = URI.create("$userUrl/followers"), + followingEndpoint = URI.create("$userUrl/following"), + instance = instanceId, + locked = false, + followersCount = ActorRelationshipCount(0), + followingCount = ActorRelationshipCount(0), + postsCount = ActorPostsCount(0), + lastPostDate = null, + suspend = false + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt new file mode 100644 index 00000000..a5d107e4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorDescription +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import org.springframework.stereotype.Component + +@Component +class ActorDescriptionFactoryImpl( + private val applicationConfig: ApplicationConfig, + private val emojiRepository: CustomEmojiRepository, +) : ActorDescription.ActorDescriptionFactory() { + val regex = Regex(":(w+):") + suspend fun create(description: String): ActorDescription { + val findAll = regex.findAll(description) + + val emojis = + emojiRepository.findByNamesAndDomain( + findAll.map { it.groupValues[1] }.toList(), + applicationConfig.url.host + ) + return create(description, emojis.map { EmojiId(it.id) }) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt new file mode 100644 index 00000000..b82773e4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorScreenName +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import org.springframework.stereotype.Component + +@Component +class ActorScreenNameFactoryImpl( + private val applicationConfig: ApplicationConfig, + private val emojiRepository: CustomEmojiRepository, +) : ActorScreenName.ActorScreenNameFactory() { + val regex = Regex(":(w+):") + + suspend fun create(content: String): ActorScreenName { + + val findAll = regex.findAll(content) + + val emojis = + emojiRepository.findByNamesAndDomain( + findAll.map { it.groupValues[1] }.toList(), + applicationConfig.url.host + ) + return create(content, emojis.map { EmojiId(it.id) }) + + } + +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt new file mode 100644 index 00000000..b8536126 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.core.domain.model.post.PostContent +import dev.usbharu.hideout.core.service.post.PostContentFormatter +import org.springframework.stereotype.Component + +@Component +class PostContentFactoryImpl( + private val postContentFormatter: PostContentFormatter, +) : PostContent.PostContentFactory() { + suspend fun create(content: String): PostContent { + val format = postContentFormatter.format(content) + return super.create( + format.content, + format.html, + emptyList() + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt new file mode 100644 index 00000000..4a3ae815 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.service.id.IdGenerateService +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorName +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post2 +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.domain.model.post.Visibility +import org.springframework.stereotype.Component +import java.net.URI +import java.time.Instant + +@Component +class PostFactoryImpl( + private val idGenerateService: IdGenerateService, + private val postContentFactoryImpl: PostContentFactoryImpl, + private val applicationConfig: ApplicationConfig, +) : Post2.PostFactory() { + suspend fun createLocal( + actorId: ActorId, + actorName: ActorName, + overview: PostOverview, + content: String, + visibility: Visibility, + repostId: PostId?, + replyId: PostId?, + sensitive: Boolean, + mediaIds: List, + ): Post2 { + val id = idGenerateService.generateId() + val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName + "/posts/" + id) + return super.create( + PostId(id), + actorId, + overview, + postContentFactoryImpl.create(content), + Instant.now(), + visibility, + url, + repostId, + replyId, + sensitive, + url, + false, + mediaIds + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 889c5257..fbc80538 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -159,7 +159,7 @@ class UserServiceImpl( id = actor.id, name = actor.name, domain = actor.domain, - apiId = actor.url, + apId = actor.url, publicKey = actor.publicKey, deletedAt = Instant.now() ) @@ -179,7 +179,7 @@ class UserServiceImpl( deletedActorRepository.delete(deletedActor) - owlProducer.publishTask(UpdateActorTask(deletedActor.id, deletedActor.apiId)) + owlProducer.publishTask(UpdateActorTask(deletedActor.id, deletedActor.apId)) } override suspend fun deleteLocalUser(userId: Long) { @@ -189,7 +189,7 @@ class UserServiceImpl( id = actor.id, name = actor.name, domain = actor.domain, - apiId = actor.url, + apId = actor.url, publicKey = actor.publicKey, deletedAt = Instant.now() ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt new file mode 100644 index 00000000..c1160fc3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class DeleteLocalActorApplicationService { + suspend fun delete(actorId: ActorId, executor: ActorId) { + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt new file mode 100644 index 00000000..ee853538 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class MigrationLocalActorApplicationService { + suspend fun migration(from: ActorId, to: ActorId, executor: ActorId) { + TODO() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt new file mode 100644 index 00000000..8031a87c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +data class RegisterLocalActor( + val name: String, + val password: String, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt new file mode 100644 index 00000000..7127d8a9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainService +import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService +import dev.usbharu.hideout.core.infrastructure.factory.Actor2FactoryImpl +import org.springframework.stereotype.Service + +@Service +class RegisterLocalActorApplicationService( + private val transaction: Transaction, + private val actorDomainService: LocalActorDomainService, + private val actor2Repository: Actor2Repository, + private val actor2FactoryImpl: Actor2FactoryImpl, + private val instanceRepository: InstanceRepository, + private val applicationConfig: ApplicationConfig, + private val userDetailDomainService: UserDetailDomainService, + private val userDetailRepository: UserDetailRepository, +) { + suspend fun register(registerLocalActor: RegisterLocalActor) { + transaction.transaction { + if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) { + //todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ + throw Exception("Username already exists") + } + val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! + + + val actor = actor2FactoryImpl.createLocal( + registerLocalActor.name, + actorDomainService.generateKeyPair(), + instance.id + ) + actor2Repository.save(actor) + val userDetail = UserDetail.create( + actor.id, + userDetailDomainService.hashPassword(registerLocalActor.password), + ) + userDetailRepository.save(userDetail) + + } + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt new file mode 100644 index 00000000..1961ce0f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class SuspendLocalActorApplicationService(private val actor2Repository: Actor2Repository) { + suspend fun suspend(actorId: Long, executor: ActorId) { + val findById = actor2Repository.findById(ActorId(actorId))!! + + findById.suspend = true + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt new file mode 100644 index 00000000..e87d9b0d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +interface UnsuspendLocalActorApplicationService { + suspend fun unsuspend(actorId: ActorId, executor: ActorId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt new file mode 100644 index 00000000..80fd74ca --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.core.domain.model.post.Post2Repository +import dev.usbharu.hideout.core.domain.model.post.PostId +import org.springframework.stereotype.Service + +@Service +class DeleteLocalPostApplicationService(private val postRepository: Post2Repository) { + suspend fun delete(postId: Long) { + val findById = postRepository.findById(PostId(postId))!! + findById.delete() + postRepository.save(findById) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt new file mode 100644 index 00000000..025991d9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.core.domain.model.post.Visibility + +data class RegisterLocalPost( + val actorId: Long, + val content: String, + val overview: String, + val visibility: Visibility, + val repostId: Long?, + val replyId: Long?, + val sensitive: Boolean, + val mediaIds: List, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt new file mode 100644 index 00000000..e08bffb2 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post2Repository +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl +import org.springframework.stereotype.Service + +@Service +class RegisterLocalPostApplicationService( + private val postFactory: PostFactoryImpl, + private val actor2Repository: Actor2Repository, + private val postRepository: Post2Repository, +) { + suspend fun register(registerLocalPost: RegisterLocalPost) { + + val actorId = ActorId(registerLocalPost.actorId) + val post = postFactory.createLocal( + actorId, + actor2Repository.findById(actorId)!!.name, + PostOverview(registerLocalPost.overview), + registerLocalPost.content, + registerLocalPost.visibility, + registerLocalPost.repostId?.let { PostId(it) }, + registerLocalPost.replyId?.let { PostId(it) }, + registerLocalPost.sensitive, + registerLocalPost.mediaIds.map { MediaId(it) } + ) + + postRepository.save(post) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt new file mode 100644 index 00000000..318c010a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +data class UpdateLocalNote( + val postId: Long, + val overview: String?, + val content: String, + val sensitive: Boolean, + val mediaIds: List, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt new file mode 100644 index 00000000..d7aaa905 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post2Repository +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl +import org.springframework.stereotype.Service + +@Service +class UpdateLocalNoteApplicationService( + private val transaction: Transaction, + private val postRepository: Post2Repository, + private val postContentFactoryImpl: PostContentFactoryImpl, +) { + suspend fun update(updateLocalNote: UpdateLocalNote) { + transaction.transaction { + val post = postRepository.findById(PostId(updateLocalNote.postId))!! + + post.content = postContentFactoryImpl.create(updateLocalNote.content) + post.overview = updateLocalNote.overview?.let { PostOverview(it) } + post.mediaIds = updateLocalNote.mediaIds.map { MediaId(it) } + post.sensitive = updateLocalNote.sensitive + + postRepository.save(post) + } + } +} \ No newline at end of file From c2e8c0ca1f4adabc8d557f08f2a5d80be28db37f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 May 2024 00:18:44 +0900 Subject: [PATCH 02/54] wip --- .../core/domain/event/actor/ActorEvent.kt | 4 ++ .../hideout/core/domain/model/actor/Actor2.kt | 43 ++++++++++++++++--- .../domain/model/actor/Actor2Repository.kt | 2 +- .../domain/model/actor/ActorDescription.kt | 2 +- .../core/domain/model/post/Post2Repository.kt | 4 ++ .../domain/model/userdetails/UserDetail.kt | 38 ++++++++++------ .../domain/model/userdetails/UserDetailId.kt | 20 +++++++++ .../LocalActorMigrationCheckDomainService.kt | 35 +++++++++++++++ .../DeleteLocalActorApplicationService.kt | 17 +++++++- .../MigrationLocalActorApplicationService.kt | 36 ++++++++++++++-- .../RegisterLocalActorApplicationService.kt | 8 +++- .../SuspendLocalActorApplicationService.kt | 18 ++++++-- .../UnsuspendLocalActorApplicationService.kt | 18 +++++++- 13 files changed, 211 insertions(+), 34 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt index 62e0d9e9..a35fda34 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -38,4 +38,8 @@ class ActorEventBody(actor: Actor2) : DomainEventBody( enum class ActorEvent(val eventName: String) { update("ActorUpdate"), delete("ActorDelete"), + checkUpdate("ActorCheckUpdate"), + move("ActorMove"), + actorSuspend("ActorSuspend"), + actorUnsuspend("ActorUnsuspend"), } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt index cc4f96ae..06655269 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -17,8 +17,7 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory -import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.delete -import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.update +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.* 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.shared.domainevent.DomainEventStorable @@ -38,20 +37,46 @@ class Actor2 private constructor( val privateKey: ActorPrivateKey? = null, val createdAt: Instant, val keyId: ActorKeyId, - val followersEndpoint: URI, - val followingEndpoint: URI, + val followersEndpoint: URI?, + val followingEndpoint: URI?, val instance: InstanceId, var locked: Boolean, var followersCount: ActorRelationshipCount?, var followingCount: ActorRelationshipCount?, var postsCount: ActorPostsCount, var lastPostDate: Instant? = null, - var suspend: Boolean, + suspend: Boolean, + var lastUpdate: Instant = createdAt, + alsoKnownAs: List = emptyList(), + moveTo: ActorId? = null, ) : DomainEventStorable() { - val emojis - get() = screenName.emojis + var suspend = suspend + set(value) { + if (field != value && value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(actorSuspend)) + } else if (field != value && !value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(actorUnsuspend)) + } + field = value + } + var alsoKnownAs = alsoKnownAs + set(value) { + require(value.find { it == id } == null) + field = value.distinct() + } + + var moveTo = moveTo + set(value) { + require(moveTo != id) + addDomainEvent(ActorDomainEventFactory(this).createEvent(move)) + field = value + } + + + val emojis + get() = screenName.emojis + description.emojis var description = description set(value) { @@ -69,6 +94,10 @@ class Actor2 private constructor( addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) } + fun checkUpdate() { + addDomainEvent(ActorDomainEventFactory(this).createEvent(checkUpdate)) + } + abstract class Actor2Factory { protected suspend fun create( id: ActorId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt index 10c21a63..ddfccccd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt @@ -18,6 +18,6 @@ package dev.usbharu.hideout.core.domain.model.actor interface Actor2Repository { suspend fun save(actor: Actor2): Actor2 - suspend fun deleteById(actor: ActorId) + suspend fun delete(actor: Actor2) suspend fun findById(id: ActorId): Actor2? } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt index 3050836d..4064d241 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -19,7 +19,7 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.core.domain.model.emoji.EmojiId -class ActorDescription private constructor(private val description: String, private val emojis: List) { +class ActorDescription private constructor(val description: String, val emojis: List) { abstract class ActorDescriptionFactory { protected suspend fun create(description: String, emojis: List): ActorDescription = ActorDescription(description, emojis) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt index 91961a6f..f2bffd9a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt @@ -16,8 +16,12 @@ package dev.usbharu.hideout.core.domain.model.post +import dev.usbharu.hideout.core.domain.model.actor.ActorId + interface Post2Repository { suspend fun save(post: Post2): Post2 + suspend fun saveAll(posts: List): List suspend fun findById(id: PostId): Post2? + suspend fun findByActorId(id: ActorId): List suspend fun deleteById(id: PostId) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt index 19c32158..52016f71 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt @@ -17,32 +17,44 @@ package dev.usbharu.hideout.core.domain.model.userdetails import dev.usbharu.hideout.core.domain.model.actor.ActorId +import java.time.Instant -class UserDetail( +class UserDetail private constructor( + val id: UserDetailId, val actorId: ActorId, var password: UserDetailHashedPassword, var autoAcceptFolloweeFollowRequest: Boolean, + var lastMigration: Instant? = null, ) { + + companion object { + fun create( + id: UserDetailId, + actorId: ActorId, + password: UserDetailHashedPassword, + autoAcceptFolloweeFollowRequest: Boolean = false, + lastMigration: Instant? = null, + ): UserDetail { + return UserDetail( + id, + actorId, + password, + autoAcceptFolloweeFollowRequest, + lastMigration + ) + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as UserDetail - return actorId == other.actorId + return id == other.id } override fun hashCode(): Int { - return actorId.hashCode() - } - - companion object { - fun create( - actorId: ActorId, - password: UserDetailHashedPassword, - autoAcceptFolloweeFollowRequest: Boolean = false, - ): UserDetail { - return UserDetail(actorId, password, autoAcceptFolloweeFollowRequest) - } + return id.hashCode() } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt new file mode 100644 index 00000000..cc048546 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.userdetails + +@JvmInline +value class UserDetailId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt new file mode 100644 index 00000000..01fd5aeb --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.domain.model.actor.Actor2 + +interface LocalActorMigrationCheckDomainService { + suspend fun canAccountMigration(from: Actor2, to: Actor2): AccountMigrationCheck +} + +sealed class AccountMigrationCheck( + val canMigration: Boolean, +) { + class CanAccountMigration : AccountMigrationCheck(true) + + class CircularReferences(val message: String) : AccountMigrationCheck(false) + + class SelfReferences : AccountMigrationCheck(false) + + class AlreadyMoved(val message: String) : AccountMigrationCheck(false) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt index c1160fc3..be22d34d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt @@ -16,10 +16,23 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import org.springframework.stereotype.Service -class DeleteLocalActorApplicationService { - suspend fun delete(actorId: ActorId, executor: ActorId) { +@Service +class DeleteLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, +) { + suspend fun delete(actorId: Long, executor: ActorId) { + transaction.transaction { + val id = ActorId(actorId) + val findById = actor2Repository.findById(id)!! + findById.delete() + actor2Repository.delete(findById) + } } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt index ee853538..a7dc73bd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -16,10 +16,40 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* +import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService +import org.springframework.stereotype.Service + +@Service +class MigrationLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, + private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, +) { + suspend fun migration(from: Long, to: Long, executor: ActorId) { + transaction.transaction { + + val fromActorId = ActorId(from) + val toActorId = ActorId(to) + + val fromActor = actor2Repository.findById(fromActorId)!! + val toActor = actor2Repository.findById(toActorId)!! + + val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor) + when (canAccountMigration) { + is AlreadyMoved -> TODO() + is CanAccountMigration -> { + fromActor.moveTo = toActorId + actor2Repository.save(fromActor) + } + + is CircularReferences -> TODO() + is SelfReferences -> TODO() + } + } -class MigrationLocalActorApplicationService { - suspend fun migration(from: ActorId, to: ActorId, executor: ActorId) { - TODO() } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt index 7127d8a9..d93a7c0a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt @@ -18,9 +18,11 @@ package dev.usbharu.hideout.core.usecase.actor import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository 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.actor.local.LocalActorDomainService import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService @@ -37,6 +39,7 @@ class RegisterLocalActorApplicationService( private val applicationConfig: ApplicationConfig, private val userDetailDomainService: UserDetailDomainService, private val userDetailRepository: UserDetailRepository, + private val idGenerateService: IdGenerateService, ) { suspend fun register(registerLocalActor: RegisterLocalActor) { transaction.transaction { @@ -54,8 +57,9 @@ class RegisterLocalActorApplicationService( ) actor2Repository.save(actor) val userDetail = UserDetail.create( - actor.id, - userDetailDomainService.hashPassword(registerLocalActor.password), + id = UserDetailId(idGenerateService.generateId()), + actorId = actor.id, + password = userDetailDomainService.hashPassword(registerLocalActor.password), ) userDetailRepository.save(userDetail) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt index 1961ce0f..08c78483 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt @@ -16,13 +16,25 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import org.springframework.stereotype.Service -class SuspendLocalActorApplicationService(private val actor2Repository: Actor2Repository) { +@Service +class SuspendLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, +) { suspend fun suspend(actorId: Long, executor: ActorId) { - val findById = actor2Repository.findById(ActorId(actorId))!! + transaction.transaction { + + val id = ActorId(actorId) + + val findById = actor2Repository.findById(id)!! + findById.suspend = true + } + - findById.suspend = true } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt index e87d9b0d..962e9e2b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt @@ -16,8 +16,22 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import org.springframework.stereotype.Service -interface UnsuspendLocalActorApplicationService { - suspend fun unsuspend(actorId: ActorId, executor: ActorId) +@Service +class UnsuspendLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, +) { + suspend fun unsuspend(actorId: Long, executor: Long) { + transaction.transaction { + val findById = actor2Repository.findById(ActorId(actorId))!! + + findById.suspend = false + } + + } } \ No newline at end of file From 75f60a7a6274a2ba0df216790e09d912d86e45e4 Mon Sep 17 00:00:00 2001 From: usbharu Date: Tue, 28 May 2024 17:54:17 +0900 Subject: [PATCH 03/54] wip --- .../actor/local/LocalActorMigrationCheckDomainService.kt | 2 ++ .../actor/MigrationLocalActorApplicationService.kt | 1 + .../actor/SetAlsoKnownAsLocalActorApplicationService.kt | 8 ++++++++ 3 files changed, 11 insertions(+) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt index 01fd5aeb..dfa8c9c5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt @@ -32,4 +32,6 @@ sealed class AccountMigrationCheck( class SelfReferences : AccountMigrationCheck(false) class AlreadyMoved(val message: String) : AccountMigrationCheck(false) + + class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt index a7dc73bd..9384eff1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -48,6 +48,7 @@ class MigrationLocalActorApplicationService( is CircularReferences -> TODO() is SelfReferences -> TODO() + is AlsoKnownAsNotFound -> TODO() } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt new file mode 100644 index 00000000..772385d8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.usecase.actor + +import org.springframework.stereotype.Service + +@Service +interface SetAlsoKnownAsLocalActorApplicationService { + suspend fun setAlsoKnownAs(actorId: Long, alsoKnownAs: List) +} \ No newline at end of file From 997e28bdf62c8de781b6e0747992bef06c11474e Mon Sep 17 00:00:00 2001 From: usbharu Date: Wed, 29 May 2024 11:56:51 +0900 Subject: [PATCH 04/54] wip --- .../core/domain/event/actor/ActorEvent.kt | 9 +- .../ActorInstanceRelationshipEvent.kt | 4 +- .../domain/event/instance/InstanceEvent.kt | 4 +- .../core/domain/event/post/PostEvent.kt | 4 +- .../hideout/core/domain/model/actor/Actor.kt | 1 + .../hideout/core/domain/model/actor/Actor2.kt | 4 +- .../core/domain/model/actor/ActorId.kt | 3 + .../core/domain/model/actor/ActorName.kt | 6 +- .../domain/model/actor/ActorPostsCount.kt | 2 +- .../ActorInstanceRelationship.kt | 2 +- .../core/domain/model/instance/Instance.kt | 2 +- .../hideout/core/domain/model/post/Post.kt | 1 + .../hideout/core/domain/model/post/Post2.kt | 2 +- .../model/shared/domainevent/DomainEvent.kt | 37 --------- .../actor/RemoteActorCheckDomainService.kt | 6 +- .../domain/shared/domainevent/DomainEvent.kt | 53 ++++++++++++ .../shared/domainevent/DomainEventBody.kt | 2 +- .../domainevent/DomainEventPublisher.kt | 5 ++ .../shared/domainevent/DomainEventStorable.kt | 2 +- .../domainevent/DomainEventSubscriber.kt | 7 ++ .../DomainEventPublishableRepository.kt | 22 +++++ .../ExposedActor2Repository.kt | 37 +++++++++ .../factory/Actor2FactoryImpl.kt | 2 +- .../core/domain/model/actor/Actor2Test.kt | 10 +++ .../model/actor/ActorDescriptionTest.kt | 5 ++ .../core/domain/model/actor/ActorIdTest.kt | 12 +++ .../domain/model/actor/ActorPublicKeyTest.kt | 3 + .../domain/model/actor/ActorScreenNameTest.kt | 5 ++ .../domain/model/actor/TestActor2Factory.kt | 82 +++++++++++++++++++ .../RemoteActorCheckDomainServiceTest.kt | 14 ++++ 30 files changed, 288 insertions(+), 60 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/{model => }/shared/domainevent/DomainEventBody.kt (91%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/{model => }/shared/domainevent/DomainEventStorable.kt (93%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorIdTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKeyTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt index a35fda34..49128421 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -17,14 +17,15 @@ package dev.usbharu.hideout.core.domain.event.actor import dev.usbharu.hideout.core.domain.model.actor.Actor2 -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody class ActorDomainEventFactory(private val actor: Actor2) { fun createEvent(actorEvent: ActorEvent): DomainEvent { return DomainEvent.create( actorEvent.eventName, - ActorEventBody(actor) + ActorEventBody(actor), + actorEvent.collectable ) } } @@ -35,7 +36,7 @@ class ActorEventBody(actor: Actor2) : DomainEventBody( ) ) -enum class ActorEvent(val eventName: String) { +enum class ActorEvent(val eventName: String, val collectable: Boolean = true) { update("ActorUpdate"), delete("ActorDelete"), checkUpdate("ActorCheckUpdate"), diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt index 708c2f4d..f2e0dd47 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt @@ -17,8 +17,8 @@ package dev.usbharu.hideout.core.domain.event.actorinstancerelationship import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody +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 { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt index 101c1cba..5a3b2f33 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt @@ -17,8 +17,8 @@ package dev.usbharu.hideout.core.domain.event.instance import dev.usbharu.hideout.core.domain.model.instance.Instance -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody +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 { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt index e5c0812f..50eb3a50 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt @@ -17,8 +17,8 @@ package dev.usbharu.hideout.core.domain.event.post import dev.usbharu.hideout.core.domain.model.post.Post2 -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody class PostDomainEventFactory(private val post: Post2) { fun createEvent(postEvent: PostEvent): DomainEvent { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index eee1e824..7e747110 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -26,6 +26,7 @@ import org.springframework.stereotype.Component import java.time.Instant import kotlin.math.max +@Deprecated("Actor2を使ã†") data class Actor private constructor( @get:NotNull @get:Positive diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt index 06655269..db95bacc 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.* 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.shared.domainevent.DomainEventStorable +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant @@ -99,7 +99,7 @@ class Actor2 private constructor( } abstract class Actor2Factory { - protected suspend fun create( + protected suspend fun internalCreate( id: ActorId, name: ActorName, domain: Domain, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt index 73f5a022..871df769 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt @@ -18,6 +18,9 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline value class ActorId(val id: Long) { + init { + require(0 <= id) + } companion object { val ghost = ActorId(0L) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt index 77983e4e..b3136f9a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt @@ -17,4 +17,8 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorName(val name: String) \ No newline at end of file +value class ActorName(val name: String) { + init { + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt index ae44b411..d2b21221 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorPostsCount(private val postsCount: Long) { +value class ActorPostsCount(private val postsCount: Int) { init { require(0 <= this.postsCount) { "Posts count must be greater than 0" } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt index 82ce5599..eaf51768 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.event.actorinstancerelationship.ActorInst import dev.usbharu.hideout.core.domain.event.actorinstancerelationship.ActorInstanceRelationshipEvent.* 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.shared.domainevent.DomainEventStorable +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable data class ActorInstanceRelationship( val actorId: ActorId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index d8ea3516..f9d00f17 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -18,7 +18,7 @@ package dev.usbharu.hideout.core.domain.model.instance import dev.usbharu.hideout.core.domain.event.instance.InstanceEvent import dev.usbharu.hideout.core.domain.event.instance.InstanceEventFactory -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventStorable +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index 2cd1d39b..67dc170d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -24,6 +24,7 @@ import org.hibernate.validator.constraints.URL import org.springframework.stereotype.Component import java.time.Instant +@Deprecated("Post2を使ã†") data class Post private constructor( @get:Positive val id: Long, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt index f2b4f990..137820c7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt @@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.media.MediaId -import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventStorable +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt deleted file mode 100644 index 53c48d86..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.shared.domainevent - -import java.time.Instant -import java.util.* - -data class DomainEvent( - private val id: String, - private val name: String, - private val occurredOn: Instant, - private val body: DomainEventBody, -) { - companion object { - fun create(name: String, body: DomainEventBody): DomainEvent { - return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body) - } - - fun reconstruct(id: String, name: String, occurredOn: Instant, body: DomainEventBody): DomainEvent { - return DomainEvent(id, name, occurredOn, body) - } - } -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt index 6c594f1b..24c5b325 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt @@ -17,14 +17,14 @@ package dev.usbharu.hideout.core.domain.service.actor import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.actor.Actor2 import org.springframework.stereotype.Service interface IRemoteActorCheckDomainService { - fun isRemoteActor(actor: Actor): Boolean + fun isRemoteActor(actor: Actor2): Boolean } @Service class RemoteActorCheckDomainService(private val applicationConfig: ApplicationConfig) : IRemoteActorCheckDomainService { - override fun isRemoteActor(actor: Actor): Boolean = actor.domain == applicationConfig.url.host + override fun isRemoteActor(actor: Actor2): Boolean = actor.domain.domain == applicationConfig.url.host } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt new file mode 100644 index 00000000..cb5c711b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.shared.domainevent + +import java.time.Instant +import java.util.* + +/** + * エンティティã§ç™ºç”Ÿã—ãŸãƒ‰ãƒ¡ã‚¤ãƒ³ã‚¤ãƒ™ãƒ³ãƒˆ + * + * @property id ID + * @property name ドメインイベントå + * @property occurredOn 発生時刻 + * @property body ドメインイベントã®ãƒœãƒ‡ã‚£ + * @property collectable trueã§åŒã˜ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚¤ãƒ™ãƒ³ãƒˆåã§ã‚’ã¾ã¨ã‚ã‚‹ + */ +data class DomainEvent( + val id: String, + val name: String, + val occurredOn: Instant, + val body: DomainEventBody, + val collectable: Boolean = false +) { + companion object { + fun create(name: String, body: DomainEventBody, collectable: Boolean = false): DomainEvent { + return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body, collectable) + } + + fun reconstruct( + id: String, + name: String, + occurredOn: Instant, + body: DomainEventBody, + collectable: Boolean + ): DomainEvent { + return DomainEvent(id, name, occurredOn, body, collectable) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt similarity index 91% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt index cb7dd4d9..fed5479d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.domain.model.shared.domainevent +package dev.usbharu.hideout.core.domain.shared.domainevent abstract class DomainEventBody(val map: Map) { fun toMap(): Map { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt new file mode 100644 index 00000000..59d19150 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.domain.shared.domainevent + +interface DomainEventPublisher { + suspend fun publishEvent(domainEvent: DomainEvent) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt index a0da8d06..dd0c6e60 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.domain.model.shared.domainevent +package dev.usbharu.hideout.core.domain.shared.domainevent abstract class DomainEventStorable { private val domainEvents: MutableList = mutableListOf() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt new file mode 100644 index 00000000..8d529739 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.domain.shared.domainevent + +interface DomainEventSubscriber { + fun subscribe(eventName: String, domainEventConsumer: DomainEventConsumer) +} + +typealias DomainEventConsumer = (DomainEvent) -> Unit \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt new file mode 100644 index 00000000..d542a825 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.core.domain.shared.repository + +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable +import org.springframework.stereotype.Repository + +@Repository +interface DomainEventPublishableRepository { + val domainEventPublisher: DomainEventPublisher + suspend fun update(entity: T) { + entity.getDomainEvents().distinctBy { + if (it.collectable) { + it.name + } else { + it.id + } + }.forEach { + domainEventPublisher.publishEvent(it) + } + entity.clearDomainEvents() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt new file mode 100644 index 00000000..834a332b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt @@ -0,0 +1,37 @@ +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.model.actor.Actor2 +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher +import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class ExposedActor2Repository(override val domainEventPublisher: DomainEventPublisher) : AbstractRepository(), + DomainEventPublishableRepository, Actor2Repository { + override val logger: Logger + get() = Companion.logger + + companion object { + private val logger = LoggerFactory.getLogger(ExposedActor2Repository::class.java) + } + + override suspend fun save(actor: Actor2): Actor2 { + query { + + } + update(actor) + return actor + } + + override suspend fun delete(actor: Actor2) { + TODO("Not yet implemented") + } + + override suspend fun findById(id: ActorId): Actor2? { + TODO("Not yet implemented") + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt index 937744d8..e25f6157 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt @@ -39,7 +39,7 @@ class Actor2FactoryImpl( ): Actor2 { val actorName = ActorName(name) val userUrl = "${applicationConfig.url}/users/${actorName.name}" - return super.create( + return super.internalCreate( id = ActorId(idGenerateService.generateId()), name = actorName, domain = Domain(applicationConfig.url.host), diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt new file mode 100644 index 00000000..bf053cfa --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt @@ -0,0 +1,10 @@ +package dev.usbharu.hideout.core.domain.model.actor + +import org.junit.jupiter.api.Test + +class Actor2Test { + @Test + fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œã¦ã¯ã„ã‘ãªã„() { + TestActor2Factory.create() + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt new file mode 100644 index 00000000..90670a45 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.domain.model.actor + +class ActorDescriptionTest { + +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorIdTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorIdTest.kt new file mode 100644 index 00000000..12c57c0c --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorIdTest.kt @@ -0,0 +1,12 @@ +package dev.usbharu.hideout.core.domain.model.actor + +import org.junit.jupiter.api.Test + +class ActorIdTest { + @Test + fun idã‚’è² ã®æ•°ã«ã™ã‚‹ã“ã¨ã¯ã§ããªã„() { + org.junit.jupiter.api.assertThrows { + ActorId(-1) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKeyTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKeyTest.kt new file mode 100644 index 00000000..37bb1938 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKeyTest.kt @@ -0,0 +1,3 @@ +package dev.usbharu.hideout.core.domain.model.actor + +class ActorPublicKeyTest \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt new file mode 100644 index 00000000..febff754 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.domain.model.actor + +class ActorScreenNameTest { + +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt new file mode 100644 index 00000000..6ff26446 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt @@ -0,0 +1,82 @@ +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService +import dev.usbharu.hideout.core.domain.model.instance.InstanceId +import dev.usbharu.hideout.core.domain.model.shared.Domain +import kotlinx.coroutines.runBlocking +import java.net.URI +import java.time.Instant + +object TestActor2Factory : Actor2.Actor2Factory() { + private val idGenerateService = TwitterSnowflakeIdGenerateService + + fun create( + id: Long = generateId(), + actorName: String = "test-$id", + domain: String = "example.com", + actorScreenName: String = actorName, + description: String = "test description", + inbox: URI = URI.create("https://example.com/$id/inbox"), + outbox: URI = URI.create("https://example.com/$id/outbox"), + uri: URI = URI.create("https://example.com/$id"), + publicKey: ActorPublicKey, + privateKey: ActorPrivateKey?, + createdAt: Instant = Instant.now(), + keyId: String = "https://example.com/$id#key-id", + followersEndpoint: URI = URI.create("https://example.com/$id/followers"), + followingEndpoint: URI = URI.create("https://example.com/$id/following"), + instanceId: Long = 1L, + locked: Boolean = false, + followersCount: Int = 0, + followingCount: Int = 0, + postCount: Int = 0, + lastPostDate: Instant? = null, + suspend: Boolean = false + ): Actor2 { + return runBlocking { + super.internalCreate( + id = ActorId(id), + name = ActorName(actorName), + domain = Domain(domain), + screenName = TestActorScreenNameFactory.create(actorScreenName), + description = TestActorDescriptionFactory.create(description), + inbox = inbox, + outbox = outbox, + url = uri, + publicKey = publicKey, + privateKey = privateKey, + createdAt = createdAt, + keyId = ActorKeyId(keyId), + followersEndpoint = followersEndpoint, + followingEndpoint = followingEndpoint, + InstanceId(instanceId), + locked, + followersCount = ActorRelationshipCount(followersCount), + followingCount = ActorRelationshipCount(followingCount), + postsCount = ActorPostsCount(postCount), + lastPostDate = lastPostDate, + suspend = suspend + ) + } + } + + private fun generateId(): Long = runBlocking { + idGenerateService.generateId() + } +} + +object TestActorScreenNameFactory : ActorScreenName.ActorScreenNameFactory() { + fun create(name: String): ActorScreenName { + return runBlocking { + super.create(name, emptyList()) + } + } +} + +object TestActorDescriptionFactory : ActorDescription.ActorDescriptionFactory() { + fun create(description: String): ActorDescription { + return runBlocking { + super.create(description, emptyList()) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt new file mode 100644 index 00000000..8cd71362 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.domain.service.actor + +import dev.usbharu.hideout.application.config.ApplicationConfig +import org.junit.jupiter.api.Test +import java.net.URI + +class RemoteActorCheckDomainServiceTest { + @Test + fun リモートã®ãƒ‰ãƒ¡ã‚¤ãƒ³ãªã‚‰trueã‚’è¿”ã™() { + val actor = + + RemoteActorCheckDomainService(ApplicationConfig(URI.create("https://example.com").toURL())).isRemoteActor() + } +} \ No newline at end of file From 58077d13f69efd093b86fbded59d392297dd5860 Mon Sep 17 00:00:00 2001 From: usbharu Date: Wed, 29 May 2024 12:31:51 +0900 Subject: [PATCH 05/54] wip --- .../hideout/core/domain/model/actor/Actor2.kt | 4 +-- .../core/domain/model/actor/Actor2Test.kt | 18 ++++++++++- .../domain/model/actor/TestActor2Factory.kt | 2 +- .../RemoteActorCheckDomainServiceTest.kt | 31 +++++++++++++++++-- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt index db95bacc..08cf77c6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -47,7 +47,7 @@ class Actor2 private constructor( var lastPostDate: Instant? = null, suspend: Boolean, var lastUpdate: Instant = createdAt, - alsoKnownAs: List = emptyList(), + alsoKnownAs: Set = emptySet(), moveTo: ActorId? = null, ) : DomainEventStorable() { @@ -64,7 +64,7 @@ class Actor2 private constructor( var alsoKnownAs = alsoKnownAs set(value) { require(value.find { it == id } == null) - field = value.distinct() + field = value } var moveTo = moveTo diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt index bf053cfa..00272d61 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt @@ -1,10 +1,26 @@ package dev.usbharu.hideout.core.domain.model.actor import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows class Actor2Test { @Test fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œã¦ã¯ã„ã‘ãªã„() { - TestActor2Factory.create() + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + assertThrows { + actor.alsoKnownAs = setOf(actor.id) + } } + + @Test + fun moveToã«è‡ªåˆ†è‡ªèº«ãŒè¨­å®šã•ã‚Œã¦ã¯ã„ã‘ãªã„() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + assertThrows { + actor.moveTo = actor.id + } + } + + } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt index 6ff26446..7a30bdc6 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt @@ -20,7 +20,7 @@ object TestActor2Factory : Actor2.Actor2Factory() { outbox: URI = URI.create("https://example.com/$id/outbox"), uri: URI = URI.create("https://example.com/$id"), publicKey: ActorPublicKey, - privateKey: ActorPrivateKey?, + privateKey: ActorPrivateKey? = null, createdAt: Instant = Instant.now(), keyId: String = "https://example.com/$id#key-id", followersEndpoint: URI = URI.create("https://example.com/$id/followers"), diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt index 8cd71362..7903a999 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt @@ -1,14 +1,41 @@ package dev.usbharu.hideout.core.domain.service.actor import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey +import dev.usbharu.hideout.core.domain.model.actor.TestActor2Factory import org.junit.jupiter.api.Test import java.net.URI +import kotlin.test.assertFalse +import kotlin.test.assertTrue class RemoteActorCheckDomainServiceTest { @Test fun リモートã®ãƒ‰ãƒ¡ã‚¤ãƒ³ãªã‚‰trueã‚’è¿”ã™() { - val actor = + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) - RemoteActorCheckDomainService(ApplicationConfig(URI.create("https://example.com").toURL())).isRemoteActor() + val remoteActor = RemoteActorCheckDomainService( + ApplicationConfig( + URI.create("https://local.example.com").toURL() + ) + ).isRemoteActor( + actor + ) + + assertTrue(remoteActor) + } + + @Test + fun ローカルã®Actorãªã‚‰falseã‚’è¿”ã™() { + val actor = TestActor2Factory.create(domain = "local.example.com", publicKey = ActorPublicKey("")) + + val localActor = RemoteActorCheckDomainService( + ApplicationConfig( + URI.create("https://local.example.com").toURL() + ) + ).isRemoteActor( + actor + ) + + assertFalse(localActor) } } \ No newline at end of file From 99c27e45c240fb843b88f59dc6eed9554c51c8b3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 27 May 2024 21:59:06 +0900 Subject: [PATCH 06/54] wip --- .../core/domain/event/actor/ActorEvent.kt | 41 ++++ .../ActorInstanceRelationshipEvent.kt | 47 ++++ .../domain/event/instance/InstanceEvent.kt | 36 +++ .../core/domain/event/post/PostEvent.kt | 39 +++ .../hideout/core/domain/model/actor/Actor2.kt | 121 +++++++++ .../domain/model/actor/Actor2Repository.kt | 23 ++ .../domain/model/actor/ActorDescription.kt | 27 +++ .../core/domain/model/actor/ActorId.kt | 24 ++ .../core/domain/model/actor/ActorKeyId.kt | 20 ++ .../core/domain/model/actor/ActorName.kt | 20 ++ .../domain/model/actor/ActorPostsCount.kt | 27 +++ .../domain/model/actor/ActorPrivateKey.kt | 20 ++ .../core/domain/model/actor/ActorPublicKey.kt | 20 ++ .../model/actor/ActorRelationshipCount.kt | 27 +++ .../domain/model/actor/ActorScreenName.kt | 28 +++ .../ActorInstanceRelationship.kt | 88 +++++++ .../domain/model/deletedActor/DeletedActor.kt | 14 +- .../model/deletedActor/DeletedActorId.kt | 20 ++ .../model/emoji/CustomEmojiRepository.kt | 1 + .../core/domain/model/emoji/EmojiId.kt | 20 ++ .../core/domain/model/instance/Instance.kt | 55 +++-- .../model/instance/InstanceDescription.kt | 20 ++ .../core/domain/model/instance/InstanceId.kt | 20 ++ .../model/instance/InstanceModerationNote.kt | 20 ++ .../domain/model/instance/InstanceName.kt | 20 ++ .../model/instance/InstanceRepository.kt | 8 +- .../domain/model/instance/InstanceSoftware.kt | 20 ++ .../domain/model/instance/InstanceVersion.kt | 20 ++ .../core/domain/model/media/MediaId.kt | 20 ++ .../hideout/core/domain/model/post/Post2.kt | 229 ++++++++++++++++++ .../core/domain/model/post/Post2Repository.kt | 23 ++ .../core/domain/model/post/PostContent.kt | 34 +++ .../hideout/core/domain/model/post/PostId.kt | 20 ++ .../core/domain/model/post/PostOverview.kt | 20 ++ .../core/domain/model/shared/Domain.kt | 20 ++ .../model/shared/domainevent/DomainEvent.kt | 37 +++ .../shared/domainevent/DomainEventBody.kt | 23 ++ .../shared/domainevent/DomainEventStorable.kt | 29 +++ .../domain/model/userdetails/UserDetail.kt | 35 ++- .../userdetails/UserDetailHashedPassword.kt | 20 ++ .../actor/RemoteActorCheckDomainService.kt | 30 +++ .../actor/local/LocalActorDomainService.kt | 25 ++ .../ActorInstanceRelationshipDomainService.kt | 21 ++ .../service/userdetail/PasswordEncoder.kt | 21 ++ .../userdetail/UserDetailDomainService.kt | 25 ++ .../DeletedActorRepositoryImpl.kt | 6 +- .../factory/Actor2FactoryImpl.kt | 66 +++++ .../factory/ActorDescriptionFactoryImpl.kt | 41 ++++ .../factory/ActorScreenNameFactoryImpl.kt | 45 ++++ .../factory/PostContentFactoryImpl.kt | 35 +++ .../infrastructure/factory/PostFactoryImpl.kt | 67 +++++ .../core/service/user/UserServiceImpl.kt | 6 +- .../DeleteLocalActorApplicationService.kt | 25 ++ .../MigrationLocalActorApplicationService.kt | 25 ++ .../core/usecase/actor/RegisterLocalActor.kt | 22 ++ .../RegisterLocalActorApplicationService.kt | 65 +++++ .../SuspendLocalActorApplicationService.kt | 28 +++ .../UnsuspendLocalActorApplicationService.kt | 23 ++ .../post/DeleteLocalPostApplicationService.kt | 30 +++ .../core/usecase/post/RegisterLocalPost.kt | 30 +++ .../RegisterLocalPostApplicationService.kt | 51 ++++ .../core/usecase/post/UpdateLocalNote.kt | 25 ++ .../post/UpdateLocalNoteApplicationService.kt | 45 ++++ 63 files changed, 2080 insertions(+), 33 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt new file mode 100644 index 00000000..62e0d9e9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.actor + +import dev.usbharu.hideout.core.domain.model.actor.Actor2 +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class ActorDomainEventFactory(private val actor: Actor2) { + fun createEvent(actorEvent: ActorEvent): DomainEvent { + return DomainEvent.create( + actorEvent.eventName, + ActorEventBody(actor) + ) + } +} + +class ActorEventBody(actor: Actor2) : DomainEventBody( + mapOf( + "actor" to actor + ) +) + +enum class ActorEvent(val eventName: String) { + update("ActorUpdate"), + delete("ActorDelete"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt new file mode 100644 index 00000000..708c2f4d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.actorinstancerelationship + +import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class ActorInstanceRelationshipDomainEventFactory(private val actorInstanceRelationship: ActorInstanceRelationship) { + fun createEvent(actorInstanceRelationshipEvent: ActorInstanceRelationshipEvent): DomainEvent { + return DomainEvent.create( + actorInstanceRelationshipEvent.eventName, + ActorInstanceRelationshipEventBody(actorInstanceRelationship) + ) + } +} + +class ActorInstanceRelationshipEventBody(actorInstanceRelationship: ActorInstanceRelationship) : + DomainEventBody( + mapOf( + "actorId" to actorInstanceRelationship.actorId, + "instanceId" to actorInstanceRelationship.instanceId, + "muting" to actorInstanceRelationship.isMuting(), + "blocking" to actorInstanceRelationship.isBlocking(), + "doNotSendPrivate" to actorInstanceRelationship.isDoNotSendPrivate(), + ) + ) + +enum class ActorInstanceRelationshipEvent(val eventName: String) { + block("ActorInstanceBlock"), + mute("ActorInstanceMute"), + unmute("ActorInstanceUnmute"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt new file mode 100644 index 00000000..101c1cba --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.instance + +import dev.usbharu.hideout.core.domain.model.instance.Instance +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class InstanceEventFactory(private val instance: Instance) { + fun createEvent(event: InstanceEvent): DomainEvent { + return DomainEvent.create( + event.eventName, + InstanceEventBody(instance) + ) + } +} + +class InstanceEventBody(instance: Instance) : DomainEventBody(mapOf("instance" to instance)) + +enum class InstanceEvent(val eventName: String) { + update("InstanceUpdate") +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt new file mode 100644 index 00000000..e5c0812f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.post + +import dev.usbharu.hideout.core.domain.model.post.Post2 +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventBody + +class PostDomainEventFactory(private val post: Post2) { + fun createEvent(postEvent: PostEvent): DomainEvent { + return DomainEvent.create( + postEvent.eventName, + PostEventBody(post) + ) + } +} + +class PostEventBody(post: Post2) : DomainEventBody(mapOf("post" to post)) + +enum class PostEvent(val eventName: String) { + delete("PostDelete"), + update("PostUpdate"), + create("PostCreate"), + checkUpdate("PostCheckUpdate"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt new file mode 100644 index 00000000..cc4f96ae --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.delete +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.update +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.shared.domainevent.DomainEventStorable +import java.net.URI +import java.time.Instant + +class Actor2 private constructor( + val id: ActorId, + val name: ActorName, + val domain: Domain, + screenName: ActorScreenName, + description: ActorDescription, + val inbox: URI, + val outbox: URI, + val url: URI, + val publicKey: ActorPublicKey, + val privateKey: ActorPrivateKey? = null, + val createdAt: Instant, + val keyId: ActorKeyId, + val followersEndpoint: URI, + val followingEndpoint: URI, + val instance: InstanceId, + var locked: Boolean, + var followersCount: ActorRelationshipCount?, + var followingCount: ActorRelationshipCount?, + var postsCount: ActorPostsCount, + var lastPostDate: Instant? = null, + var suspend: Boolean, +) : DomainEventStorable() { + + val emojis + get() = screenName.emojis + + + var description = description + set(value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + field = value + } + var screenName = screenName + set(value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + field = value + } + + + fun delete() { + addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) + } + + abstract class Actor2Factory { + protected suspend fun create( + id: ActorId, + name: ActorName, + domain: Domain, + screenName: ActorScreenName, + description: ActorDescription, + inbox: URI, + outbox: URI, + url: URI, + publicKey: ActorPublicKey, + privateKey: ActorPrivateKey? = null, + createdAt: Instant, + keyId: ActorKeyId, + followersEndpoint: URI, + followingEndpoint: URI, + instance: InstanceId, + locked: Boolean, + followersCount: ActorRelationshipCount, + followingCount: ActorRelationshipCount, + postsCount: ActorPostsCount, + lastPostDate: Instant? = null, + suspend: Boolean, + ): Actor2 { + return Actor2( + id = id, + name = name, + domain = domain, + screenName = screenName, + description = description, + inbox = inbox, + outbox = outbox, + url = url, + publicKey = publicKey, + privateKey = privateKey, + createdAt = createdAt, + keyId = keyId, + followersEndpoint = followersEndpoint, + followingEndpoint = followingEndpoint, + instance = instance, + locked = locked, + followersCount = followersCount, + followingCount = followingCount, + postsCount = postsCount, + lastPostDate = lastPostDate, + suspend = suspend + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt new file mode 100644 index 00000000..10c21a63 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +interface Actor2Repository { + suspend fun save(actor: Actor2): Actor2 + suspend fun deleteById(actor: ActorId) + suspend fun findById(id: ActorId): Actor2? +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt new file mode 100644 index 00000000..3050836d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId + + +class ActorDescription private constructor(private val description: String, private val emojis: List) { + abstract class ActorDescriptionFactory { + protected suspend fun create(description: String, emojis: List): ActorDescription = + ActorDescription(description, emojis) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt new file mode 100644 index 00000000..73f5a022 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorId(val id: Long) { + companion object { + val ghost = ActorId(0L) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt new file mode 100644 index 00000000..776ceda2 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorKeyId(private val keyId: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt new file mode 100644 index 00000000..77983e4e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorName(val name: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt new file mode 100644 index 00000000..ae44b411 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorPostsCount(private val postsCount: Long) { + init { + require(0 <= this.postsCount) { "Posts count must be greater than 0" } + } + + operator fun inc() = ActorPostsCount(postsCount + 1) + operator fun dec() = ActorPostsCount(postsCount - 1) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt new file mode 100644 index 00000000..a909cfa3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorPrivateKey(private val privateKey: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt new file mode 100644 index 00000000..5428c231 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorPublicKey(private val publicKey: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt new file mode 100644 index 00000000..c55be570 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +@JvmInline +value class ActorRelationshipCount(private val followersCount: Int) { + init { + require(0 <= followersCount) { "Followers count must be > 0" } + } + + operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(followersCount + 1) + operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(followersCount - 1) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt new file mode 100644 index 00000000..83ed08aa --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId + + +class ActorScreenName private constructor(val screenName: String, val emojis: List) { + + abstract class ActorScreenNameFactory { + protected suspend fun create(screenName: String, emojis: List): ActorScreenName = + ActorScreenName(screenName, emojis) + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt new file mode 100644 index 00000000..82ce5599 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actorinstancerelationship + +import dev.usbharu.hideout.core.domain.event.actorinstancerelationship.ActorInstanceRelationshipDomainEventFactory +import dev.usbharu.hideout.core.domain.event.actorinstancerelationship.ActorInstanceRelationshipEvent.* +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.shared.domainevent.DomainEventStorable + +data class ActorInstanceRelationship( + val actorId: ActorId, + val instanceId: InstanceId, + private var blocking: Boolean = false, + private var muting: Boolean = false, + private var doNotSendPrivate: Boolean = false, +) : DomainEventStorable() { + fun block(): ActorInstanceRelationship { + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(block)) + blocking = true + return this + } + + fun unblock(): ActorInstanceRelationship { + blocking = false + return this + } + + fun mute(): ActorInstanceRelationship { + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(mute)) + muting = true + return this + } + + fun unmute(): ActorInstanceRelationship { + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(unmute)) + muting = false + return this + } + + fun doNotSendPrivate(): ActorInstanceRelationship { + doNotSendPrivate = true + return this + } + + fun doSendPrivate(): ActorInstanceRelationship { + doNotSendPrivate = false + return this + } + + fun isBlocking() = blocking + + fun isMuting() = muting + + fun isDoNotSendPrivate() = doNotSendPrivate + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ActorInstanceRelationship + + if (actorId != other.actorId) return false + if (instanceId != other.instanceId) return false + + return true + } + + override fun hashCode(): Int { + var result = actorId.hashCode() + result = 31 * result + instanceId.hashCode() + return result + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt index 0455435f..61e15775 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt @@ -16,13 +16,17 @@ package dev.usbharu.hideout.core.domain.model.deletedActor +import dev.usbharu.hideout.core.domain.model.actor.ActorName +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey +import dev.usbharu.hideout.core.domain.model.shared.Domain +import java.net.URI import java.time.Instant data class DeletedActor( - val id: Long, - val name: String, - val domain: String, - val apiId: String, - val publicKey: String, + val id: DeletedActorId, + val name: ActorName, + val domain: Domain, + val apId: URI, + val publicKey: ActorPublicKey, val deletedAt: Instant, ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt new file mode 100644 index 00000000..b8cfbc98 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.deletedActor + +@JvmInline +value class DeletedActorId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt index eaac7bd5..de339d34 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt @@ -22,4 +22,5 @@ interface CustomEmojiRepository { suspend fun findById(id: Long): CustomEmoji? suspend fun delete(customEmoji: CustomEmoji) suspend fun findByNameAndDomain(name: String, domain: String): CustomEmoji? + suspend fun findByNamesAndDomain(names: List, domain: String): List } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt new file mode 100644 index 00000000..f5d537c2 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.emoji + +@JvmInline +value class EmojiId(private val emojiId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index 3d0aac30..d8ea3516 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -16,19 +16,46 @@ package dev.usbharu.hideout.core.domain.model.instance +import dev.usbharu.hideout.core.domain.event.instance.InstanceEvent +import dev.usbharu.hideout.core.domain.event.instance.InstanceEventFactory +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventStorable +import java.net.URI import java.time.Instant -data class Instance( - val id: Long, - val name: String, - val description: String, - val url: String, - val iconUrl: String, - val sharedInbox: String?, - val software: String, - val version: String, - val isBlocked: Boolean, - val isMuted: Boolean, - val moderationNote: String, - val createdAt: Instant -) +class Instance( + val id: InstanceId, + var name: InstanceName, + var description: InstanceDescription, + val url: URI, + iconUrl: URI, + var sharedInbox: URI?, + var software: InstanceSoftware, + var version: InstanceVersion, + var isBlocked: Boolean, + var isMuted: Boolean, + var moderationNote: InstanceModerationNote, + val createdAt: Instant, +) : DomainEventStorable() { + + + var iconUrl = iconUrl + set(value) { + addDomainEvent(InstanceEventFactory(this).createEvent(InstanceEvent.update)) + field = value + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Instance + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt new file mode 100644 index 00000000..8a6f2084 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceDescription.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceDescription(val description: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt new file mode 100644 index 00000000..de5e4277 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceId(private val instanceId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt new file mode 100644 index 00000000..6439f75c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceModerationNote.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceModerationNote(val note: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt new file mode 100644 index 00000000..5133566e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceName.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceName(val name: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt index 121e5adc..7ab21f8f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt @@ -16,10 +16,12 @@ package dev.usbharu.hideout.core.domain.model.instance +import java.net.URI + interface InstanceRepository { - suspend fun generateId(): Long + suspend fun generateId(): InstanceId suspend fun save(instance: Instance): Instance - suspend fun findById(id: Long): Instance? + suspend fun findById(id: InstanceId): Instance? suspend fun delete(instance: Instance) - suspend fun findByUrl(url: String): Instance? + suspend fun findByUrl(url: URI): Instance? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt new file mode 100644 index 00000000..30d06746 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceSoftware.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceSoftware(val software: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt new file mode 100644 index 00000000..b8770133 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceVersion.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.instance + +@JvmInline +value class InstanceVersion(val version: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt new file mode 100644 index 00000000..5003f164 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.media + +@JvmInline +value class MediaId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt new file mode 100644 index 00000000..f2b4f990 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory +import dev.usbharu.hideout.core.domain.event.post.PostEvent +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.shared.domainevent.DomainEventStorable +import java.net.URI +import java.time.Instant + +class Post2 private constructor( + val id: PostId, + actorId: ActorId, + overview: PostOverview? = null, + content: PostContent, + val createdAt: Instant, + visibility: Visibility, + val url: URI, + val repostId: PostId?, + val replyId: PostId?, + sensitive: Boolean, + val apId: URI, + deleted: Boolean, + mediaIds: List, + visibleActors: List = emptyList(), + hide: Boolean = false, + moveTo: PostId? = null, +) : DomainEventStorable() { + + var actorId = actorId + private set + get() { + if (deleted) { + return ActorId.ghost + } + return field + } + + var visibility = visibility + set(value) { + require(value != Visibility.DIRECT) + require(field.ordinal >= value.ordinal) + + require(deleted.not()) + + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + var visibleActors = visibleActors + set(value) { + require(deleted.not()) + if (visibility == Visibility.DIRECT) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + field = field.plus(value).distinct() + } + } + + var content = content + get() { + if (hide) { + return PostContent.empty + } + return field + } + set(value) { + require(deleted.not()) + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + var overview = overview + get() { + if (hide) { + return null + } + return field + } + set(value) { + require(deleted.not()) + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + var sensitive = sensitive + set(value) { + require(deleted.not()) + if (field != value) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + } + field = value + } + + val text + get() = content.text + + val emojiIds + get() = content.emojiIds + + var mediaIds = mediaIds + get() { + if (hide) { + return emptyList() + } + return field + } + private set + + fun addMediaIds(mediaIds: List) { + require(deleted.not()) + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + this.mediaIds = this.mediaIds.plus(mediaIds).distinct() + } + + var deleted = deleted + private set + + fun delete() { + if (deleted.not()) { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.delete)) + content = PostContent.empty + overview = null + mediaIds = emptyList() + + } + deleted = true + } + + fun checkUpdate() { + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.checkUpdate)) + } + + fun restore(content: PostContent, overview: PostOverview?, mediaIds: List) { + deleted = false + this.content = content + this.overview = overview + this.mediaIds = mediaIds + } + + var hide = hide + private set + + fun hide() { + hide = true + } + + fun show() { + hide = false + } + + var moveTo = moveTo + private set + + fun moveTo(moveTo: PostId) { + require(this.moveTo == null) + this.moveTo = moveTo + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Post2 + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + abstract class PostFactory { + protected fun create( + id: PostId, + actorId: ActorId, + overview: PostOverview? = null, + content: PostContent, + createdAt: Instant, + visibility: Visibility, + url: URI, + repostId: PostId?, + replyId: PostId?, + sensitive: Boolean, + apId: URI, + deleted: Boolean, + mediaIds: List, + hide: Boolean, + ): Post2 { + return Post2( + id = id, + actorId = actorId, + overview = overview, + content = content, + createdAt = createdAt, + visibility = visibility, + url = url, + repostId = repostId, + replyId = replyId, + sensitive = sensitive, + apId = apId, + deleted = deleted, + mediaIds = mediaIds, + hide = hide + ).apply { addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.create)) } + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt new file mode 100644 index 00000000..91961a6f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +interface Post2Repository { + suspend fun save(post: Post2): Post2 + suspend fun findById(id: PostId): Post2? + suspend fun deleteById(id: PostId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt new file mode 100644 index 00000000..2ed4df88 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId + +class PostContent private constructor(val text: String, val content: String, val emojiIds: List) { + + companion object { + val empty = PostContent("", "", emptyList()) + } + + abstract class PostContentFactory { + protected suspend fun create(text: String, content: String, emojiIds: List): PostContent { + return PostContent( + text, content, emojiIds + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt new file mode 100644 index 00000000..e4682ff1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +@JvmInline +value class PostId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt new file mode 100644 index 00000000..995329b1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.post + +@JvmInline +value class PostOverview(val overview: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt new file mode 100644 index 00000000..a6aaa4c8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared + +@JvmInline +value class Domain(val domain: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt new file mode 100644 index 00000000..53c48d86 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared.domainevent + +import java.time.Instant +import java.util.* + +data class DomainEvent( + private val id: String, + private val name: String, + private val occurredOn: Instant, + private val body: DomainEventBody, +) { + companion object { + fun create(name: String, body: DomainEventBody): DomainEvent { + return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body) + } + + fun reconstruct(id: String, name: String, occurredOn: Instant, body: DomainEventBody): DomainEvent { + return DomainEvent(id, name, occurredOn, body) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt new file mode 100644 index 00000000..cb7dd4d9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared.domainevent + +abstract class DomainEventBody(val map: Map) { + fun toMap(): Map { + return map + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt new file mode 100644 index 00000000..a0da8d06 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.shared.domainevent + +abstract class DomainEventStorable { + private val domainEvents: MutableList = mutableListOf() + + protected fun addDomainEvent(domainEvent: DomainEvent) { + domainEvents.add(domainEvent) + } + + fun clearDomainEvents() = domainEvents.clear() + + fun getDomainEvents(): List = domainEvents.toList() +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt index aabaa0ce..19c32158 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt @@ -16,8 +16,33 @@ package dev.usbharu.hideout.core.domain.model.userdetails -data class UserDetail( - val actorId: Long, - val password: String, - val autoAcceptFolloweeFollowRequest: Boolean -) +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class UserDetail( + val actorId: ActorId, + var password: UserDetailHashedPassword, + var autoAcceptFolloweeFollowRequest: Boolean, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UserDetail + + return actorId == other.actorId + } + + override fun hashCode(): Int { + return actorId.hashCode() + } + + companion object { + fun create( + actorId: ActorId, + password: UserDetailHashedPassword, + autoAcceptFolloweeFollowRequest: Boolean = false, + ): UserDetail { + return UserDetail(actorId, password, autoAcceptFolloweeFollowRequest) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt new file mode 100644 index 00000000..f0dc4399 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailHashedPassword.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.userdetails + +@JvmInline +value class UserDetailHashedPassword(val password: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt new file mode 100644 index 00000000..6c594f1b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.Actor +import org.springframework.stereotype.Service + +interface IRemoteActorCheckDomainService { + fun isRemoteActor(actor: Actor): Boolean +} + +@Service +class RemoteActorCheckDomainService(private val applicationConfig: ApplicationConfig) : IRemoteActorCheckDomainService { + override fun isRemoteActor(actor: Actor): Boolean = actor.domain == applicationConfig.url.host +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt new file mode 100644 index 00000000..677654f7 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.domain.model.actor.ActorPrivateKey +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey + +interface LocalActorDomainService { + suspend fun usernameAlreadyUse(name: String): Boolean + suspend fun generateKeyPair(): Pair +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt new file mode 100644 index 00000000..ac573d8c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actorinstancerelationship + +interface ActorInstanceRelationshipDomainService { + suspend fun block() +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt new file mode 100644 index 00000000..9ff2ee3d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.userdetail + +interface PasswordEncoder { + suspend fun encode(input: String): String +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt new file mode 100644 index 00000000..33db1331 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.userdetail + +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import org.springframework.stereotype.Service + +@Service +class UserDetailDomainService(private val passwordEncoder: PasswordEncoder) { + suspend fun hashPassword(password: String) = UserDetailHashedPassword(passwordEncoder.encode(password)) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt index 41bd924b..a6900563 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt @@ -39,7 +39,7 @@ class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() it[id] = deletedActor.id it[name] = deletedActor.name it[domain] = deletedActor.domain - it[apId] = deletedActor.apiId + it[apId] = deletedActor.apId it[publicKey] = deletedActor.publicKey it[deletedAt] = deletedActor.deletedAt } @@ -47,7 +47,7 @@ class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() DeletedActors.update({ DeletedActors.id eq deletedActor.id }) { it[name] = deletedActor.name it[domain] = deletedActor.domain - it[apId] = deletedActor.apiId + it[apId] = deletedActor.apId it[publicKey] = deletedActor.publicKey it[deletedAt] = deletedActor.deletedAt } @@ -85,7 +85,7 @@ private fun deletedActor(singleOr: ResultRow): DeletedActor { id = singleOr[DeletedActors.id], name = singleOr[DeletedActors.name], domain = singleOr[DeletedActors.domain], - apiId = singleOr[DeletedActors.publicKey], + apId = singleOr[DeletedActors.publicKey], publicKey = singleOr[DeletedActors.apId], deletedAt = singleOr[DeletedActors.deletedAt] ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt new file mode 100644 index 00000000..937744d8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.service.id.IdGenerateService +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 org.springframework.stereotype.Component +import java.net.URI +import java.time.Instant + +@Component +class Actor2FactoryImpl( + private val idGenerateService: IdGenerateService, + private val actorScreenNameFactory: ActorScreenNameFactoryImpl, + private val actorDescriptionFactory: ActorDescriptionFactoryImpl, + private val applicationConfig: ApplicationConfig, +) : Actor2.Actor2Factory() { + suspend fun createLocal( + name: String, + keyPair: Pair, + instanceId: InstanceId, + ): Actor2 { + val actorName = ActorName(name) + val userUrl = "${applicationConfig.url}/users/${actorName.name}" + return super.create( + id = ActorId(idGenerateService.generateId()), + name = actorName, + domain = Domain(applicationConfig.url.host), + screenName = actorScreenNameFactory.create(name), + description = actorDescriptionFactory.create(""), + inbox = URI.create("$userUrl/inbox"), + outbox = URI.create("$userUrl/outbox"), + url = applicationConfig.url.toURI(), + publicKey = keyPair.first, + privateKey = keyPair.second, + createdAt = Instant.now(), + keyId = ActorKeyId("$userUrl#main-key"), + followersEndpoint = URI.create("$userUrl/followers"), + followingEndpoint = URI.create("$userUrl/following"), + instance = instanceId, + locked = false, + followersCount = ActorRelationshipCount(0), + followingCount = ActorRelationshipCount(0), + postsCount = ActorPostsCount(0), + lastPostDate = null, + suspend = false + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt new file mode 100644 index 00000000..a5d107e4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorDescription +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import org.springframework.stereotype.Component + +@Component +class ActorDescriptionFactoryImpl( + private val applicationConfig: ApplicationConfig, + private val emojiRepository: CustomEmojiRepository, +) : ActorDescription.ActorDescriptionFactory() { + val regex = Regex(":(w+):") + suspend fun create(description: String): ActorDescription { + val findAll = regex.findAll(description) + + val emojis = + emojiRepository.findByNamesAndDomain( + findAll.map { it.groupValues[1] }.toList(), + applicationConfig.url.host + ) + return create(description, emojis.map { EmojiId(it.id) }) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt new file mode 100644 index 00000000..b82773e4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorScreenName +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.emoji.EmojiId +import org.springframework.stereotype.Component + +@Component +class ActorScreenNameFactoryImpl( + private val applicationConfig: ApplicationConfig, + private val emojiRepository: CustomEmojiRepository, +) : ActorScreenName.ActorScreenNameFactory() { + val regex = Regex(":(w+):") + + suspend fun create(content: String): ActorScreenName { + + val findAll = regex.findAll(content) + + val emojis = + emojiRepository.findByNamesAndDomain( + findAll.map { it.groupValues[1] }.toList(), + applicationConfig.url.host + ) + return create(content, emojis.map { EmojiId(it.id) }) + + } + +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt new file mode 100644 index 00000000..b8536126 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.core.domain.model.post.PostContent +import dev.usbharu.hideout.core.service.post.PostContentFormatter +import org.springframework.stereotype.Component + +@Component +class PostContentFactoryImpl( + private val postContentFormatter: PostContentFormatter, +) : PostContent.PostContentFactory() { + suspend fun create(content: String): PostContent { + val format = postContentFormatter.format(content) + return super.create( + format.content, + format.html, + emptyList() + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt new file mode 100644 index 00000000..4a3ae815 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.factory + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.service.id.IdGenerateService +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorName +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post2 +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.domain.model.post.Visibility +import org.springframework.stereotype.Component +import java.net.URI +import java.time.Instant + +@Component +class PostFactoryImpl( + private val idGenerateService: IdGenerateService, + private val postContentFactoryImpl: PostContentFactoryImpl, + private val applicationConfig: ApplicationConfig, +) : Post2.PostFactory() { + suspend fun createLocal( + actorId: ActorId, + actorName: ActorName, + overview: PostOverview, + content: String, + visibility: Visibility, + repostId: PostId?, + replyId: PostId?, + sensitive: Boolean, + mediaIds: List, + ): Post2 { + val id = idGenerateService.generateId() + val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName + "/posts/" + id) + return super.create( + PostId(id), + actorId, + overview, + postContentFactoryImpl.create(content), + Instant.now(), + visibility, + url, + repostId, + replyId, + sensitive, + url, + false, + mediaIds + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 889c5257..fbc80538 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -159,7 +159,7 @@ class UserServiceImpl( id = actor.id, name = actor.name, domain = actor.domain, - apiId = actor.url, + apId = actor.url, publicKey = actor.publicKey, deletedAt = Instant.now() ) @@ -179,7 +179,7 @@ class UserServiceImpl( deletedActorRepository.delete(deletedActor) - owlProducer.publishTask(UpdateActorTask(deletedActor.id, deletedActor.apiId)) + owlProducer.publishTask(UpdateActorTask(deletedActor.id, deletedActor.apId)) } override suspend fun deleteLocalUser(userId: Long) { @@ -189,7 +189,7 @@ class UserServiceImpl( id = actor.id, name = actor.name, domain = actor.domain, - apiId = actor.url, + apId = actor.url, publicKey = actor.publicKey, deletedAt = Instant.now() ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt new file mode 100644 index 00000000..c1160fc3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class DeleteLocalActorApplicationService { + suspend fun delete(actorId: ActorId, executor: ActorId) { + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt new file mode 100644 index 00000000..ee853538 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class MigrationLocalActorApplicationService { + suspend fun migration(from: ActorId, to: ActorId, executor: ActorId) { + TODO() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt new file mode 100644 index 00000000..8031a87c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +data class RegisterLocalActor( + val name: String, + val password: String, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt new file mode 100644 index 00000000..7127d8a9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainService +import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService +import dev.usbharu.hideout.core.infrastructure.factory.Actor2FactoryImpl +import org.springframework.stereotype.Service + +@Service +class RegisterLocalActorApplicationService( + private val transaction: Transaction, + private val actorDomainService: LocalActorDomainService, + private val actor2Repository: Actor2Repository, + private val actor2FactoryImpl: Actor2FactoryImpl, + private val instanceRepository: InstanceRepository, + private val applicationConfig: ApplicationConfig, + private val userDetailDomainService: UserDetailDomainService, + private val userDetailRepository: UserDetailRepository, +) { + suspend fun register(registerLocalActor: RegisterLocalActor) { + transaction.transaction { + if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) { + //todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ + throw Exception("Username already exists") + } + val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! + + + val actor = actor2FactoryImpl.createLocal( + registerLocalActor.name, + actorDomainService.generateKeyPair(), + instance.id + ) + actor2Repository.save(actor) + val userDetail = UserDetail.create( + actor.id, + userDetailDomainService.hashPassword(registerLocalActor.password), + ) + userDetailRepository.save(userDetail) + + } + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt new file mode 100644 index 00000000..1961ce0f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +class SuspendLocalActorApplicationService(private val actor2Repository: Actor2Repository) { + suspend fun suspend(actorId: Long, executor: ActorId) { + val findById = actor2Repository.findById(ActorId(actorId))!! + + findById.suspend = true + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt new file mode 100644 index 00000000..e87d9b0d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +interface UnsuspendLocalActorApplicationService { + suspend fun unsuspend(actorId: ActorId, executor: ActorId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt new file mode 100644 index 00000000..80fd74ca --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.core.domain.model.post.Post2Repository +import dev.usbharu.hideout.core.domain.model.post.PostId +import org.springframework.stereotype.Service + +@Service +class DeleteLocalPostApplicationService(private val postRepository: Post2Repository) { + suspend fun delete(postId: Long) { + val findById = postRepository.findById(PostId(postId))!! + findById.delete() + postRepository.save(findById) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt new file mode 100644 index 00000000..025991d9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.core.domain.model.post.Visibility + +data class RegisterLocalPost( + val actorId: Long, + val content: String, + val overview: String, + val visibility: Visibility, + val repostId: Long?, + val replyId: Long?, + val sensitive: Boolean, + val mediaIds: List, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt new file mode 100644 index 00000000..e08bffb2 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post2Repository +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl +import org.springframework.stereotype.Service + +@Service +class RegisterLocalPostApplicationService( + private val postFactory: PostFactoryImpl, + private val actor2Repository: Actor2Repository, + private val postRepository: Post2Repository, +) { + suspend fun register(registerLocalPost: RegisterLocalPost) { + + val actorId = ActorId(registerLocalPost.actorId) + val post = postFactory.createLocal( + actorId, + actor2Repository.findById(actorId)!!.name, + PostOverview(registerLocalPost.overview), + registerLocalPost.content, + registerLocalPost.visibility, + registerLocalPost.repostId?.let { PostId(it) }, + registerLocalPost.replyId?.let { PostId(it) }, + registerLocalPost.sensitive, + registerLocalPost.mediaIds.map { MediaId(it) } + ) + + postRepository.save(post) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt new file mode 100644 index 00000000..318c010a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +data class UpdateLocalNote( + val postId: Long, + val overview: String?, + val content: String, + val sensitive: Boolean, + val mediaIds: List, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt new file mode 100644 index 00000000..d7aaa905 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.usecase.post + +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post2Repository +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl +import org.springframework.stereotype.Service + +@Service +class UpdateLocalNoteApplicationService( + private val transaction: Transaction, + private val postRepository: Post2Repository, + private val postContentFactoryImpl: PostContentFactoryImpl, +) { + suspend fun update(updateLocalNote: UpdateLocalNote) { + transaction.transaction { + val post = postRepository.findById(PostId(updateLocalNote.postId))!! + + post.content = postContentFactoryImpl.create(updateLocalNote.content) + post.overview = updateLocalNote.overview?.let { PostOverview(it) } + post.mediaIds = updateLocalNote.mediaIds.map { MediaId(it) } + post.sensitive = updateLocalNote.sensitive + + postRepository.save(post) + } + } +} \ No newline at end of file From 6b80d81410a3900dd052aefe32c985131ead5b56 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 May 2024 00:18:44 +0900 Subject: [PATCH 07/54] wip --- .../core/domain/event/actor/ActorEvent.kt | 4 ++ .../hideout/core/domain/model/actor/Actor2.kt | 43 ++++++++++++++++--- .../domain/model/actor/Actor2Repository.kt | 2 +- .../domain/model/actor/ActorDescription.kt | 2 +- .../core/domain/model/post/Post2Repository.kt | 4 ++ .../domain/model/userdetails/UserDetail.kt | 38 ++++++++++------ .../domain/model/userdetails/UserDetailId.kt | 20 +++++++++ .../LocalActorMigrationCheckDomainService.kt | 35 +++++++++++++++ .../DeleteLocalActorApplicationService.kt | 17 +++++++- .../MigrationLocalActorApplicationService.kt | 36 ++++++++++++++-- .../RegisterLocalActorApplicationService.kt | 8 +++- .../SuspendLocalActorApplicationService.kt | 18 ++++++-- .../UnsuspendLocalActorApplicationService.kt | 18 +++++++- 13 files changed, 211 insertions(+), 34 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt index 62e0d9e9..a35fda34 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -38,4 +38,8 @@ class ActorEventBody(actor: Actor2) : DomainEventBody( enum class ActorEvent(val eventName: String) { update("ActorUpdate"), delete("ActorDelete"), + checkUpdate("ActorCheckUpdate"), + move("ActorMove"), + actorSuspend("ActorSuspend"), + actorUnsuspend("ActorUnsuspend"), } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt index cc4f96ae..06655269 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -17,8 +17,7 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory -import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.delete -import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.update +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.* 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.shared.domainevent.DomainEventStorable @@ -38,20 +37,46 @@ class Actor2 private constructor( val privateKey: ActorPrivateKey? = null, val createdAt: Instant, val keyId: ActorKeyId, - val followersEndpoint: URI, - val followingEndpoint: URI, + val followersEndpoint: URI?, + val followingEndpoint: URI?, val instance: InstanceId, var locked: Boolean, var followersCount: ActorRelationshipCount?, var followingCount: ActorRelationshipCount?, var postsCount: ActorPostsCount, var lastPostDate: Instant? = null, - var suspend: Boolean, + suspend: Boolean, + var lastUpdate: Instant = createdAt, + alsoKnownAs: List = emptyList(), + moveTo: ActorId? = null, ) : DomainEventStorable() { - val emojis - get() = screenName.emojis + var suspend = suspend + set(value) { + if (field != value && value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(actorSuspend)) + } else if (field != value && !value) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(actorUnsuspend)) + } + field = value + } + var alsoKnownAs = alsoKnownAs + set(value) { + require(value.find { it == id } == null) + field = value.distinct() + } + + var moveTo = moveTo + set(value) { + require(moveTo != id) + addDomainEvent(ActorDomainEventFactory(this).createEvent(move)) + field = value + } + + + val emojis + get() = screenName.emojis + description.emojis var description = description set(value) { @@ -69,6 +94,10 @@ class Actor2 private constructor( addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) } + fun checkUpdate() { + addDomainEvent(ActorDomainEventFactory(this).createEvent(checkUpdate)) + } + abstract class Actor2Factory { protected suspend fun create( id: ActorId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt index 10c21a63..ddfccccd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt @@ -18,6 +18,6 @@ package dev.usbharu.hideout.core.domain.model.actor interface Actor2Repository { suspend fun save(actor: Actor2): Actor2 - suspend fun deleteById(actor: ActorId) + suspend fun delete(actor: Actor2) suspend fun findById(id: ActorId): Actor2? } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt index 3050836d..4064d241 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -19,7 +19,7 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.core.domain.model.emoji.EmojiId -class ActorDescription private constructor(private val description: String, private val emojis: List) { +class ActorDescription private constructor(val description: String, val emojis: List) { abstract class ActorDescriptionFactory { protected suspend fun create(description: String, emojis: List): ActorDescription = ActorDescription(description, emojis) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt index 91961a6f..f2bffd9a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt @@ -16,8 +16,12 @@ package dev.usbharu.hideout.core.domain.model.post +import dev.usbharu.hideout.core.domain.model.actor.ActorId + interface Post2Repository { suspend fun save(post: Post2): Post2 + suspend fun saveAll(posts: List): List suspend fun findById(id: PostId): Post2? + suspend fun findByActorId(id: ActorId): List suspend fun deleteById(id: PostId) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt index 19c32158..52016f71 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt @@ -17,32 +17,44 @@ package dev.usbharu.hideout.core.domain.model.userdetails import dev.usbharu.hideout.core.domain.model.actor.ActorId +import java.time.Instant -class UserDetail( +class UserDetail private constructor( + val id: UserDetailId, val actorId: ActorId, var password: UserDetailHashedPassword, var autoAcceptFolloweeFollowRequest: Boolean, + var lastMigration: Instant? = null, ) { + + companion object { + fun create( + id: UserDetailId, + actorId: ActorId, + password: UserDetailHashedPassword, + autoAcceptFolloweeFollowRequest: Boolean = false, + lastMigration: Instant? = null, + ): UserDetail { + return UserDetail( + id, + actorId, + password, + autoAcceptFolloweeFollowRequest, + lastMigration + ) + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as UserDetail - return actorId == other.actorId + return id == other.id } override fun hashCode(): Int { - return actorId.hashCode() - } - - companion object { - fun create( - actorId: ActorId, - password: UserDetailHashedPassword, - autoAcceptFolloweeFollowRequest: Boolean = false, - ): UserDetail { - return UserDetail(actorId, password, autoAcceptFolloweeFollowRequest) - } + return id.hashCode() } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt new file mode 100644 index 00000000..cc048546 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.userdetails + +@JvmInline +value class UserDetailId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt new file mode 100644 index 00000000..01fd5aeb --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.domain.model.actor.Actor2 + +interface LocalActorMigrationCheckDomainService { + suspend fun canAccountMigration(from: Actor2, to: Actor2): AccountMigrationCheck +} + +sealed class AccountMigrationCheck( + val canMigration: Boolean, +) { + class CanAccountMigration : AccountMigrationCheck(true) + + class CircularReferences(val message: String) : AccountMigrationCheck(false) + + class SelfReferences : AccountMigrationCheck(false) + + class AlreadyMoved(val message: String) : AccountMigrationCheck(false) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt index c1160fc3..be22d34d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt @@ -16,10 +16,23 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import org.springframework.stereotype.Service -class DeleteLocalActorApplicationService { - suspend fun delete(actorId: ActorId, executor: ActorId) { +@Service +class DeleteLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, +) { + suspend fun delete(actorId: Long, executor: ActorId) { + transaction.transaction { + val id = ActorId(actorId) + val findById = actor2Repository.findById(id)!! + findById.delete() + actor2Repository.delete(findById) + } } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt index ee853538..a7dc73bd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -16,10 +16,40 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* +import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService +import org.springframework.stereotype.Service + +@Service +class MigrationLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, + private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, +) { + suspend fun migration(from: Long, to: Long, executor: ActorId) { + transaction.transaction { + + val fromActorId = ActorId(from) + val toActorId = ActorId(to) + + val fromActor = actor2Repository.findById(fromActorId)!! + val toActor = actor2Repository.findById(toActorId)!! + + val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor) + when (canAccountMigration) { + is AlreadyMoved -> TODO() + is CanAccountMigration -> { + fromActor.moveTo = toActorId + actor2Repository.save(fromActor) + } + + is CircularReferences -> TODO() + is SelfReferences -> TODO() + } + } -class MigrationLocalActorApplicationService { - suspend fun migration(from: ActorId, to: ActorId, executor: ActorId) { - TODO() } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt index 7127d8a9..d93a7c0a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt @@ -18,9 +18,11 @@ package dev.usbharu.hideout.core.usecase.actor import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository 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.actor.local.LocalActorDomainService import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService @@ -37,6 +39,7 @@ class RegisterLocalActorApplicationService( private val applicationConfig: ApplicationConfig, private val userDetailDomainService: UserDetailDomainService, private val userDetailRepository: UserDetailRepository, + private val idGenerateService: IdGenerateService, ) { suspend fun register(registerLocalActor: RegisterLocalActor) { transaction.transaction { @@ -54,8 +57,9 @@ class RegisterLocalActorApplicationService( ) actor2Repository.save(actor) val userDetail = UserDetail.create( - actor.id, - userDetailDomainService.hashPassword(registerLocalActor.password), + id = UserDetailId(idGenerateService.generateId()), + actorId = actor.id, + password = userDetailDomainService.hashPassword(registerLocalActor.password), ) userDetailRepository.save(userDetail) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt index 1961ce0f..08c78483 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt @@ -16,13 +16,25 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import org.springframework.stereotype.Service -class SuspendLocalActorApplicationService(private val actor2Repository: Actor2Repository) { +@Service +class SuspendLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, +) { suspend fun suspend(actorId: Long, executor: ActorId) { - val findById = actor2Repository.findById(ActorId(actorId))!! + transaction.transaction { + + val id = ActorId(actorId) + + val findById = actor2Repository.findById(id)!! + findById.suspend = true + } + - findById.suspend = true } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt index e87d9b0d..962e9e2b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt @@ -16,8 +16,22 @@ package dev.usbharu.hideout.core.usecase.actor +import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import org.springframework.stereotype.Service -interface UnsuspendLocalActorApplicationService { - suspend fun unsuspend(actorId: ActorId, executor: ActorId) +@Service +class UnsuspendLocalActorApplicationService( + private val transaction: Transaction, + private val actor2Repository: Actor2Repository, +) { + suspend fun unsuspend(actorId: Long, executor: Long) { + transaction.transaction { + val findById = actor2Repository.findById(ActorId(actorId))!! + + findById.suspend = false + } + + } } \ No newline at end of file From 7f89701077cd95c9284ea35f029c708c34d4969f Mon Sep 17 00:00:00 2001 From: usbharu Date: Tue, 28 May 2024 17:54:17 +0900 Subject: [PATCH 08/54] wip --- .../actor/local/LocalActorMigrationCheckDomainService.kt | 2 ++ .../actor/MigrationLocalActorApplicationService.kt | 1 + .../actor/SetAlsoKnownAsLocalActorApplicationService.kt | 8 ++++++++ 3 files changed, 11 insertions(+) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt index 01fd5aeb..dfa8c9c5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt @@ -32,4 +32,6 @@ sealed class AccountMigrationCheck( class SelfReferences : AccountMigrationCheck(false) class AlreadyMoved(val message: String) : AccountMigrationCheck(false) + + class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt index a7dc73bd..9384eff1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -48,6 +48,7 @@ class MigrationLocalActorApplicationService( is CircularReferences -> TODO() is SelfReferences -> TODO() + is AlsoKnownAsNotFound -> TODO() } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt new file mode 100644 index 00000000..772385d8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.usecase.actor + +import org.springframework.stereotype.Service + +@Service +interface SetAlsoKnownAsLocalActorApplicationService { + suspend fun setAlsoKnownAs(actorId: Long, alsoKnownAs: List) +} \ No newline at end of file From 671d1e4f9e1eb0302da13b578b96332e4c9ba07f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 1 Jun 2024 11:39:26 +0900 Subject: [PATCH 09/54] wip --- .../hideout/core/domain/model/actor/Actor2.kt | 6 +- .../domain/model/actor/ActorDescription.kt | 3 + .../core/domain/model/actor/ActorKeyId.kt | 2 +- .../core/domain/model/actor/ActorName.kt | 4 + .../domain/model/actor/ActorPostsCount.kt | 2 +- .../domain/model/actor/ActorPrivateKey.kt | 2 +- .../core/domain/model/actor/ActorPublicKey.kt | 2 +- .../model/actor/ActorRelationshipCount.kt | 8 +- .../domain/model/actor/ActorScreenName.kt | 3 + .../ActorInstanceRelationshipRepository.kt | 22 +++++ .../deletedActor/DeletedActorRepository.kt | 2 +- .../core/domain/model/instance/InstanceId.kt | 2 +- .../core/domain/model/shared/Domain.kt | 6 +- .../ExposedActor2Repository.kt | 91 +++++++++++++++++-- .../SpringFrameworkDomainEventPublisher.kt | 30 ++++++ .../MigrationLocalActorApplicationService.kt | 2 +- .../resources/db/migration/V1__Init_DB.sql | 10 +- .../actor/{Actor2Test.kt => Actors2Test.kt} | 2 +- 18 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt rename hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/{Actor2Test.kt => Actors2Test.kt} (96%) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt index 08cf77c6..78cc0864 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt @@ -44,9 +44,9 @@ class Actor2 private constructor( var followersCount: ActorRelationshipCount?, var followingCount: ActorRelationshipCount?, var postsCount: ActorPostsCount, - var lastPostDate: Instant? = null, + var lastPostAt: Instant? = null, suspend: Boolean, - var lastUpdate: Instant = createdAt, + var lastUpdateAt: Instant = createdAt, alsoKnownAs: Set = emptySet(), moveTo: ActorId? = null, ) : DomainEventStorable() { @@ -142,7 +142,7 @@ class Actor2 private constructor( followersCount = followersCount, followingCount = followingCount, postsCount = postsCount, - lastPostDate = lastPostDate, + lastPostAt = lastPostDate, suspend = suspend ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt index 4064d241..8711377e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -20,6 +20,9 @@ import dev.usbharu.hideout.core.domain.model.emoji.EmojiId class ActorDescription private constructor(val description: String, val emojis: List) { + companion object { + val length = 10000 + } abstract class ActorDescriptionFactory { protected suspend fun create(description: String, emojis: List): ActorDescription = ActorDescription(description, emojis) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt index 776ceda2..5b2fdb84 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorKeyId(private val keyId: String) \ No newline at end of file +value class ActorKeyId(val keyId: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt index b3136f9a..14e4b850 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt @@ -21,4 +21,8 @@ value class ActorName(val name: String) { init { } + + companion object { + val length = 300 + } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt index d2b21221..d76b2f95 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorPostsCount(private val postsCount: Int) { +value class ActorPostsCount(val postsCount: Int) { init { require(0 <= this.postsCount) { "Posts count must be greater than 0" } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt index a909cfa3..8dbb7fad 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorPrivateKey(private val privateKey: String) \ No newline at end of file +value class ActorPrivateKey(val privateKey: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt index 5428c231..f94cb421 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorPublicKey(private val publicKey: String) \ No newline at end of file +value class ActorPublicKey(val publicKey: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt index c55be570..78bdfe09 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt @@ -17,11 +17,11 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorRelationshipCount(private val followersCount: Int) { +value class ActorRelationshipCount(val relationshipCount: Int) { init { - require(0 <= followersCount) { "Followers count must be > 0" } + require(0 <= relationshipCount) { "Followers count must be > 0" } } - operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(followersCount + 1) - operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(followersCount - 1) + operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount + 1) + operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount - 1) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt index 83ed08aa..b8b80cfc 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt @@ -20,6 +20,9 @@ import dev.usbharu.hideout.core.domain.model.emoji.EmojiId class ActorScreenName private constructor(val screenName: String, val emojis: List) { + companion object { + val length = 300 + } abstract class ActorScreenNameFactory { protected suspend fun create(screenName: String, emojis: List): ActorScreenName = diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt new file mode 100644 index 00000000..74c3cc02 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actorinstancerelationship + +interface ActorInstanceRelationshipRepository { + suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship + suspend fun delete(actorInstanceRelationship: ActorInstanceRelationship) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt index 42c3d5b0..530e4738 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt @@ -19,6 +19,6 @@ package dev.usbharu.hideout.core.domain.model.deletedActor interface DeletedActorRepository { suspend fun save(deletedActor: DeletedActor): DeletedActor suspend fun delete(deletedActor: DeletedActor) - suspend fun findById(id: Long): DeletedActor? + suspend fun findById(id: DeletedActorId): DeletedActor? suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt index de5e4277..1ad754d9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.instance @JvmInline -value class InstanceId(private val instanceId: Long) \ No newline at end of file +value class InstanceId(val instanceId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt index a6aaa4c8..95cbff75 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt @@ -17,4 +17,8 @@ package dev.usbharu.hideout.core.domain.model.shared @JvmInline -value class Domain(val domain: String) \ No newline at end of file +value class Domain(val domain: String) { + companion object { + val length = 1000 + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt index 834a332b..1645d927 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt @@ -1,10 +1,12 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.core.domain.model.actor.Actor2 -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository -import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.* +import dev.usbharu.hideout.core.domain.model.shared.Domain 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.jetbrains.exposed.sql.javatime.timestamp import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @@ -21,17 +23,94 @@ class ExposedActor2Repository(override val domainEventPublisher: DomainEventPubl override suspend fun save(actor: Actor2): Actor2 { query { - + Actors2.upsert { + it[id] = actor.id.id + it[name] = actor.name.name + it[domain] = actor.domain.domain + it[screenName] = actor.screenName.screenName + it[description] = actor.description.description + it[inbox] = actor.inbox.toString() + it[outbox] = actor.outbox.toString() + it[url] = actor.outbox.toString() + it[publicKey] = actor.publicKey.publicKey + it[privateKey] = actor.privateKey?.privateKey + it[createdAt] = actor.createdAt + it[keyId] = actor.keyId.keyId + it[following] = actor.followingEndpoint?.toString() + it[followers] = actor.followersEndpoint?.toString() + it[instance] = actor.instance.instanceId + it[locked] = actor.locked + it[followingCount] = actor.followingCount?.relationshipCount + it[followersCount] = actor.followersCount?.relationshipCount + it[postsCount] = actor.postsCount.postsCount + it[lastPostAt] = actor.lastPostAt + it[lastUpdateAt] = actor.lastUpdateAt + it[suspend] = actor.suspend + it[moveTo] = actor.moveTo?.id + it[emojis] = actor.emojis.joinToString(",") + } + Actors2AlsoKnownAs.deleteWhere { + actorId eq actor.id.id + } + Actors2AlsoKnownAs.batchInsert(actor.alsoKnownAs) { + this[Actors2AlsoKnownAs.actorId] = actor.id.id + this[Actors2AlsoKnownAs.alsoKnownAs] = it.id + } } update(actor) return actor } override suspend fun delete(actor: Actor2) { - TODO("Not yet implemented") + query { + Actors2.deleteWhere { id eq actor.id.id } + Actors2AlsoKnownAs.deleteWhere { actorId eq actor.id.id } + } + update(actor) } override suspend fun findById(id: ActorId): Actor2? { - TODO("Not yet implemented") + TODO() } } + +object Actors2 : Table("actors") { + val id = long("id") + val name = varchar("name", ActorName.length) + val domain = varchar("domain", Domain.length) + val screenName = varchar("screen_name", ActorScreenName.length) + val description = varchar("description", ActorDescription.length) + val inbox = varchar("inbox", 1000).uniqueIndex() + val outbox = varchar("outbox", 1000).uniqueIndex() + val url = varchar("url", 1000).uniqueIndex() + val publicKey = varchar("public_key", 10000) + val privateKey = varchar("private_key", 100000).nullable() + val createdAt = timestamp("created_at") + val keyId = varchar("key_id", 1000) + val following = varchar("following", 1000).nullable() + val followers = varchar("followers", 1000).nullable() + val instance = long("instance").references(Instance.id) + val locked = bool("locked") + val followingCount = integer("following_count").nullable() + val followersCount = integer("followers_count").nullable() + val postsCount = integer("posts_count") + val lastPostAt = timestamp("last_post_at").nullable() + val lastUpdateAt = timestamp("last_update_at") + val suspend = bool("suspend") + val moveTo = long("move_to").references(id).nullable() + val emojis = varchar("emojis", 3000) + + override val primaryKey = PrimaryKey(id) + + init { + uniqueIndex(name, domain) + } +} + +object Actors2AlsoKnownAs : Table("actor_alsoknwonas") { + val actorId = + long("actor_id").references(Actors2.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) + val alsoKnownAs = long("alsoKnownAs").references(Actors2.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + + override val primaryKey: PrimaryKey = PrimaryKey(actorId, alsoKnownAs) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt new file mode 100644 index 00000000..a4c74a73 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework.domainevent + +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher +import org.springframework.context.ApplicationEventPublisher +import org.springframework.stereotype.Component + +@Component +class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: ApplicationEventPublisher) : + DomainEventPublisher { + override suspend fun publishEvent(domainEvent: DomainEvent) { + applicationEventPublisher.publishEvent(domainEvent) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt index 9384eff1..a4f38e1c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt @@ -30,7 +30,7 @@ class MigrationLocalActorApplicationService( private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, ) { suspend fun migration(from: Long, to: Long, executor: ActorId) { - transaction.transaction { + transaction.transaction { val fromActorId = ActorId(from) val toActorId = ActorId(to) diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 09f4f0ca..0e81fca9 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -45,15 +45,19 @@ create table if not exists actors key_id varchar(1000) not null, "following" varchar(1000) null, followers varchar(1000) null, - "instance" bigint not null, + "instance" bigint not null, locked boolean not null, following_count int not null, followers_count int not null, posts_count int not null, last_post_at timestamp null default null, - emojis varchar(300) not null default '', + last_update_at timestamp not null, + suspend boolean not null, + move_to bigint null default null, + emojis varchar(3000) not null default '', unique ("name", "domain"), - constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict + constraint fk_actors_instance__id foreign key ("instance") references instance (id) on delete restrict on update restrict, + constraint fk_actors_actors__move_to foreign key ("move_to") references actors (id) on delete restrict on update restrict ); create table if not exists user_details diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actors2Test.kt similarity index 96% rename from hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt rename to hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actors2Test.kt index 00272d61..e9d5260f 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Test.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actors2Test.kt @@ -3,7 +3,7 @@ package dev.usbharu.hideout.core.domain.model.actor import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -class Actor2Test { +class Actors2Test { @Test fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œã¦ã¯ã„ã‘ãªã„() { val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) From 869ef1e111d58d9b897c25645061ff698a16e21c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:23:44 +0900 Subject: [PATCH 10/54] wip --- hideout-activitypub/build.gradle.kts | 21 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + hideout-activitypub/gradlew | 234 +++++++++++ hideout-activitypub/gradlew.bat | 89 ++++ hideout-activitypub/settings.gradle.kts | 5 + hideout-activitypub/src/main/kotlin/Main.kt | 5 + .../src/e2eTest/kotlin/AssertionUtil.kt | 1 - .../kotlin/mastodon/account/AccountApiTest.kt | 2 - ...WithHttpSignatureSecurityContextFactory.kt | 1 - .../hideout/activitypub/domain/Constant.kt | 41 -- .../exception/ActivityPubProcessException.kt | 37 -- .../exception/FailedProcessException.kt | 37 -- ...FailedToGetActivityPubResourceException.kt | 32 -- .../HttpSignatureUnauthorizedException.kt | 37 -- .../IllegalActivityPubObjectException.kt | 31 -- .../domain/exception/JsonParseException.kt | 31 -- .../activitypub/domain/model/Accept.kt | 62 --- .../activitypub/domain/model/Announce.kt | 76 ---- .../activitypub/domain/model/Create.kt | 79 ---- .../activitypub/domain/model/Delete.kt | 78 ---- .../activitypub/domain/model/Document.kt | 57 --- .../hideout/activitypub/domain/model/Emoji.kt | 66 --- .../activitypub/domain/model/Follow.kt | 61 --- .../activitypub/domain/model/HasActor.kt | 21 - .../hideout/activitypub/domain/model/HasId.kt | 21 - .../hideout/activitypub/domain/model/Image.kt | 55 --- .../activitypub/domain/model/JsonLd.kt | 101 ----- .../hideout/activitypub/domain/model/Key.kt | 53 --- .../hideout/activitypub/domain/model/Like.kt | 73 ---- .../hideout/activitypub/domain/model/Note.kt | 110 ----- .../activitypub/domain/model/Person.kt | 102 ----- .../activitypub/domain/model/Reject.kt | 59 --- .../domain/model/StringOrObject.kt | 76 ---- .../activitypub/domain/model/Tombstone.kt | 41 -- .../hideout/activitypub/domain/model/Undo.kt | 66 --- .../domain/model/nodeinfo/Nodeinfo.kt | 26 -- .../domain/model/nodeinfo/Nodeinfo2_0.kt | 67 ---- .../domain/model/objects/Object.kt | 77 ---- .../model/objects/ObjectDeserializer.kt | 110 ----- .../domain/model/objects/ObjectValue.kt | 43 -- .../domain/model/webfinger/WebFinger.kt | 21 - .../ExposedAnnounceQueryService.kt | 82 ---- .../exposedquery/NoteQueryServiceImpl.kt | 129 ------ .../interfaces/api/actor/UserAPController.kt | 29 -- .../api/actor/UserAPControllerImpl.kt | 38 -- .../api/hostmeta/HostMetaController.kt | 52 --- .../interfaces/api/inbox/InboxController.kt | 38 -- .../api/inbox/InboxControllerImpl.kt | 133 ------ .../api/nodeinfo/NodeinfoController.kt | 82 ---- .../interfaces/api/note/NoteApController.kt | 29 -- .../api/note/NoteApControllerImpl.kt | 49 --- .../interfaces/api/outbox/OutboxController.kt | 28 -- .../api/outbox/OutboxControllerImpl.kt | 27 -- .../api/webfinger/WebFingerController.kt | 68 ---- .../activitypub/query/AnnounceQueryService.kt | 27 -- .../activitypub/query/NoteQueryService.kt | 25 -- .../activity/accept/ApAcceptProcessor.kt | 62 --- .../activity/accept/ApSendAcceptService.kt | 49 --- .../activity/announce/ApAnnounceProcessor.kt | 37 -- .../activity/create/ApSendCreateService.kt | 23 -- .../create/ApSendCreateServiceImpl.kt | 66 --- .../create/CreateActivityProcessor.kt | 38 -- .../activity/delete/APDeleteProcessor.kt | 66 --- .../activity/delete/APSendDeleteService.kt | 86 ---- .../activity/follow/APFollowProcessor.kt | 49 --- .../activity/follow/APReceiveFollowService.kt | 42 -- .../activity/follow/APSendFollowService.kt | 43 -- .../service/activity/like/APLikeProcessor.kt | 77 ---- .../activity/like/APReactionService.kt | 93 ----- .../activity/reject/ApRejectProcessor.kt | 68 ---- .../activity/reject/ApSendRejectService.kt | 23 -- .../reject/ApSendRejectServiceImpl.kt | 45 --- .../activity/undo/APSendUndoService.kt | 23 -- .../activity/undo/APSendUndoServiceImpl.kt | 50 --- .../service/activity/undo/APUndoProcessor.kt | 145 ------- .../service/common/APRequestService.kt | 41 -- .../service/common/APRequestServiceImpl.kt | 251 ------------ .../common/APResourceResolveService.kt | 31 -- .../common/APResourceResolveServiceImpl.kt | 104 ----- .../activitypub/service/common/APService.kt | 103 ----- .../common/AbstractActivityPubProcessor.kt | 56 --- .../common/ActivityPubProcessContext.kt | 30 -- .../service/common/ActivityPubProcessor.kt | 27 -- .../service/common/ActivityType.kt | 49 --- .../service/common/ActivityVocabulary.kt | 74 ---- .../common/ExtendedActivityVocabulary.kt | 75 ---- .../service/common/ExtendedVocabulary.kt | 21 - .../service/objects/emoji/EmojiService.kt | 26 -- .../service/objects/emoji/EmojiServiceImpl.kt | 95 ----- .../service/objects/note/APNoteService.kt | 275 ------------- .../service/objects/note/NoteApApiService.kt | 23 -- .../objects/note/NoteApApiServiceImpl.kt | 72 ---- .../service/objects/user/APUserService.kt | 166 -------- .../service/webfinger/WebFingerApiService.kt | 44 -- .../application/config/SecurityConfig.kt | 1 - .../event/relationship/RelationshipEvent.kt | 40 ++ .../hideout/core/domain/model/actor/Acct.kt | 19 - .../hideout/core/domain/model/actor/Actor.kt | 269 ------------- .../domain/model/actor/ActorRepository.kt | 49 --- .../core/domain/model/emoji/EmojiId.kt | 2 +- .../ExposedNotificationRepository.kt | 102 ----- .../hideout/core/domain/model/post/Post.kt | 220 ---------- .../core/domain/model/post/Post2Repository.kt | 2 +- .../core/domain/model/post/PostContent.kt | 2 + .../core/domain/model/post/PostOverview.kt | 6 +- .../core/domain/model/post/PostRepository.kt | 37 -- .../model/relationship/Relationship2.kt | 106 +++++ .../relationship/Relationship2Repository.kt} | 9 +- .../relationship/RelationshipRepository.kt | 72 ---- .../RelationshipRepositoryImpl.kt | 1 - .../shared/domainevent/DomainEventStorable.kt | 29 -- .../infrastructure/exposed/PostQueryMapper.kt | 41 -- .../exposed/PostResultRowMapper.kt | 44 -- .../infrastructure/exposed/UserQueryMapper.kt | 28 -- .../exposed/UserResultRowMapper.kt | 53 --- .../exposedquery/FollowerQueryServiceImpl.kt | 38 -- .../exposedrepository/ActorRepositoryImpl.kt | 182 --------- .../ExposedPost2Repository.kt | 175 ++++++++ .../exposedrepository/PostRepositoryImpl.kt | 232 ----------- .../HttpSignatureUserDetailsService.kt | 1 - .../oauth2/UserDetailsServiceImpl.kt | 1 - .../interfaces/api/auth/AuthController.kt | 1 - .../core/query/FollowerQueryService.kt | 25 -- .../core/service/auth/AuthApiService.kt | 23 -- .../core/service/auth/AuthApiServiceImpl.kt | 67 ---- .../core/service/filter/MuteProcessService.kt | 30 -- .../service/filter/MuteProcessServiceImpl.kt | 139 ------- .../core/service/follow/SendFollowDto.kt | 21 - .../notification/NotificationServiceImpl.kt | 3 - .../service/notification/NotificationStore.kt | 34 -- .../hideout/core/service/post/PostService.kt | 30 -- .../core/service/post/PostServiceImpl.kt | 138 ------- .../service/reaction/ReactionServiceImpl.kt | 1 - .../relationship/RelationshipService.kt | 38 -- .../relationship/RelationshipServiceImpl.kt | 341 ---------------- .../core/service/timeline/TimelineService.kt | 87 ---- .../core/service/user/UserAuthServiceImpl.kt | 1 - .../hideout/core/service/user/UserService.kt | 40 -- .../core/service/user/UserServiceImpl.kt | 227 ----------- .../config/MastodonApiSecurityConfig.kt | 173 -------- .../exception/AccountNotFoundException.kt | 54 --- .../domain/exception/ClientException.kt | 38 -- .../domain/exception/MastodonApiException.kt | 56 --- .../domain/exception/ServerException.kt | 21 - .../exception/StatusNotFoundException.kt | 56 --- .../domain/model/MastodonApiErrorResponse.kt | 19 - .../domain/model/MastodonNotification.kt | 34 -- .../model/MastodonNotificationRepository.kt | 37 -- .../mastodon/domain/model/NotificationType.kt | 49 --- .../exposedquery/AccountQueryServiceImpl.kt | 74 ---- .../exposedquery/StatusQueryServiceImpl.kt | 250 ------------ .../ExposedMastodonNotificationRepository.kt | 131 ------ .../MongoMastodonNotificationRepository.kt | 27 -- ...goMastodonNotificationRepositoryWrapper.kt | 83 ---- .../springweb/MastodonApiControllerAdvice.kt | 111 ----- .../account/MastodonAccountApiController.kt | 250 ------------ .../api/apps/MastodonAppsApiController.kt | 53 --- .../api/filter/MastodonFilterApiController.kt | 174 -------- .../instance/MastodonInstanceApiController.kt | 30 -- .../api/media/MastodonMediaApiController.kt | 45 --- .../interfaces/api/media/MediaRequest.kt | 26 -- .../MastodonNotificationApiController.kt | 86 ---- .../status/MastodonStatusesApiContoller.kt | 65 --- .../interfaces/api/status/StatusQuery.kt | 25 -- .../interfaces/api/status/StatusesRequest.kt | 124 ------ .../timeline/MastodonTimelineApiController.kt | 95 ----- .../mastodon/query/AccountQueryService.kt | 24 -- .../mastodon/query/StatusQueryService.kt | 41 -- .../service/account/AccountApiService.kt | 379 ------------------ .../service/account/AccountService.kt | 37 -- .../mastodon/service/app/AppApiService.kt | 85 ---- .../filter/MastodonFilterApiService.kt | 301 -------------- .../service/instance/InstanceApiService.kt | 99 ----- .../mastodon/service/media/MediaApiService.kt | 26 -- .../service/media/MediaApiServiceImpl.kt | 50 --- .../notification/MastodonNotificationStore.kt | 82 ---- .../notification/NotificationApiService.kt | 39 -- .../NotificationApiServiceImpl.kt | 126 ------ .../service/status/StatusesApiService.kt | 212 ---------- .../service/timeline/TimelineApiService.kt | 61 --- .../dev/usbharu/hideout/util/AcctUtil.kt | 58 --- .../activity/accept/ApAcceptProcessorTest.kt | 2 - .../follow/APSendFollowServiceImplTest.kt | 1 - .../APResourceResolveServiceImplTest.kt | 1 - .../objects/note/APNoteServiceImplTest.kt | 3 - .../NotificationServiceImplTest.kt | 3 - .../core/service/post/PostServiceImplTest.kt | 4 - .../reaction/ReactionServiceImplTest.kt | 1 - .../RelationshipServiceImplTest.kt | 3 - .../service/timeline/TimelineServiceTest.kt | 3 - .../core/service/user/ActorServiceTest.kt | 5 - .../account/AccountApiServiceImplTest.kt | 5 - .../src/test/kotlin/utils/PostBuilder.kt | 1 - .../src/test/kotlin/utils/UserBuilder.kt | 1 - hideout-mastodon/build.gradle.kts | 21 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + hideout-mastodon/gradlew | 234 +++++++++++ hideout-mastodon/gradlew.bat | 89 ++++ hideout-mastodon/settings.gradle.kts | 5 + hideout-mastodon/src/main/kotlin/Main.kt | 5 + .../hideout/worker/DeliverAcceptTaskRunner.kt | 1 - .../hideout/worker/DeliverCreateTaskRunner.kt | 1 - .../hideout/worker/DeliverDeleteTaskRunner.kt | 1 - .../worker/DeliverReactionTaskRunner.kt | 1 - .../hideout/worker/DeliverRejectTaskRunner.kt | 1 - .../hideout/worker/DeliverUndoTaskRunner.kt | 1 - .../hideout/worker/ReceiveFollowTaskRunner.kt | 2 - .../hideout/worker/UpdateActorWorker.kt | 1 - 210 files changed, 1055 insertions(+), 11855 deletions(-) create mode 100644 hideout-activitypub/build.gradle.kts create mode 100644 hideout-activitypub/gradle/wrapper/gradle-wrapper.jar create mode 100644 hideout-activitypub/gradle/wrapper/gradle-wrapper.properties create mode 100644 hideout-activitypub/gradlew create mode 100644 hideout-activitypub/gradlew.bat create mode 100644 hideout-activitypub/settings.gradle.kts create mode 100644 hideout-activitypub/src/main/kotlin/Main.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{activitypub/domain/model/HasName.kt => core/domain/model/relationship/Relationship2Repository.kt} (73%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt create mode 100644 hideout-mastodon/build.gradle.kts create mode 100644 hideout-mastodon/gradle/wrapper/gradle-wrapper.jar create mode 100644 hideout-mastodon/gradle/wrapper/gradle-wrapper.properties create mode 100644 hideout-mastodon/gradlew create mode 100644 hideout-mastodon/gradlew.bat create mode 100644 hideout-mastodon/settings.gradle.kts create mode 100644 hideout-mastodon/src/main/kotlin/Main.kt diff --git a/hideout-activitypub/build.gradle.kts b/hideout-activitypub/build.gradle.kts new file mode 100644 index 00000000..45be2756 --- /dev/null +++ b/hideout-activitypub/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") version "1.9.23" +} + +group = "dev.usbharu" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/hideout-activitypub/gradle/wrapper/gradle-wrapper.jar b/hideout-activitypub/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/hideout-activitypub/gradlew.bat b/hideout-activitypub/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/hideout-activitypub/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/hideout-activitypub/settings.gradle.kts b/hideout-activitypub/settings.gradle.kts new file mode 100644 index 00000000..1fd1691d --- /dev/null +++ b/hideout-activitypub/settings.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" +} +rootProject.name = "hideout-activitypub" + diff --git a/hideout-activitypub/src/main/kotlin/Main.kt b/hideout-activitypub/src/main/kotlin/Main.kt new file mode 100644 index 00000000..27f6ee1a --- /dev/null +++ b/hideout-activitypub/src/main/kotlin/Main.kt @@ -0,0 +1,5 @@ +package dev.usbharu + +fun main() { + println("Hello World!") +} \ No newline at end of file diff --git a/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt b/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt index 8d73d7c6..a93ba706 100644 --- a/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt +++ b/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt @@ -14,7 +14,6 @@ * limitations under the License. */ -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.selectAll import java.net.MalformedURLException diff --git a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt index 6b482f85..d48c72d8 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt @@ -17,8 +17,6 @@ package mastodon.account import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.infrastructure.exposedquery.FollowerQueryServiceImpl import dev.usbharu.owl.producer.api.OwlProducer import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest diff --git a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt index 6d809267..f20da72f 100644 --- a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt +++ b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt @@ -18,7 +18,6 @@ package util import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt deleted file mode 100644 index 6c19c683..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/Constant.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain - -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject - -object Constant { - val context = listOf( - StringOrObject("https://www.w3.org/ns/activitystreams"), - StringOrObject("https://w3id.org/security/v1"), - StringOrObject( - mapOf( - "manuallyApprovesFollowers" to "as:manuallyApprovesFollowers", - "sensitive" to "as:sensitive", - "Hashtag" to "as:Hashtag", - "quoteUrl" to "as:quoteUrl", - "toot" to "http://joinmastodon.org/ns#", - "Emoji" to "toot:Emoji", - "featured" to "toot:featured", - "discoverable" to "toot:discoverable", - "schema" to "http://schema.org#", - "PropertyValue" to "schema:PropertyValue", - "value" to "schema:value", - ) - ) - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt deleted file mode 100644 index 8ce12a19..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/ActivityPubProcessException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class ActivityPubProcessException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 5370068873167636639L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt deleted file mode 100644 index d3125395..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedProcessException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class FailedProcessException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -1305337651143409144L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt deleted file mode 100644 index 528277a8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/FailedToGetActivityPubResourceException.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import java.io.Serial - -class FailedToGetActivityPubResourceException : FailedToGetResourcesException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 6420233106776818052L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt deleted file mode 100644 index aa50d3db..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/HttpSignatureUnauthorizedException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class HttpSignatureUnauthorizedException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -6449793151674654501L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt deleted file mode 100644 index ae84181d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/IllegalActivityPubObjectException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class IllegalActivityPubObjectException : IllegalArgumentException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 7216998115771415263L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt deleted file mode 100644 index 841641cc..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/exception/JsonParseException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.exception - -import java.io.Serial - -class JsonParseException : IllegalArgumentException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 7975567796830950692L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt deleted file mode 100644 index 84c090cc..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Accept.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Accept @JsonCreator constructor( - type: List = emptyList(), - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") - val apObject: Object, - override val actor: String -) : Object( - type = add(type, "Accept") -), - HasActor { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Accept - - if (apObject != other.apObject) return false - if (actor != other.actor) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - return result - } - - override fun toString(): String { - return "Accept(" + - "apObject=$apObject, " + - "actor='$actor'" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt deleted file mode 100644 index c07eab7c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Announce.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonProperty -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Announce @JsonCreator constructor( - type: List = emptyList(), - @JsonProperty("object") - val apObject: String, - override val actor: String, - override val id: String, - val published: String, - val to: List = emptyList(), - val cc: List = emptyList() -) : Object( - type = add(type, "Announce") -), - HasActor, - HasId { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Announce - - if (apObject != other.apObject) return false - if (actor != other.actor) return false - if (id != other.id) return false - if (published != other.published) return false - if (to != other.to) return false - if (cc != other.cc) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + published.hashCode() - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() - return result - } - - override fun toString(): String { - return "Announce(" + - "apObject='$apObject', " + - "actor='$actor', " + - "id='$id', " + - "published='$published', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt deleted file mode 100644 index 53e12cd5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Create.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Create( - type: List = emptyList(), - val name: String? = null, - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") - val apObject: Object, - override val actor: String, - override val id: String, - val to: List = emptyList(), - val cc: List = emptyList() -) : Object( - type = add(type, "Create") -), - HasId, - HasActor { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Create - - if (name != other.name) return false - if (apObject != other.apObject) return false - if (actor != other.actor) return false - if (id != other.id) return false - if (to != other.to) return false - if (cc != other.cc) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + (name?.hashCode() ?: 0) - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() - return result - } - - override fun toString(): String { - return "Create(" + - "name=$name, " + - "apObject=$apObject, " + - "actor='$actor', " + - "id='$id', " + - "to=$to, " + - "cc=$cc" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt deleted file mode 100644 index 43ec1a51..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Delete : Object, HasId, HasActor { - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") - val apObject: Object - val published: String - override var actor: String = "" - override var id: String = "" - - constructor( - type: List = emptyList(), - actor: String, - id: String, - `object`: Object, - published: String - ) : super(add(type, "Delete")) { - this.apObject = `object` - this.published = published - this.actor = actor - this.id = id - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Delete - - if (apObject != other.apObject) return false - if (published != other.published) return false - if (actor != other.actor) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + published.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String { - return "Delete(" + - "apObject=$apObject, " + - "published='$published', " + - "actor='$actor', " + - "id='$id'" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt deleted file mode 100644 index 632fb7cb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Document.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonSetter -import com.fasterxml.jackson.annotation.Nulls -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Document( - type: List = emptyList(), - @JsonSetter(nulls = Nulls.AS_EMPTY) - override val name: String = "", - val mediaType: String, - val url: String -) : Object( - type = add(type, "Document") -), - HasName { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Document - - if (mediaType != other.mediaType) return false - if (url != other.url) return false - if (name != other.name) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + mediaType.hashCode() - result = 31 * result + url.hashCode() - result = 31 * result + name.hashCode() - return result - } - - override fun toString(): String = "Document(mediaType=$mediaType, url=$url, name='$name') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt deleted file mode 100644 index f1f7ae7b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Emoji( - type: List, - override val name: String, - override val id: String, - val updated: String, - val icon: Image -) : Object( - type = add(type, "Emoji") -), - HasName, - HasId { - - override fun toString(): String { - return "Emoji(" + - "name='$name', " + - "id='$id', " + - "updated='$updated', " + - "icon=$icon" + - ")" + - " ${super.toString()}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Emoji - - if (name != other.name) return false - if (id != other.id) return false - if (updated != other.updated) return false - if (icon != other.icon) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + updated.hashCode() - result = 31 * result + icon.hashCode() - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt deleted file mode 100644 index f404946a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Follow( - type: List = emptyList(), - @JsonProperty("object") val apObject: String, - override val actor: String, - val id: String? = null -) : Object( - type = add(type, "Follow") -), - HasActor { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Follow - - if (apObject != other.apObject) return false - if (actor != other.actor) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + (id?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Follow(" + - "apObject='$apObject', " + - "actor='$actor', " + - "id=$id" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt deleted file mode 100644 index d04a3a7c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasActor.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -interface HasActor { - val actor: String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt deleted file mode 100644 index b4043b5c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasId.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -interface HasId { - val id: String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt deleted file mode 100644 index a522574c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Image.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Image( - type: List = emptyList(), - val mediaType: String? = null, - val url: String -) : Object( - add(type, "Image") -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Image - - if (mediaType != other.mediaType) return false - if (url != other.url) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + (mediaType?.hashCode() ?: 0) - result = 31 * result + url.hashCode() - return result - } - - override fun toString(): String { - return "Image(" + - "mediaType=$mediaType, " + - "url='$url'" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt deleted file mode 100644 index 88a8e48d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLd.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonAutoDetect -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import com.fasterxml.jackson.databind.annotation.JsonSerialize - -@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) -open class JsonLd { - @JsonProperty("@context") - @JsonDeserialize(contentUsing = StringOrObjectDeserializer::class) - @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY, using = ContextSerializer::class) - @JsonInclude(JsonInclude.Include.NON_EMPTY) - var context: List = emptyList() - set(value) { - field = value.filterNot { it.isEmpty() } - } - - @JsonCreator - constructor(context: List?) { - if (context != null) { - this.context = context.filterNotNull().filterNot { it.isEmpty() } - } else { - this.context = emptyList() - } - } - - protected constructor() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is JsonLd) return false - - return context == other.context - } - - override fun hashCode(): Int = context.hashCode() - - override fun toString(): String = "JsonLd(context=$context)" -} - -class ContextDeserializer : JsonDeserializer() { - - override fun deserialize( - p0: com.fasterxml.jackson.core.JsonParser?, - p1: com.fasterxml.jackson.databind.DeserializationContext? - ): String { - val readTree: JsonNode = p0?.codec?.readTree(p0) ?: return "" - if (readTree.isValueNode) { - return readTree.textValue() - } - return "" - } -} - -class ContextSerializer : JsonSerializer>() { - - @Deprecated("Deprecated in Java") - override fun isEmpty(value: List?): Boolean = value.isNullOrEmpty() - - override fun isEmpty(provider: SerializerProvider?, value: List?): Boolean = value.isNullOrEmpty() - - override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider) { - if (value.isNullOrEmpty()) { - serializers.defaultSerializeNull(gen) - return - } - if (value.size == 1) { - serializers.findValueSerializer(StringOrObject::class.java).serialize(value[0], gen, serializers) - } else { - gen?.writeStartArray() - value.forEach { - serializers.findValueSerializer(StringOrObject::class.java).serialize(it, gen, serializers) - } - gen?.writeEndArray() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt deleted file mode 100644 index bb046d56..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Key.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Key( - override val id: String, - val owner: String, - val publicKeyPem: String -) : Object( - type = add(list = emptyList(), type = "Key") -), - HasId { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Key - - if (owner != other.owner) return false - if (publicKeyPem != other.publicKeyPem) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + owner.hashCode() - result = 31 * result + publicKeyPem.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String = "Key(owner=$owner, publicKeyPem=$publicKeyPem, id='$id') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt deleted file mode 100644 index 6b46cb54..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Like.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Like( - type: List = emptyList(), - override val actor: String, - override val id: String, - @JsonProperty("object") val apObject: String, - val content: String, - @JsonDeserialize(contentUsing = ObjectDeserializer::class) val tag: List = emptyList() -) : Object( - type = add(type, "Like") -), - HasId, - HasActor { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Like - - if (actor != other.actor) return false - if (id != other.id) return false - if (apObject != other.apObject) return false - if (content != other.content) return false - if (tag != other.tag) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + content.hashCode() - result = 31 * result + tag.hashCode() - return result - } - - override fun toString(): String { - return "Like(" + - "actor='$actor', " + - "id='$id', " + - "apObject='$apObject', " + - "content='$content', " + - "tag=$tag" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt deleted file mode 100644 index b925c861..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Note -@Suppress("LongParameterList", "CyclomaticComplexMethod") -constructor( - type: List = emptyList(), - override val id: String, - val attributedTo: String, - val content: String, - val published: String, - val to: List = emptyList(), - val cc: List = emptyList(), - val sensitive: Boolean = false, - val inReplyTo: String? = null, - val attachment: List = emptyList(), - @JsonDeserialize(contentUsing = ObjectDeserializer::class) - val tag: List = emptyList(), - val quoteUri: String? = null, - val quoteUrl: String? = null, - @JsonProperty("_misskey_quote") - val misskeyQuote: String? = null -) : Object( - type = add(type, "Note") -), - HasId { - - @Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod") - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Note - - if (id != other.id) return false - if (attributedTo != other.attributedTo) return false - if (content != other.content) return false - if (published != other.published) return false - if (to != other.to) return false - if (cc != other.cc) return false - if (sensitive != other.sensitive) return false - if (inReplyTo != other.inReplyTo) return false - if (attachment != other.attachment) return false - if (tag != other.tag) return false - if (quoteUri != other.quoteUri) return false - if (quoteUrl != other.quoteUrl) return false - if (misskeyQuote != other.misskeyQuote) return false - - return true - } - - @Suppress("CyclomaticComplexMethod") - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + attributedTo.hashCode() - result = 31 * result + content.hashCode() - result = 31 * result + published.hashCode() - result = 31 * result + to.hashCode() - result = 31 * result + cc.hashCode() - result = 31 * result + sensitive.hashCode() - result = 31 * result + (inReplyTo?.hashCode() ?: 0) - result = 31 * result + attachment.hashCode() - result = 31 * result + tag.hashCode() - result = 31 * result + (quoteUri?.hashCode() ?: 0) - result = 31 * result + (quoteUrl?.hashCode() ?: 0) - result = 31 * result + (misskeyQuote?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Note(" + - "id='$id', " + - "attributedTo='$attributedTo', " + - "content='$content', " + - "published='$published', " + - "to=$to, " + - "cc=$cc, " + - "sensitive=$sensitive, " + - "inReplyTo=$inReplyTo, " + - "attachment=$attachment, " + - "tag=$tag, " + - "quoteUri=$quoteUri, " + - "quoteUrl=$quoteUrl, " + - "misskeyQuote=$misskeyQuote" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt deleted file mode 100644 index 3f4a7dbe..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Person.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Person -@Suppress("LongParameterList") -constructor( - type: List = emptyList(), - val name: String?, - override val id: String, - var preferredUsername: String, - var summary: String?, - var inbox: String, - var outbox: String, - var url: String, - private var icon: Image?, - var publicKey: Key, - var endpoints: Map = emptyMap(), - var followers: String?, - var following: String?, - val manuallyApprovesFollowers: Boolean? = false -) : Object(add(type, "Person")), HasId { - - @Suppress("CyclomaticComplexMethod", "CognitiveComplexMethod") - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Person - - if (name != other.name) return false - if (id != other.id) return false - if (preferredUsername != other.preferredUsername) return false - if (summary != other.summary) return false - if (inbox != other.inbox) return false - if (outbox != other.outbox) return false - if (url != other.url) return false - if (icon != other.icon) return false - if (publicKey != other.publicKey) return false - if (endpoints != other.endpoints) return false - if (followers != other.followers) return false - if (following != other.following) return false - if (manuallyApprovesFollowers != other.manuallyApprovesFollowers) return false - - return true - } - - @Suppress("CyclomaticComplexMethod") - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + (name?.hashCode() ?: 0) - result = 31 * result + id.hashCode() - result = 31 * result + preferredUsername.hashCode() - result = 31 * result + (summary?.hashCode() ?: 0) - result = 31 * result + inbox.hashCode() - result = 31 * result + outbox.hashCode() - result = 31 * result + url.hashCode() - result = 31 * result + (icon?.hashCode() ?: 0) - result = 31 * result + publicKey.hashCode() - result = 31 * result + endpoints.hashCode() - result = 31 * result + (followers?.hashCode() ?: 0) - result = 31 * result + (following?.hashCode() ?: 0) - result = 31 * result + (manuallyApprovesFollowers?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Person(" + - "name=$name, " + - "id='$id', " + - "preferredUsername='$preferredUsername', " + - "summary=$summary, " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "icon=$icon, " + - "publicKey=$publicKey, " + - "endpoints=$endpoints, " + - "followers=$followers, " + - "following=$following, " + - "manuallyApprovesFollowers=$manuallyApprovesFollowers" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt deleted file mode 100644 index 5216c2a1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Reject.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Reject( - override val actor: String, - override val id: String, - @JsonDeserialize(using = ObjectDeserializer::class) @JsonProperty("object") val apObject: Object -) : Object(listOf("Reject")), HasId, HasActor { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Reject - - if (actor != other.actor) return false - if (id != other.id) return false - if (apObject != other.apObject) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + apObject.hashCode() - return result - } - - override fun toString(): String { - return "Reject(" + - "actor='$actor', " + - "id='$id', " + - "apObject=$apObject" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt deleted file mode 100644 index 3a9969db..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/StringOrObject.kt +++ /dev/null @@ -1,76 +0,0 @@ -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.databind.* - -open class StringOrObject { - var contextString: String? = null - var contextObject: Map? = null - - @JsonCreator - protected constructor() - - constructor(string: String) : this() { - contextString = string - } - - constructor(contextObject: Map) : this() { - this.contextObject = contextObject - } - - fun isEmpty(): Boolean = contextString.isNullOrEmpty() and contextObject.isNullOrEmpty() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as StringOrObject - - if (contextString != other.contextString) return false - if (contextObject != other.contextObject) return false - - return true - } - - override fun hashCode(): Int { - var result = contextString?.hashCode() ?: 0 - result = 31 * result + (contextObject?.hashCode() ?: 0) - return result - } - - override fun toString(): String = "StringOrObject(contextString=$contextString, contextObject=$contextObject)" -} - -class StringOrObjectDeserializer : JsonDeserializer() { - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext): StringOrObject { - val readTree: JsonNode = p?.codec?.readTree(p) ?: return StringOrObject("") - return if (readTree.isValueNode) { - StringOrObject(readTree.textValue()) - } else if (readTree.isObject) { - val map: Map = ctxt.readTreeAsValue( - readTree, - ctxt.typeFactory.constructType(object : TypeReference>() {}) - ) - StringOrObject(map) - } else { - StringOrObject("") - } - } -} - -class StringORObjectSerializer : JsonSerializer() { - override fun serialize(value: StringOrObject?, gen: JsonGenerator?, serializers: SerializerProvider) { - if (value == null) { - serializers.defaultSerializeNull(gen) - return - } - if (value.contextString != null) { - gen?.writeString(value.contextString) - } else { - serializers.defaultSerializeValue(value.contextObject, gen) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt deleted file mode 100644 index 56ea7cde..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Tombstone.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -open class Tombstone(type: List = emptyList(), override val id: String) : - Object(add(type, "Tombstone")), - HasId { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Tombstone - - return id == other.id - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String = "Tombstone(id='$id') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt deleted file mode 100644 index 038d4054..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer - -open class Undo( - type: List = emptyList(), - override val actor: String, - override val id: String, - @JsonDeserialize(using = ObjectDeserializer::class) - @JsonProperty("object") val apObject: Object, - val published: String? -) : Object(add(type, "Undo")), HasId, HasActor { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Undo - - if (actor != other.actor) return false - if (id != other.id) return false - if (apObject != other.apObject) return false - if (published != other.published) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + actor.hashCode() - result = 31 * result + id.hashCode() - result = 31 * result + apObject.hashCode() - result = 31 * result + (published?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "Undo(" + - "actor='$actor', " + - "id='$id', " + - "apObject=$apObject, " + - "published=$published" + - ")" + - " ${super.toString()}" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt deleted file mode 100644 index 2e3cbf1f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.nodeinfo - -data class Nodeinfo( - val links: List -) { - data class Links( - val rel: String, - val href: String - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt deleted file mode 100644 index f65749ef..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/nodeinfo/Nodeinfo2_0.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("ClassName") - -package dev.usbharu.hideout.activitypub.domain.model.nodeinfo - -@Suppress("ClassNaming") -data class Nodeinfo2_0( - val version: String, - val software: Software, - val protocols: List, - val services: Services, - val openRegistrations: Boolean, - val usage: Usage, - val metadata: Metadata -) { - data class Software( - val name: String, - val version: String - ) - - data class Services( - val inbound: List, - val outbound: List - ) - - data class Usage( - val users: Users, - val localPosts: Int, - val localComments: Int - ) { - data class Users( - val total: Int, - val activeHalfYear: Int, - val activeMonth: Int - ) - } - - data class Metadata( - val nodeName: String, - val nodeDescription: String, - val maintainer: Maintainer, - val langs: List, - val tosUrl: String, - val repositoryUrl: String, - val feedbackUrl: String, - ) { - data class Maintainer( - val name: String, - val email: String - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt deleted file mode 100644 index c40b37fb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/Object.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.JsonSerializer -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.annotation.JsonSerialize -import dev.usbharu.hideout.activitypub.domain.model.JsonLd - -open class Object : JsonLd { - @JsonSerialize(using = TypeSerializer::class) - var type: List = emptyList() - set(value) { - field = value.filter { it.isNotBlank() } - } - - protected constructor() - constructor(type: List) : super() { - this.type = type.filter { it.isNotBlank() } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as Object - - return type == other.type - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + type.hashCode() - return result - } - - override fun toString(): String = "Object(type=$type) ${super.toString()}" - - companion object { - @JvmStatic - protected fun add(list: List, type: String): List { - val toMutableList = list.toMutableList() - toMutableList.add(type) - return toMutableList.distinct() - } - } -} - -class TypeSerializer : JsonSerializer>() { - override fun serialize(value: List?, gen: JsonGenerator?, serializers: SerializerProvider?) { - if (value?.size == 1) { - gen?.writeString(value[0]) - } else { - gen?.writeStartArray() - value?.forEach { - gen?.writeString(it) - } - gen?.writeEndArray() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt deleted file mode 100644 index 1a554745..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectDeserializer.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import dev.usbharu.hideout.activitypub.domain.model.* -import dev.usbharu.hideout.activitypub.service.common.ExtendedActivityVocabulary - -class ObjectDeserializer : JsonDeserializer() { - @Suppress("LongMethod", "CyclomaticComplexMethod") - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object? { - requireNotNull(p) - val treeNode: JsonNode = requireNotNull(p.codec?.readTree(p)) - if (treeNode.isValueNode) { - return ObjectValue( - emptyList(), - treeNode.asText() - ) - } else if (treeNode.isObject) { - val type = treeNode["type"] - val activityType = if (type.isArray) { - type.firstNotNullOf { jsonNode: JsonNode -> - ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(jsonNode.asText(), true) } - } - } else if (type.isValueNode) { - ExtendedActivityVocabulary.values().firstOrNull { it.name.equals(type.asText(), true) } - } else { - null - } - - return when (activityType) { - ExtendedActivityVocabulary.Follow -> p.codec.treeToValue(treeNode, Follow::class.java) - ExtendedActivityVocabulary.Note -> p.codec.treeToValue(treeNode, Note::class.java) - ExtendedActivityVocabulary.Object -> p.codec.treeToValue(treeNode, Object::class.java) - ExtendedActivityVocabulary.Link -> null - ExtendedActivityVocabulary.Activity -> null - ExtendedActivityVocabulary.IntransitiveActivity -> null - ExtendedActivityVocabulary.Collection -> null - ExtendedActivityVocabulary.OrderedCollection -> null - ExtendedActivityVocabulary.CollectionPage -> null - ExtendedActivityVocabulary.OrderedCollectionPage -> null - ExtendedActivityVocabulary.Accept -> p.codec.treeToValue(treeNode, Accept::class.java) - ExtendedActivityVocabulary.Add -> null - ExtendedActivityVocabulary.Announce -> p.codec.treeToValue(treeNode, Announce::class.java) - ExtendedActivityVocabulary.Arrive -> null - ExtendedActivityVocabulary.Block -> null - ExtendedActivityVocabulary.Create -> p.codec.treeToValue(treeNode, Create::class.java) - ExtendedActivityVocabulary.Delete -> p.codec.treeToValue(treeNode, Delete::class.java) - ExtendedActivityVocabulary.Dislike -> null - ExtendedActivityVocabulary.Flag -> null - ExtendedActivityVocabulary.Ignore -> null - ExtendedActivityVocabulary.Invite -> null - ExtendedActivityVocabulary.Join -> null - ExtendedActivityVocabulary.Leave -> null - ExtendedActivityVocabulary.Like -> p.codec.treeToValue(treeNode, Like::class.java) - ExtendedActivityVocabulary.Listen -> null - ExtendedActivityVocabulary.Move -> null - ExtendedActivityVocabulary.Offer -> null - ExtendedActivityVocabulary.Question -> null - ExtendedActivityVocabulary.Reject -> p.codec.treeToValue(treeNode, Reject::class.java) - ExtendedActivityVocabulary.Read -> null - ExtendedActivityVocabulary.Remove -> null - ExtendedActivityVocabulary.TentativeReject -> null - ExtendedActivityVocabulary.TentativeAccept -> null - ExtendedActivityVocabulary.Travel -> null - ExtendedActivityVocabulary.Undo -> p.codec.treeToValue(treeNode, Undo::class.java) - ExtendedActivityVocabulary.Update -> null - ExtendedActivityVocabulary.View -> null - ExtendedActivityVocabulary.Application -> null - ExtendedActivityVocabulary.Group -> null - ExtendedActivityVocabulary.Organization -> null - ExtendedActivityVocabulary.Person -> p.codec.treeToValue(treeNode, Person::class.java) - ExtendedActivityVocabulary.Service -> null - ExtendedActivityVocabulary.Article -> null - ExtendedActivityVocabulary.Audio -> null - ExtendedActivityVocabulary.Document -> p.codec.treeToValue(treeNode, Document::class.java) - ExtendedActivityVocabulary.Event -> null - ExtendedActivityVocabulary.Image -> p.codec.treeToValue(treeNode, Image::class.java) - ExtendedActivityVocabulary.Page -> null - ExtendedActivityVocabulary.Place -> null - ExtendedActivityVocabulary.Profile -> null - ExtendedActivityVocabulary.Relationship -> null - ExtendedActivityVocabulary.Tombstone -> p.codec.treeToValue(treeNode, Tombstone::class.java) - ExtendedActivityVocabulary.Video -> null - ExtendedActivityVocabulary.Mention -> null - ExtendedActivityVocabulary.Emoji -> p.codec.treeToValue(treeNode, Emoji::class.java) - null -> null - } - } else { - return null - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt deleted file mode 100644 index 6bb2c9d5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.annotation.JsonCreator - -@Suppress("VariableNaming") -open class ObjectValue @JsonCreator constructor(type: List, var `object`: String) : Object( - type -) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as ObjectValue - - return `object` == other.`object` - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + `object`.hashCode() - return result - } - - override fun toString(): String = "ObjectValue(`object`='$`object`') ${super.toString()}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt deleted file mode 100644 index 3760a991..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/webfinger/WebFinger.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.webfinger - -data class WebFinger(val subject: String, val links: List) { - data class Link(val rel: String, val type: String, val href: String) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt deleted file mode 100644 index 7807131d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/ExposedAnnounceQueryService.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.infrastructure.exposedquery - -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.activitypub.query.AnnounceQueryService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.springframework.stereotype.Repository -import java.time.Instant - -@Repository -class ExposedAnnounceQueryService( - private val postRepository: PostRepository, - private val postResultRowMapper: ResultRowMapper -) : AnnounceQueryService { - override suspend fun findById(id: Long): Pair? { - return Posts - .leftJoin(Actors) - .selectAll().where { Posts.id eq id } - .singleOrNull() - ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } - } - - override suspend fun findByApId(apId: String): Pair? { - return Posts - .leftJoin(Actors) - .selectAll().where { Posts.apId eq apId } - .singleOrNull() - ?.let { (it.toAnnounce() ?: return null) to (postResultRowMapper.map(it)) } - } - - private suspend fun ResultRow.toAnnounce(): Announce? { - val repostId = this[Posts.repostId] ?: return null - val repost = postRepository.findById(repostId)?.url ?: return null - - val (to, cc) = visibility( - Visibility.entries.first { visibility -> visibility.ordinal == this[Posts.visibility] }, - this[Actors.followers] - ) - - return Announce( - type = emptyList(), - id = this[Posts.apId], - apObject = repost, - actor = this[Actors.url], - published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(), - to = to, - cc = cc - ) - } - - private fun visibility(visibility: Visibility, followers: String?): Pair, List> { - return when (visibility) { - Visibility.PUBLIC -> listOf(APNoteServiceImpl.public) to listOf(APNoteServiceImpl.public) - Visibility.UNLISTED -> listOfNotNull(followers) to listOf(APNoteServiceImpl.public) - Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers) - Visibility.DIRECT -> TODO() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt deleted file mode 100644 index 85ee76ae..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/infrastructure/exposedquery/NoteQueryServiceImpl.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.infrastructure.exposedquery - -import dev.usbharu.hideout.activitypub.domain.model.Document -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.exposedrepository.* -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository -import java.time.Instant - -@Repository -class NoteQueryServiceImpl(private val postRepository: PostRepository, private val postQueryMapper: QueryMapper) : - NoteQueryService { - override suspend fun findById(id: Long): Pair? { - return Posts - .leftJoin(Actors) - .leftJoin(PostsMedia) - .leftJoin(Media) - .selectAll().where { Posts.id eq id } - .let { - (it.toNote() ?: return null) to ( - postQueryMapper.map(it) - .singleOrNull() ?: return null - ) - } - } - - override suspend fun findByApid(apId: String): Pair? { - return Posts - .leftJoin(Actors) - .leftJoin(PostsMedia) - .leftJoin(Media) - .selectAll().where { Posts.apId eq apId } - .let { - (it.toNote() ?: return null) to ( - postQueryMapper.map(it) - .singleOrNull() ?: return null - ) - } - } - - private suspend fun ResultRow.toNote(mediaList: List): Note { - val replyId = this[Posts.replyId] - val replyTo = if (replyId != null) { - val url = postRepository.findById(replyId)?.url - if (url == null) { - logger.warn("Failed to get replyId: $replyId") - } - url - } else { - null - } - - val repostId = this[Posts.repostId] - val repost = if (repostId != null) { - val url = postRepository.findById(repostId)?.url - if (url == null) { - logger.warn("Failed to get repostId: $repostId") - } - url - } else { - null - } - - val visibility1 = - visibility( - Visibility.values().first { visibility -> visibility.ordinal == this[Posts.visibility] }, - this[Actors.followers] - ) - return Note( - id = this[Posts.apId], - attributedTo = this[Actors.url], - content = this[Posts.text], - published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(), - to = visibility1.first, - cc = visibility1.second, - inReplyTo = replyTo, - misskeyQuote = repost, - quoteUri = repost, - quoteUrl = repost, - sensitive = this[Posts.sensitive], - attachment = mediaList.map { Document(url = it.url, mediaType = "image/jpeg") } - ) - } - - private suspend fun Query.toNote(): Note? { - return this.groupBy { it[Posts.id] } - .map { it.value } - .map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) } - .singleOrNull() - } - - private fun visibility(visibility: Visibility, followers: String?): Pair, List> { - return when (visibility) { - Visibility.PUBLIC -> listOf(public) to listOf(public) - Visibility.UNLISTED -> listOfNotNull(followers) to listOf(public) - Visibility.FOLLOWERS -> listOfNotNull(followers) to listOfNotNull(followers) - Visibility.DIRECT -> TODO() - } - } - - companion object { - private val logger = LoggerFactory.getLogger(NoteQueryServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt deleted file mode 100644 index a51a7683..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPController.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.actor - -import dev.usbharu.hideout.activitypub.domain.model.Person -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RestController - -@RestController -interface UserAPController { - @GetMapping("/users/{username}") - suspend fun userAp(@PathVariable("username") username: String): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt deleted file mode 100644 index 73b9b8a5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/UserAPControllerImpl.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.actor - -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController - -@RestController -class UserAPControllerImpl(private val apUserService: APUserService) : UserAPController { - override suspend fun userAp(username: String): ResponseEntity { - val person = try { - apUserService.getPersonByName(username) - } catch (_: UserNotFoundException) { - return ResponseEntity.notFound().build() - } - person.context += Constant.context - return ResponseEntity(person, HttpStatus.OK) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt deleted file mode 100644 index f89b9260..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/hostmeta/HostMetaController.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.hostmeta - -import dev.usbharu.hideout.application.config.ApplicationConfig -import org.intellij.lang.annotations.Language -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RestController - -@RestController -class HostMetaController(private val applicationConfig: ApplicationConfig) { - - val xml = //language=XML - """ - - -""" - - @Language("JSON") - val json = """{ - "links": [ - { - "rel": "lrdd", - "type": "application/jrd+json", - "template": "${applicationConfig.url}/.well-known/webfinger?resource={uri}" - } - ] -}""" - - @GetMapping("/.well-known/host-meta", produces = ["application/xml"]) - fun hostmeta(): ResponseEntity = ResponseEntity(xml, HttpStatus.OK) - - @GetMapping("/.well-known/host-meta.json", produces = ["application/json"]) - fun hostmetJson(): ResponseEntity = ResponseEntity(json, HttpStatus.OK) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt deleted file mode 100644 index 2cbbdbec..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxController.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.inbox - -import jakarta.servlet.http.HttpServletRequest -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RestController - -@RestController -interface InboxController { - @RequestMapping( - "/inbox", - "/users/{username}/inbox", - produces = [ - "application/activity+json", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ], - consumes = ["application/json", "application/*+json"], - method = [RequestMethod.POST] - ) - suspend fun inbox(httpServletRequest: HttpServletRequest): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt deleted file mode 100644 index 5045007b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImpl.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.inbox - -import dev.usbharu.hideout.activitypub.service.common.APService -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import jakarta.servlet.http.HttpServletRequest -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.slf4j.MDCContext -import kotlinx.coroutines.withContext -import org.slf4j.LoggerFactory -import org.springframework.http.HttpHeaders.WWW_AUTHENTICATE -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController -import java.net.URL - -@RestController -class InboxControllerImpl( - private val apService: APService, - private val httpSignatureHeaderChecker: HttpSignatureHeaderChecker, -) : InboxController { - @Suppress("TooGenericExceptionCaught") - override suspend fun inbox( - httpServletRequest: HttpServletRequest, - ): ResponseEntity { - val headersList = httpServletRequest.headerNames?.toList().orEmpty() - LOGGER.trace("Inbox Headers {}", headersList) - - val body = withContext(Dispatchers.IO + MDCContext()) { - httpServletRequest.inputStream.readAllBytes()!! - } - - val responseEntity = checkHeader(httpServletRequest, body) - - if (responseEntity != null) { - return responseEntity - } - - val parseActivity = try { - apService.parseActivity(body.decodeToString()) - } catch (e: Exception) { - LOGGER.warn("FAILED Parse Activity", e) - return ResponseEntity.accepted().build() - } - LOGGER.info("INBOX Processing Activity Type: {}", parseActivity) - try { - val url = httpServletRequest.requestURL.toString() - - val headers = - headersList.associateWith { header -> - httpServletRequest.getHeaders(header)?.toList().orEmpty() - } - - apService.processActivity( - body.decodeToString(), - parseActivity, - HttpRequest( - URL(url + httpServletRequest.queryString.orEmpty()), - HttpHeaders(headers), - HttpMethod.POST - ), - headers - ) - } catch (e: Exception) { - LOGGER.warn("FAILED Process Activity $parseActivity", e) - return ResponseEntity(HttpStatus.ACCEPTED) - } - LOGGER.info("SUCCESS Processing Activity Type: {}", parseActivity) - return ResponseEntity(HttpStatus.ACCEPTED) - } - - private fun checkHeader( - httpServletRequest: HttpServletRequest, - body: ByteArray, - ): ResponseEntity? { - try { - httpSignatureHeaderChecker.checkDate(httpServletRequest.getHeader("date")!!) - } catch (_: NullPointerException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Required date header") - } catch (_: IllegalArgumentException) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Request is too old.") - } - try { - httpSignatureHeaderChecker.checkHost(httpServletRequest.getHeader("host")!!) - } catch (_: NullPointerException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Required host header") - } catch (_: IllegalArgumentException) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Wrong host for request") - } - try { - httpSignatureHeaderChecker.checkDigest(body, httpServletRequest.getHeader("digest")!!) - } catch (_: NullPointerException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body("Required request body digest in digest header (sha256)") - } catch (_: IllegalArgumentException) { - return ResponseEntity - .status(HttpStatus.UNAUTHORIZED) - .body("Wrong digest for request") - } - - if (httpServletRequest.getHeader("signature").orEmpty().isBlank()) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .header( - WWW_AUTHENTICATE, - "Signature realm=\"Example\",headers=\"(request-target) date host digest\"" - ) - .build() - } - return null - } - - companion object { - private val LOGGER = LoggerFactory.getLogger(InboxControllerImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt deleted file mode 100644 index 00cda72e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/nodeinfo/NodeinfoController.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.nodeinfo - -import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo -import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0 -import dev.usbharu.hideout.application.config.ApplicationConfig -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RestController - -@RestController -class NodeinfoController(private val applicationConfig: ApplicationConfig) { - @GetMapping("/.well-known/nodeinfo") - fun nodeinfo(): ResponseEntity { - return ResponseEntity( - Nodeinfo( - listOf( - Nodeinfo.Links( - "http://nodeinfo.diaspora.software/ns/schema/2.0", - "${applicationConfig.url}/nodeinfo/2.0" - ) - ) - ), - HttpStatus.OK - ) - } - - @GetMapping("/nodeinfo/2.0") - @Suppress("FunctionNaming") - fun nodeinfo2_0(): ResponseEntity { - return ResponseEntity( - Nodeinfo2_0( - version = "2.0", - software = Nodeinfo2_0.Software( - name = "hideout", - version = "0.0.1" - ), - protocols = listOf("activitypub"), - services = Nodeinfo2_0.Services( - inbound = emptyList(), - outbound = emptyList() - ), - openRegistrations = false, - usage = Nodeinfo2_0.Usage( - users = Nodeinfo2_0.Usage.Users( - total = 1, - activeHalfYear = 1, - activeMonth = 1 - ), - localPosts = 1, - localComments = 0 - ), - metadata = Nodeinfo2_0.Metadata( - nodeName = "hideout", - nodeDescription = "hideout test server", - maintainer = Nodeinfo2_0.Metadata.Maintainer("usbharu", "i@usbharu.dev"), - langs = emptyList(), - tosUrl = "", - repositoryUrl = "https://github.com/usbharu/Hideout", - feedbackUrl = "https://github.com/usbharu/Hideout/issues/new/choose", - ) - ), - HttpStatus.OK - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt deleted file mode 100644 index 2150171a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApController.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable - -interface NoteApController { - @GetMapping("/users/*/posts/{postId}") - suspend fun postsAp( - @PathVariable("postId") postId: Long - ): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt deleted file mode 100644 index 79e54dcb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImpl.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.objects.note.NoteApApiService -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser -import org.springframework.http.ResponseEntity -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RestController - -@RestController -class NoteApControllerImpl(private val noteApApiService: NoteApApiService) : NoteApController { - override suspend fun postsAp( - @PathVariable(value = "postId") postId: Long, - ): ResponseEntity { - val context = SecurityContextHolder.getContext() - val userId = - if (context.authentication is PreAuthenticatedAuthenticationToken && - context.authentication.details is HttpSignatureUser - ) { - (context.authentication.details as HttpSignatureUser).id - } else { - null - } - - val note = noteApApiService.getNote(postId, userId) - if (note != null) { - return ResponseEntity.ok(note) - } - return ResponseEntity.notFound().build() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt deleted file mode 100644 index 0574dd34..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxController.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.outbox - -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RestController - -@RestController -interface OutboxController { - @RequestMapping("/outbox", "/users/{username}/outbox", method = [RequestMethod.POST, RequestMethod.GET]) - suspend fun outbox(): ResponseEntity -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt deleted file mode 100644 index b9ebbbc4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.outbox - -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController - -@RestController -class OutboxControllerImpl : OutboxController { - override suspend fun outbox(): ResponseEntity = - ResponseEntity(HttpStatus.NOT_IMPLEMENTED) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt deleted file mode 100644 index a6d65710..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerController.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.webfinger - -import dev.usbharu.hideout.activitypub.domain.model.webfinger.WebFinger -import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.util.AcctUtil -import kotlinx.coroutines.runBlocking -import org.slf4j.LoggerFactory -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestParam - -@Controller -class WebFingerController( - private val webFingerApiService: WebFingerApiService, - private val applicationConfig: ApplicationConfig -) { - @GetMapping("/.well-known/webfinger") - fun webfinger(@RequestParam("resource") resource: String): ResponseEntity = runBlocking { - logger.info("WEBFINGER Lookup webfinger resource: {}", resource) - val acct = try { - AcctUtil.parse(resource.replace("acct:", "")) - } catch (e: IllegalArgumentException) { - logger.warn("FAILED Parse acct.", e) - return@runBlocking ResponseEntity.badRequest().build() - } - val user = try { - webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host) - } catch (_: UserNotFoundException) { - return@runBlocking ResponseEntity.notFound().build() - } - val webFinger = WebFinger( - "acct:${user.name}@${user.domain}", - listOf( - WebFinger.Link( - "self", - "application/activity+json", - user.url - ) - ) - ) - logger.info("SUCCESS Lookup webfinger resource: {} acct: {}", resource, acct) - ResponseEntity(webFinger, HttpStatus.OK) - } - - companion object { - private val logger = LoggerFactory.getLogger(WebFingerController::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt deleted file mode 100644 index 77bf6287..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/AnnounceQueryService.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.query - -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.core.domain.model.post.Post -import org.springframework.stereotype.Repository - -@Repository -interface AnnounceQueryService { - suspend fun findById(id: Long): Pair? - suspend fun findByApId(apId: String): Pair? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt deleted file mode 100644 index 8d76227b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/query/NoteQueryService.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.query - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.core.domain.model.post.Post - -interface NoteQueryService { - suspend fun findById(id: Long): Pair? - suspend fun findByApid(apId: String): Pair? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt deleted file mode 100644 index 7df08bb3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessor.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import org.springframework.stereotype.Service - -@Service -class ApAcceptProcessor( - transaction: Transaction, - private val relationshipService: RelationshipService, - private val actorRepository: ActorRepository -) : - AbstractActivityPubProcessor(transaction) { - - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.apObject - - if (value.type.contains("Follow").not()) { - logger.warn("FAILED Activity type isn't Follow.") - throw IllegalActivityPubObjectException("Invalid type ${value.type}") - } - - val follow = value as Follow - - val userUrl = follow.apObject - val followerUrl = follow.actor - - val user = actorRepository.findByUrl(userUrl) ?: throw UserNotFoundException.withUrl(userUrl) - val follower = actorRepository.findByUrl(followerUrl) ?: throw UserNotFoundException.withUrl(followerUrl) - - relationshipService.acceptFollowRequest(user.id, follower.id) - logger.debug("SUCCESS Follow from ${user.url} to ${follower.url}.") - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Accept - - override fun type(): Class = Accept::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt deleted file mode 100644 index ea1793e6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApSendAcceptService.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.external.job.DeliverAcceptTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service - -interface ApSendAcceptService { - suspend fun sendAcceptFollow(actor: Actor, target: Actor) -} - -@Service -class ApSendAcceptServiceImpl( - private val owlProducer: OwlProducer, -) : ApSendAcceptService { - override suspend fun sendAcceptFollow(actor: Actor, target: Actor) { - val deliverAcceptTask = DeliverAcceptTask( - Accept( - apObject = Follow( - apObject = actor.url, - actor = target.url - ), - actor = actor.url - ), - target.inbox, - actor.id - ) - - owlProducer.publishTask(deliverAcceptTask) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt deleted file mode 100644 index 17ff0cac..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/announce/ApAnnounceProcessor.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.announce - -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.application.external.Transaction -import org.springframework.stereotype.Service - -@Service -class ApAnnounceProcessor(transaction: Transaction, private val apNoteService: APNoteService) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - apNoteService.fetchAnnounce(activity.activity) - } - - override fun isSupported(activityType: ActivityType): Boolean = ActivityType.Announce == activityType - - override fun type(): Class = Announce::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt deleted file mode 100644 index 2b8921ca..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.core.domain.model.post.Post - -interface ApSendCreateService { - suspend fun createNote(post: Post) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt deleted file mode 100644 index 0fb4a1e8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/ApSendCreateServiceImpl.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.external.job.DeliverCreateTask -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class ApSendCreateServiceImpl( - private val followerQueryService: FollowerQueryService, - private val noteQueryService: NoteQueryService, - private val applicationConfig: ApplicationConfig, - private val actorRepository: ActorRepository, - private val owlProducer: OwlProducer, -) : ApSendCreateService { - override suspend fun createNote(post: Post) { - logger.info("CREATE Create Local Note ${post.url}") - logger.debug("START Create Local Note ${post.url}") - logger.trace("{}", post) - val followers = followerQueryService.findFollowersById(post.actorId) - - logger.debug("DELIVER Deliver Note Create ${followers.size} accounts.") - - val userEntity = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) - val note = noteQueryService.findById(post.id)?.first ?: throw PostNotFoundException.withId(post.id) - val create = Create( - name = "Create Note", - apObject = note, - actor = note.attributedTo, - id = "${applicationConfig.url}/create/note/${post.id}" - ) - followers.forEach { followerEntity -> - owlProducer.publishTask(DeliverCreateTask(create, userEntity.url, followerEntity.inbox)) - } - - logger.debug("SUCCESS Create Local Note ${post.url}") - } - - companion object { - private val logger = LoggerFactory.getLogger(ApSendCreateServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt deleted file mode 100644 index 9be6fa65..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/create/CreateActivityProcessor.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.create - -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.application.external.Transaction -import org.springframework.stereotype.Service - -@Service -class CreateActivityProcessor(transaction: Transaction, private val apNoteService: APNoteService) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - apNoteService.fetchNote(activity.activity.apObject as Note) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Create - - override fun type(): Class = Create::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt deleted file mode 100644 index a0d789c5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APDeleteProcessor.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.delete - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.activitypub.domain.model.HasId -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -@Service -class APDeleteProcessor( - transaction: Transaction, - private val userService: UserService, - private val postService: PostService, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val value = activity.activity.apObject - val deleteId = if (value is HasId) { - value.id - } else if (value is ObjectValue) { - value.`object` - } else { - throw IllegalActivityPubObjectException("object hasn't id or object") - } - - val actor = actorRepository.findByUrl(deleteId) - actor?.let { userService.deleteRemoteActor(it.id) } - - val post = postRepository.findByApId(deleteId) - if (post == null) { - logger.warn("FAILED Delete id: {} is not found.", deleteId) - return - } - postService.deleteRemote(post) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Delete - - override fun type(): Class = Delete::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt deleted file mode 100644 index 4570808d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/delete/APSendDeleteService.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.delete - -import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.hideout.activitypub.domain.model.Tombstone -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.external.job.DeliverDeleteTask -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service -import java.time.Instant - -interface APSendDeleteService { - suspend fun sendDeleteNote(deletedPost: Post) - suspend fun sendDeleteActor(deletedActor: Actor) -} - -@Service -class APSendDeleteServiceImpl( - private val followerQueryService: FollowerQueryService, - private val applicationConfig: ApplicationConfig, - private val actorRepository: ActorRepository, - private val owlProducer: OwlProducer, -) : APSendDeleteService { - override suspend fun sendDeleteNote(deletedPost: Post) { - val actor = - actorRepository.findById(deletedPost.actorId) ?: throw UserNotFoundException.withId(deletedPost.actorId) - val followersById = followerQueryService.findFollowersById(deletedPost.actorId) - - val delete = Delete( - actor = actor.url, - id = "${applicationConfig.url}/delete/note/${deletedPost.id}", - published = Instant.now().toString(), - `object` = Tombstone(id = deletedPost.apId) - ) - - followersById.forEach { - val jobProps = DeliverDeleteTask( - delete, - it.inbox, - actor.id - ) - - owlProducer.publishTask(jobProps) - } - } - - override suspend fun sendDeleteActor(deletedActor: Actor) { - val followers = followerQueryService.findFollowersById(deletedActor.id) - - val delete = Delete( - actor = deletedActor.url, - `object` = ObjectValue(emptyList(), `object` = deletedActor.url), - id = "${applicationConfig.url}/delete/actor/${deletedActor.id}", - published = Instant.now().toString() - ) - - followers.forEach { - DeliverDeleteTask( - delete = delete, - it.inbox, - deletedActor.id - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt deleted file mode 100644 index 3cc8cc68..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APFollowProcessor.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.external.job.ReceiveFollowTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service - -@Service -class APFollowProcessor( - transaction: Transaction, - private val owlProducer: OwlProducer, -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - logger.info("FOLLOW from: {} to {}", activity.activity.actor, activity.activity.apObject) - - // inboxをジョブキューã«ä¹—ã›ã¦ã„ã‚‹ã®ã§æ—¢ã«ä¸è¦ã ãŒã€ãƒ•ã‚©ãƒ­ãƒ¼æ‰¿èªåˆ¶ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’実装ã™ã‚‹éš›ã«å¿…è¦ãªã®ã§æ®‹ã™ - val jobProps = ReceiveFollowTask( - activity.activity.actor, - activity.activity, - activity.activity.apObject - ) - owlProducer.publishTask(jobProps) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Follow - - override fun type(): Class = Follow::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt deleted file mode 100644 index 9d69e7be..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APReceiveFollowService.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.core.external.job.ReceiveFollowTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -interface APReceiveFollowService { - suspend fun receiveFollow(follow: Follow) -} - -@Service -class APReceiveFollowServiceImpl( - private val owlProducer: OwlProducer, -) : APReceiveFollowService { - override suspend fun receiveFollow(follow: Follow) { - logger.info("FOLLOW from: {} to: {}", follow.actor, follow.apObject) - owlProducer.publishTask(ReceiveFollowTask(follow.actor, follow, follow.apObject)) - return - } - - companion object { - private val logger = LoggerFactory.getLogger(APReceiveFollowServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt deleted file mode 100644 index 80ac8c7f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowService.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.service.follow.SendFollowDto -import org.springframework.stereotype.Service - -interface APSendFollowService { - suspend fun sendFollow(sendFollowDto: SendFollowDto) -} - -@Service -class APSendFollowServiceImpl( - private val apRequestService: APRequestService, - private val applicationConfig: ApplicationConfig, -) : APSendFollowService { - override suspend fun sendFollow(sendFollowDto: SendFollowDto) { - val follow = Follow( - apObject = sendFollowDto.followTargetActorId.url, - actor = sendFollowDto.actorId.url, - id = "${applicationConfig.url}/follow/${sendFollowDto.actorId.id}/${sendFollowDto.followTargetActorId.id}" - ) - - apRequestService.apPost(sendFollowDto.followTargetActorId.inbox, follow, sendFollowDto.actorId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt deleted file mode 100644 index b018b6d4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APLikeProcessor.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.service.reaction.ReactionService -import org.springframework.stereotype.Service - -@Service -class APLikeProcessor( - transaction: Transaction, - private val apUserService: APUserService, - private val apNoteService: APNoteService, - private val reactionService: ReactionService, - private val emojiService: EmojiService -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val actor = activity.activity.actor - val content = activity.activity.content - - val target = activity.activity.apObject - - val personWithEntity = apUserService.fetchPersonWithEntity(actor) - - try { - val post = apNoteService.fetchNoteWithEntity(target).second - - val emoji = if (content.startsWith(":")) { - val tag = activity.activity.tag - (tag.firstOrNull { it is Emoji } as? Emoji)?.let { emojiService.fetchEmoji(it).second } - } else { - UnicodeEmoji(content) - } - - reactionService.receiveReaction( - emoji ?: UnicodeEmoji("â¤"), - personWithEntity.second.id, - post.id - ) - - logger.debug("SUCCESS Add Like($content) from ${personWithEntity.second.url} to ${post.url}") - } catch (e: FailedToGetActivityPubResourceException) { - logger.debug("FAILED failed to get {}", target) - logger.trace("", e) - return - } - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Like - - override fun type(): Class = Like::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt deleted file mode 100644 index fac9ccff..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/like/APReactionService.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.like - -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.external.job.DeliverReactionTask -import dev.usbharu.hideout.core.external.job.DeliverUndoTask -import dev.usbharu.hideout.core.query.FollowerQueryService -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service -import java.time.Instant - -interface APReactionService { - suspend fun reaction(like: Reaction) - suspend fun removeReaction(like: Reaction) -} - -@Service -class APReactionServiceImpl( - private val followerQueryService: FollowerQueryService, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val applicationConfig: ApplicationConfig, - private val owlProducer: OwlProducer, -) : APReactionService { - override suspend fun reaction(like: Reaction) { - val followers = followerQueryService.findFollowersById(like.actorId) - val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) - val post = - postRepository.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId) - followers.forEach { follower -> - owlProducer.publishTask( - DeliverReactionTask( - actor = user.url, - like = Like( - actor = user.url, - id = "${applicationConfig.url}/like/note/${post.id}", - content = "â¤", - apObject = post.url - ), - inbox = follower.inbox - ) - ) - } - } - - override suspend fun removeReaction(like: Reaction) { - val followers = followerQueryService.findFollowersById(like.actorId) - val user = actorRepository.findById(like.actorId) ?: throw UserNotFoundException.withId(like.actorId) - val post = - postRepository.findById(like.postId) ?: throw PostNotFoundException.withId(like.postId) - followers.forEach { follower -> - owlProducer.publishTask( - DeliverUndoTask( - signer = user.id, - inbox = follower.inbox, - undo = Undo( - actor = user.url, - id = "${applicationConfig.url}/undo/like/${post.id}", - apObject = Like( - actor = user.url, - id = "${applicationConfig.url}/like/note/${post.id}", - content = "â¤", - apObject = post.url - ), - published = Instant.now().toString(), - ) - ) - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt deleted file mode 100644 index 9a3d5bb0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApRejectProcessor.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.reject - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Reject -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import org.springframework.stereotype.Service - -@Service -class ApRejectProcessor( - private val relationshipService: RelationshipService, - transaction: Transaction, - private val actorRepository: ActorRepository -) : - AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val activityType = activity.activity.apObject.type.firstOrNull { it == "Follow" } - - if (activityType == null) { - logger.warn("FAILED Process Reject Activity type: {}", activity.activity.apObject.type) - return - } - when (activityType) { - "Follow" -> { - val user = actorRepository.findByUrl(activity.activity.actor) ?: throw UserNotFoundException.withUrl( - activity.activity.actor - ) - - activity.activity.apObject as Follow - - val actor = activity.activity.apObject.actor - - val target = actorRepository.findByUrl(actor) ?: throw UserNotFoundException.withUrl(actor) - - logger.debug("REJECT Follow user {} target {}", user.url, target.url) - - relationshipService.rejectFollowRequest(user.id, target.id) - } - - else -> {} - } - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Reject - - override fun type(): Class = Reject::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt deleted file mode 100644 index e925aef0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.reject - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface ApSendRejectService { - suspend fun sendRejectFollow(actor: Actor, target: Actor) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt deleted file mode 100644 index eef8dc2f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/reject/ApSendRejectServiceImpl.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.reject - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Reject -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.external.job.DeliverRejectTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service - -@Service -class ApSendRejectServiceImpl( - private val applicationConfig: ApplicationConfig, - private val owlProducer: OwlProducer, -) : ApSendRejectService { - override suspend fun sendRejectFollow(actor: Actor, target: Actor) { - val deliverRejectTask = DeliverRejectTask( - Reject( - actor.url, - "${applicationConfig.url}/reject/${actor.id}/${target.id}", - Follow(apObject = actor.url, actor = target.url) - ), - target.inbox, - actor.id - ) - - owlProducer.publishTask(deliverRejectTask) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt deleted file mode 100644 index 80d4828b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface APSendUndoService { - suspend fun sendUndoFollow(actor: Actor, target: Actor) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt deleted file mode 100644 index 37326c0d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APSendUndoServiceImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.external.job.DeliverUndoTask -import dev.usbharu.owl.producer.api.OwlProducer -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class APSendUndoServiceImpl( - private val applicationConfig: ApplicationConfig, - private val owlProducer: OwlProducer, -) : APSendUndoService { - override suspend fun sendUndoFollow(actor: Actor, target: Actor) { - val deliverUndoTask = DeliverUndoTask( - Undo( - actor = actor.url, - id = "${applicationConfig.url}/undo/follow/${actor.id}/${target.url}", - apObject = Follow( - apObject = actor.url, - actor = target.url - ), - published = Instant.now().toString() - ), - target.inbox, - actor.id - ) - - owlProducer.publishTask(deliverUndoTask) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt deleted file mode 100644 index e788929e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/activity/undo/APUndoProcessor.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.undo - -import dev.usbharu.hideout.activitypub.domain.model.* -import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectValue -import dev.usbharu.hideout.activitypub.service.common.AbstractActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.local.LocalUserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.core.service.reaction.ReactionService -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -@Service -class APUndoProcessor( - transaction: Transaction, - private val apUserService: APUserService, - private val relationshipService: RelationshipService, - private val reactionService: ReactionService, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val postService: PostService, - private val userService: UserService, -) : AbstractActivityPubProcessor(transaction) { - override suspend fun internalProcess(activity: ActivityPubProcessContext) { - val undo = activity.activity - - val type = undo.apObject.type.firstOrNull { - it == "Block" || it == "Follow" || it == "Like" || it == "Announce" || it == "Accept" - } ?: return - - when (type) { - "Follow" -> { - follow(undo) - return - } - - "Accept" -> { - accept(undo) - return - } - - "Like" -> { - like(undo) - return - } - - "Announce" -> { - announce(undo) - return - } - - "Delete" -> { - delete(undo) - return - } - - else -> {} - } - TODO() - } - - private suspend fun accept(undo: Undo) { - val accept = undo.apObject as Accept - - val acceptObject = if (accept.apObject is ObjectValue) { - accept.apObject.`object` - } else if (accept.apObject is Follow) { - accept.apObject.apObject - } else { - logger.warn("FAILED Unsupported type. Undo Accept {}", accept.apObject.type) - return - } - - val accepter = apUserService.fetchPersonWithEntity(undo.actor, acceptObject).second - val target = actorRepository.findByUrl(acceptObject) ?: throw UserNotFoundException.withUrl(acceptObject) - - relationshipService.rejectFollowRequest(accepter.id, target.id) - return - } - - private suspend fun like(undo: Undo) { - val like = undo.apObject as Like - - val post = postRepository.findByUrl(like.apObject) ?: throw PostNotFoundException.withUrl(like.apObject) - - val signer = actorRepository.findById(post.actorId) ?: throw LocalUserNotFoundException.withId(post.actorId) - val actor = apUserService.fetchPersonWithEntity(like.actor, signer.url).second - - reactionService.receiveRemoveReaction(actor.id, post.id) - return - } - - private suspend fun follow(undo: Undo) { - val follow = undo.apObject as Follow - - val follower = apUserService.fetchPersonWithEntity(undo.actor, follow.apObject).second - val target = actorRepository.findByUrl(follow.apObject) ?: throw UserNotFoundException.withUrl(follow.apObject) - - relationshipService.unfollow(follower.id, target.id) - return - } - - private suspend fun announce(undo: Undo) { - val announce = undo.apObject as Announce - - val findByApId = postRepository.findByApId(announce.id) ?: return - postService.deleteRemote(findByApId) - } - - private suspend fun delete(undo: Undo) { - val announce = undo.apObject as Delete - - val actor = actorRepository.findByUrl(announce.actor) ?: throw UserNotFoundException.withUrl(announce.actor) - - userService.restorationRemoteActor(actor.id) - } - - override fun isSupported(activityType: ActivityType): Boolean = activityType == ActivityType.Undo - - override fun type(): Class = Undo::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt deleted file mode 100644 index 2507e3cb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestService.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface APRequestService { - suspend fun apGet(url: String, signer: Actor? = null, responseClass: Class): R - suspend fun apPost( - url: String, - body: T? = null, - signer: Actor? = null, - responseClass: Class - ): R - - suspend fun apPost(url: String, body: T? = null, signer: Actor? = null): String -} - -suspend inline fun APRequestService.apGet(url: String, signer: Actor? = null): R = - apGet(url, signer, R::class.java) - -suspend inline fun APRequestService.apPost( - url: String, - body: T? = null, - signer: Actor? = null -): R = apPost(url, body, signer, R::class.java) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt deleted file mode 100644 index 4b463532..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImpl.kt +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.util.Base64Util -import dev.usbharu.hideout.util.HttpUtil.Activity -import dev.usbharu.hideout.util.RsaUtil -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.common.PrivateKey -import dev.usbharu.httpsignature.sign.HttpSignatureSigner -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.util.* -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.net.URL -import java.security.MessageDigest -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter - -@Service -class APRequestServiceImpl( - private val httpClient: HttpClient, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val httpSignatureSigner: HttpSignatureSigner, - @Qualifier("http") private val dateTimeFormatter: DateTimeFormatter, -) : APRequestService { - - override suspend fun apGet(url: String, signer: Actor?, responseClass: Class): R { - logger.debug("START ActivityPub Request GET url: {}, signer: {}", url, signer?.url) - val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT"))) - val u = URL(url) - val httpResponse = if (signer?.privateKey == null) { - apGetNotSign(url, date) - } else { - apGetSign(date, u, signer, url) - } - - val bodyAsText = httpResponse.bodyAsText() - val readValue = objectMapper.readValue(bodyAsText, responseClass) - logger.debug( - "SUCCESS ActivityPub Request GET status: {} url: {}", - httpResponse.status, - httpResponse.request.url - ) - logBody(bodyAsText, url) - return readValue - } - - private suspend fun apGetSign( - date: String, - u: URL, - signer: Actor, - url: String, - ): HttpResponse { - val headers = headers { - append("Accept", Activity) - append("Date", date) - append("Host", u.host) - } - - val sign = httpSignatureSigner.sign( - httpRequest = HttpRequest( - url = u, - headers = HttpHeaders(headers.toMap()), - HttpMethod.GET - ), - privateKey = PrivateKey( - keyId = "${signer.url}#pubkey", - privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!), - ), - signHeaders = listOf("(request-target)", "date", "host", "accept") - ) - - val httpResponse = httpClient.get(url) { - headers { - headers { - appendAll(headers) - append("Signature", sign.signatureHeader) -// remove("Host") - } - } - contentType(Activity) - } - return httpResponse - } - - private suspend fun apGetNotSign(url: String, date: String?) = httpClient.get(url) { - header("Accept", Activity) - header("Date", date) - } - - override suspend fun apPost( - url: String, - body: T?, - signer: Actor?, - responseClass: Class, - ): R { - val bodyAsText = apPost(url, body, signer) - return objectMapper.readValue(bodyAsText, responseClass) - } - - override suspend fun apPost(url: String, body: T?, signer: Actor?): String { - logger.debug("START ActivityPub Request POST url: {}, signer: {}", url, signer?.url) - val requestBody = addContextIfNotNull(body) - - logger.trace( - """ - | - |***** BEGIN HTTP Request Trace url: {} ***** - | - |$requestBody - | - |***** END HTTP Request Trace url: {} ***** - | - """.trimMargin(), - url, - url - ) - - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(requestBody.orEmpty().toByteArray())) - - val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT"))) - val u = URL(url) - val httpResponse = if (signer?.privateKey == null) { - apPostNotSign(url, date, digest, requestBody) - } else { - apPostSign(date, u, digest, signer, requestBody) - } - - val bodyAsText = httpResponse.bodyAsText() - logger.debug( - "SUCCESS ActivityPub Request POST status: {} url: {}", - httpResponse.status, - httpResponse.request.url - ) - logBody(bodyAsText, url) - return bodyAsText - } - - private suspend fun apPostNotSign( - url: String, - date: String?, - digest: String, - requestBody: String?, - ) = httpClient.post(url) { - accept(Activity) - header("Date", date) - header("Digest", "sha-256=$digest") - if (requestBody != null) { - setBody(requestBody) - contentType(Activity) - } - } - - private suspend fun apPostSign( - date: String, - u: URL, - digest: String, - signer: Actor, - requestBody: String?, - ): HttpResponse { - val headers = headers { - append("Accept", Activity) - append("Date", date) - append("Host", u.host) - append("Digest", "SHA-256=$digest") - } - - val sign = httpSignatureSigner.sign( - httpRequest = HttpRequest( - u, - HttpHeaders(headers.toMap()), - HttpMethod.POST - ), - privateKey = PrivateKey( - keyId = signer.keyId, - privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!) - ), - signHeaders = listOf("(request-target)", "date", "host", "digest") - ) - - val httpResponse = httpClient.post(u) { - headers { - appendAll(headers) - append("Signature", sign.signatureHeader) -// remove("Host") - } - setBody(requestBody) - contentType(Activity) - } - return httpResponse - } - - private fun addContextIfNotNull(body: T?) = if (body != null) { - val context = mutableListOf() - context.addAll(Constant.context) - context.addAll(body.context) - body.context = context - objectMapper.writeValueAsString(body) - } else { - null - } - - private fun logBody(bodyAsText: String, url: String) { - logger.trace( - """ - | - |***** BEGIN HTTP Response Trace url: {} ***** - | - |$bodyAsText - | - |***** END HTTP Response TRACE url: {} ***** - | - """.trimMargin(), - url, - url - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(APRequestServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt deleted file mode 100644 index bd42e197..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveService.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface APResourceResolveService { - suspend fun resolve(url: String, clazz: Class, singer: Actor?): T - suspend fun resolve(url: String, clazz: Class, singerId: Long?): T -} - -suspend inline fun APResourceResolveService.resolve(url: String, singer: Actor?): T = - resolve(url, T::class.java, singer) - -suspend inline fun APResourceResolveService.resolve(url: String, singerId: Long?): T = - resolve(url, T::class.java, singerId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt deleted file mode 100644 index f40589b1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.resource.CacheManager -import dev.usbharu.hideout.core.service.resource.ResolveResponse -import org.springframework.stereotype.Service -import java.io.InputStream - -@Service -class APResourceResolveServiceImpl( - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, - private val cacheManager: CacheManager -) : - APResourceResolveService { - - override suspend fun resolve(url: String, clazz: Class, singerId: Long?): T = - internalResolve(url, singerId, clazz) - - override suspend fun resolve(url: String, clazz: Class, singer: Actor?): T = - internalResolve(url, singer, clazz) - - private suspend fun internalResolve(url: String, singerId: Long?, clazz: Class): T { - val key = genCacheKey(url, singerId) - - cacheManager.putCache(key) { - runResolve(url, singerId?.let { actorRepository.findById(it) }, clazz) - } - return (cacheManager.getOrWait(key) as APResolveResponse).objects - } - - private suspend fun internalResolve(url: String, singer: Actor?, clazz: Class): T { - val key = genCacheKey(url, singer?.id) - cacheManager.putCache(key) { - runResolve(url, singer, clazz) - } - return (cacheManager.getOrWait(key) as APResolveResponse).objects - } - - private suspend fun runResolve(url: String, singer: Actor?, clazz: Class): ResolveResponse = - APResolveResponse(apRequestService.apGet(url, singer, clazz)) - - private fun genCacheKey(url: String, singerId: Long?): String { - if (singerId != null) { - return "$url-$singerId" - } - return url - } - - private class APResolveResponse(val objects: T) : ResolveResponse { - override suspend fun body(): InputStream { - TODO("Not yet implemented") - } - - override suspend fun bodyAsText(): String { - TODO("Not yet implemented") - } - - override suspend fun bodyAsBytes(): ByteArray { - TODO("Not yet implemented") - } - - override suspend fun header(): Map> { - TODO("Not yet implemented") - } - - override suspend fun status(): Int { - TODO("Not yet implemented") - } - - override suspend fun statusMessage(): String { - TODO("Not yet implemented") - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as APResolveResponse<*> - - return objects == other.objects - } - - override fun hashCode(): Int = objects.hashCode() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt deleted file mode 100644 index 64aa581b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APService.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import dev.usbharu.hideout.core.external.job.InboxTask -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service - -interface APService { - fun parseActivity(json: String): ActivityType - - suspend fun processActivity( - json: String, - type: ActivityType, - httpRequest: HttpRequest, - map: Map> - ) -} - -@Service -class APServiceImpl( - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val owlProducer: OwlProducer, -) : APService { - - val logger: Logger = LoggerFactory.getLogger(APServiceImpl::class.java) - override fun parseActivity(json: String): ActivityType { - val readTree = try { - objectMapper.readTree(json) - } catch (e: com.fasterxml.jackson.core.JsonParseException) { - throw JsonParseException("Failed to parse json", e) - } - logger.trace( - """ - | - |***** Trace Begin Activity ***** - | - |{} - | - |***** Trace End Activity ***** - | - """.trimMargin(), - readTree.toPrettyString() - ) - if (readTree.isObject.not()) { - throw JsonParseException("Json is not object.") - } - val type = readTree["type"] ?: throw JsonParseException("Type is null") - if (type.isArray) { - try { - return type.firstNotNullOf { jsonNode: JsonNode -> - ActivityType.entries.firstOrNull { it.name.equals(jsonNode.asText(), true) } - } - } catch (e: NoSuchElementException) { - throw IllegalArgumentException("No valid TYPE", e) - } - } - try { - return ActivityType.entries.first { it.name.equals(type.asText(), true) } - } catch (e: NoSuchElementException) { - throw IllegalArgumentException("No valid TYPE", e) - } - } - - override suspend fun processActivity( - json: String, - type: ActivityType, - httpRequest: HttpRequest, - map: Map> - ) { - logger.debug("process activity: {}", type) - owlProducer.publishTask( - InboxTask( - json, - type, - httpRequest, - map - ) - ) - return - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt deleted file mode 100644 index f38d7cc1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/AbstractActivityPubProcessor.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.exception.ActivityPubProcessException -import dev.usbharu.hideout.activitypub.domain.exception.FailedProcessException -import dev.usbharu.hideout.activitypub.domain.exception.HttpSignatureUnauthorizedException -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.sql.SQLException - -abstract class AbstractActivityPubProcessor( - private val transaction: Transaction, - private val allowUnauthorized: Boolean = false -) : ActivityPubProcessor { - protected val logger: Logger = LoggerFactory.getLogger(this::class.java) - - override suspend fun process(activity: ActivityPubProcessContext) { - if (activity.isAuthorized.not() && allowUnauthorized.not()) { - throw HttpSignatureUnauthorizedException() - } - logger.info("START ActivityPub process. {}", this.type()) - try { - transaction.transaction { - try { - internalProcess(activity) - } catch (e: ResourceAccessException) { - throw SQLException(e) - } - } - } catch (e: ActivityPubProcessException) { - logger.warn("FAILED ActivityPub process", e) - throw FailedProcessException("Failed process", e) - } - logger.info("SUCCESS ActivityPub process. {}", this.type()) - } - - abstract suspend fun internalProcess(activity: ActivityPubProcessContext) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt deleted file mode 100644 index 50d07478..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessContext.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.databind.JsonNode -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.verify.Signature - -data class ActivityPubProcessContext( - val activity: T, - val jsonNode: JsonNode, - val httpRequest: HttpRequest, - val signature: Signature?, - val isAuthorized: Boolean -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt deleted file mode 100644 index d558e798..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityPubProcessor.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -interface ActivityPubProcessor { - suspend fun process(activity: ActivityPubProcessContext) - - fun isSupported(activityType: ActivityType): Boolean - - fun type(): Class -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt deleted file mode 100644 index acd1fcb6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityType.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ActivityType { - Accept, - Add, - Announce, - Arrive, - Block, - Create, - Delete, - Dislike, - Flag, - Follow, - Ignore, - Invite, - Join, - Leave, - Like, - Listen, - Move, - Offer, - Question, - Reject, - Read, - Remove, - TentativeReject, - TentativeAccept, - Travel, - Undo, - Update, - View, - Other -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt deleted file mode 100644 index 4d42dfff..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ActivityVocabulary.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ActivityVocabulary { - Object, - Link, - Activity, - IntransitiveActivity, - Collection, - OrderedCollection, - CollectionPage, - OrderedCollectionPage, - Accept, - Add, - Announce, - Arrive, - Block, - Create, - Delete, - Dislike, - Flag, - Follow, - Ignore, - Invite, - Join, - Leave, - Like, - Listen, - Move, - Offer, - Question, - Reject, - Read, - Remove, - TentativeReject, - TentativeAccept, - Travel, - Undo, - Update, - View, - Application, - Group, - Organization, - Person, - Service, - Article, - Audio, - Document, - Event, - Image, - Note, - Page, - Place, - Profile, - Relationship, - Tombstone, - Video, - Mention, -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt deleted file mode 100644 index b8150064..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedActivityVocabulary.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ExtendedActivityVocabulary { - Object, - Link, - Activity, - IntransitiveActivity, - Collection, - OrderedCollection, - CollectionPage, - OrderedCollectionPage, - Accept, - Add, - Announce, - Arrive, - Block, - Create, - Delete, - Dislike, - Flag, - Follow, - Ignore, - Invite, - Join, - Leave, - Like, - Listen, - Move, - Offer, - Question, - Reject, - Read, - Remove, - TentativeReject, - TentativeAccept, - Travel, - Undo, - Update, - View, - Application, - Group, - Organization, - Person, - Service, - Article, - Audio, - Document, - Event, - Image, - Note, - Page, - Place, - Profile, - Relationship, - Tombstone, - Video, - Mention, - Emoji -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt deleted file mode 100644 index ef1c06c6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/ExtendedVocabulary.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -enum class ExtendedVocabulary { - Emoji -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt deleted file mode 100644 index 7687dcef..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiService.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.emoji - -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji - -interface EmojiService { - suspend fun fetchEmoji(url: String): Pair - suspend fun fetchEmoji(emoji: Emoji): Pair - suspend fun findByEmojiName(emojiName: String): CustomEmoji? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt deleted file mode 100644 index 61ba3904..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/emoji/EmojiServiceImpl.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.emoji - -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveServiceImpl -import dev.usbharu.hideout.activitypub.service.common.resolve -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository -import dev.usbharu.hideout.core.service.instance.InstanceService -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.media.RemoteMedia -import org.springframework.stereotype.Service -import java.net.URL -import java.time.Instant - -@Service -class EmojiServiceImpl( - private val customEmojiRepository: CustomEmojiRepository, - private val instanceService: InstanceService, - private val mediaService: MediaService, - private val apResourceResolveServiceImpl: APResourceResolveServiceImpl, - private val applicationConfig: ApplicationConfig -) : EmojiService { - override suspend fun fetchEmoji(url: String): Pair { - val emoji = apResourceResolveServiceImpl.resolve(url, null as Long?) - return fetchEmoji(emoji) - } - - override suspend fun fetchEmoji(emoji: Emoji): Pair = emoji to save(emoji) - - private suspend fun save(emoji: Emoji): CustomEmoji { - val domain = URL(emoji.id).host - val name = emoji.name.trim(':') - val customEmoji = customEmojiRepository.findByNameAndDomain(name, domain) - - if (customEmoji != null) { - return customEmoji - } - - val instance = instanceService.fetchInstance(emoji.id) - - val media = mediaService.uploadRemoteMedia( - RemoteMedia( - emoji.name, - emoji.icon.url, - emoji.icon.mediaType.orEmpty(), - null - ) - ) - - val customEmoji1 = CustomEmoji( - id = customEmojiRepository.generateId(), - name = name, - domain = domain, - instanceId = instance.id, - url = media.url, - category = null, - createdAt = Instant.now() - ) - - return customEmojiRepository.save(customEmoji1) - } - - override suspend fun findByEmojiName(emojiName: String): CustomEmoji? { - val split = emojiName.trim(':').split("@") - - return when (split.size) { - 1 -> { - customEmojiRepository.findByNameAndDomain(split.first(), applicationConfig.url.host) - } - - 2 -> { - customEmojiRepository.findByNameAndDomain(split.first(), split[1]) - } - - else -> throw IllegalArgumentException("Unknown Emoji Format. $emojiName") - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt deleted file mode 100644 index ea9358a3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteService.kt +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.model.Announce -import dev.usbharu.hideout.activitypub.domain.model.Emoji -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.query.AnnounceQueryService -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService -import dev.usbharu.hideout.activitypub.service.common.resolve -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -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.PostRepository -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.media.RemoteMedia -import dev.usbharu.hideout.core.service.post.PostService -import io.ktor.client.plugins.* -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -interface APNoteService { - suspend fun fetchNote(url: String, targetActor: String? = null): Note = fetchNoteWithEntity(url, targetActor).first - suspend fun fetchNote(note: Note, targetActor: String? = null): Note - suspend fun fetchNoteWithEntity(url: String, targetActor: String? = null): Pair - - suspend fun fetchAnnounce(url: String, signerId: Long? = null): Pair - suspend fun fetchAnnounce(announce: Announce, signerId: Long? = null): Pair -} - -@Service -@Suppress("LongParameterList") -class APNoteServiceImpl( - private val postRepository: PostRepository, - private val apUserService: APUserService, - private val postService: PostService, - private val apResourceResolveService: APResourceResolveService, - private val postBuilder: Post.PostBuilder, - private val noteQueryService: NoteQueryService, - private val mediaService: MediaService, - private val emojiService: EmojiService, - private val announceQueryService: AnnounceQueryService - -) : APNoteService { - - private val logger = LoggerFactory.getLogger(APNoteServiceImpl::class.java) - - override suspend fun fetchNoteWithEntity(url: String, targetActor: String?): Pair { - logger.debug("START Fetch Note url: {}", url) - - val post = noteQueryService.findByApid(url) - - if (post != null) { - logger.debug("SUCCESS Found in local url: {}", url) - return post - } - - logger.info("AP GET url: {}", url) - val note = try { - apResourceResolveService.resolve(url, null as Long?) - } catch (e: ClientRequestException) { - logger.warn( - "FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", - e.response.status, - url - ) - throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) - } - val savedNote = saveIfMissing(note, targetActor) - logger.debug("SUCCESS Fetch Note url: {}", url) - return savedNote - } - - override suspend fun fetchAnnounce(url: String, signerId: Long?): Pair { - logger.debug("START Fetch Announce url: {}", url) - - val post: Pair? = announceQueryService.findByApId(url) - - if (post != null) { - logger.debug("SUCCESS Found in local url: {}", url) - return post - } - - logger.info("AP GET url: {}", url) - - val announce = try { - apResourceResolveService.resolve(url, signerId) - } catch (e: ClientRequestException) { - logger.warn( - "FAILED Failed to retrieve ActivityPub resource. HTTP Status Code: {} url: {}", - e.response.status, - url - ) - throw FailedToGetActivityPubResourceException("Could not retrieve $url.", e) - } - - return fetchAnnounce(announce, signerId) - } - - override suspend fun fetchAnnounce(announce: Announce, signerId: Long?): Pair { - val findByApId = announceQueryService.findByApId(announce.id) - - if (findByApId != null) { - return findByApId - } - - val (_, actor) = apUserService.fetchPersonWithEntity(announce.actor, null) - - val (_, post) = fetchNoteWithEntity(announce.apObject, null) - - val visibility = if (announce.to.contains(public)) { - Visibility.PUBLIC - } else if (announce.to.contains(actor.followers) && announce.cc.contains(public)) { - Visibility.UNLISTED - } else if (announce.to.contains(actor.followers)) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } - - val createRemote = postService.createRemote( - postBuilder.pureRepostOf( - id = postRepository.generateId(), - actorId = actor.id, - visibility = visibility, - createdAt = Instant.parse(announce.published), - url = announce.id, - repost = post, - apId = announce.id - ) - ) - return announce to createRemote - } - - private suspend fun saveIfMissing( - note: Note, - targetActor: String? - ): Pair = noteQueryService.findByApid(note.id) ?: saveNote(note, targetActor) - - private suspend fun saveNote(note: Note, targetActor: String?): Pair { - val person = apUserService.fetchPersonWithEntity( - note.attributedTo, - targetActor - ) - - val post = postRepository.findByApId(note.id) - if (post != null) { - return note to post - } - - logger.debug("VISIBILITY url: {} to: {} cc: {}", note.id, note.to, note.cc) - val visibility = visibility(note, person) - logger.debug("VISIBILITY is {} url: {}", visibility.name, note.id) - - val reply = note.inReplyTo?.let { - fetchNote(it, targetActor) - postRepository.findByUrl(it) - } - - val quote = (note.misskeyQuote ?: note.quoteUri ?: note.quoteUrl)?.let { - fetchNote(it, targetActor) - postRepository.findByUrl(it) - } - - val emojis = buildEmojis(note) - - val mediaList = note.attachment.map { - mediaService.uploadRemoteMedia( - RemoteMedia( - it.name, - it.url, - it.mediaType, - description = it.name - ) - ) - }.map { it.id } - - val createPost = - post(quote, person, note, visibility, reply, mediaList, emojis) - - val createRemote = postService.createRemote(createPost) - return note to createRemote - } - - private suspend fun post( - quote: Post?, - person: Pair, - note: Note, - visibility: Visibility, - reply: Post?, - mediaList: List, - emojis: List - ) = if (quote != null) { - postBuilder.quoteRepostOf( - id = postRepository.generateId(), - actorId = person.second.id, - content = note.content, - createdAt = Instant.parse(note.published), - visibility = visibility, - url = note.id, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id, - mediaIds = mediaList, - emojiIds = emojis, - repost = quote - ) - } else { - postBuilder.of( - id = postRepository.generateId(), - actorId = person.second.id, - content = note.content, - createdAt = Instant.parse(note.published).toEpochMilli(), - visibility = visibility, - url = note.id, - replyId = reply?.id, - sensitive = note.sensitive, - apId = note.id, - mediaIds = mediaList, - emojiIds = emojis - ) - } - - private suspend fun buildEmojis(note: Note) = note.tag - .filterIsInstance() - .map { - emojiService.fetchEmoji(it).second - } - .map { - it.id - } - - private fun visibility( - note: Note, - person: Pair - ): Visibility { - val visibility = if (note.to.contains(public)) { - Visibility.PUBLIC - } else if (note.to.contains(person.second.followers) && note.cc.contains(public)) { - Visibility.UNLISTED - } else if (note.to.contains(person.second.followers)) { - Visibility.FOLLOWERS - } else { - Visibility.DIRECT - } - return visibility - } - - override suspend fun fetchNote(note: Note, targetActor: String?): Note = - saveIfMissing(note, targetActor).first - - companion object { - const val public: String = "https://www.w3.org/ns/activitystreams#Public" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt deleted file mode 100644 index 97b1355d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.model.Note - -interface NoteApApiService { - suspend fun getNote(postId: Long, userId: Long?): Note? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt deleted file mode 100644 index df9ba955..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/NoteApApiServiceImpl.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.query.FollowerQueryService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class NoteApApiServiceImpl( - private val noteQueryService: NoteQueryService, - private val followerQueryService: FollowerQueryService, - private val transaction: Transaction -) : NoteApApiService { - override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction { - val findById = noteQueryService.findById(postId) - - if (findById == null) { - logger.warn("Note not found. $postId $userId") - return@transaction null - } - - when (findById.second.visibility) { - Visibility.PUBLIC, Visibility.UNLISTED -> { - return@transaction findById.first - } - - Visibility.FOLLOWERS -> { - return@transaction getFollowersNote(userId, findById) - } - - Visibility.DIRECT -> return@transaction null - } - } - - private suspend fun getFollowersNote( - userId: Long?, - findById: Pair - ): Note? { - if (userId == null) { - return null - } - - if (followerQueryService.alreadyFollow(findById.second.actorId, userId)) { - return findById.first - } - return null - } - - companion object { - private val logger = LoggerFactory.getLogger(NoteApApiServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt deleted file mode 100644 index 336a4de5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.objects.user - -import dev.usbharu.hideout.activitypub.domain.model.Image -import dev.usbharu.hideout.activitypub.domain.model.Key -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService -import dev.usbharu.hideout.activitypub.service.common.resolve -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.user.RemoteUserCreateDto -import dev.usbharu.hideout.core.service.user.UserService -import org.springframework.stereotype.Service - -interface APUserService { - suspend fun getPersonByName(name: String): Person - - /** - * Fetch person - * - * @param url - * @param targetActor ç½²åã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ - * @return - */ - suspend fun fetchPerson(url: String, targetActor: String? = null, idOverride: Long? = null): Person - - suspend fun fetchPersonWithEntity( - url: String, - targetActor: String? = null, - idOverride: Long? = null, - ): Pair -} - -@Service -class APUserServiceImpl( - private val userService: UserService, - private val transaction: Transaction, - private val applicationConfig: ApplicationConfig, - private val apResourceResolveService: APResourceResolveService, - private val actorRepository: ActorRepository, -) : - APUserService { - - override suspend fun getPersonByName(name: String): Person { - val userEntity = transaction.transaction { - actorRepository.findByNameAndDomain(name, applicationConfig.url.host) - ?: throw UserNotFoundException.withNameAndDomain(name, applicationConfig.url.host) - } - // TODO: JOINã§æ›¸ãç›´ã— - val userUrl = "${applicationConfig.url}/users/$name" - return Person( - type = emptyList(), - name = userEntity.name, - id = userUrl, - preferredUsername = name, - summary = userEntity.description, - inbox = "$userUrl/inbox", - outbox = "$userUrl/outbox", - url = userUrl, - icon = Image( - type = emptyList(), - mediaType = "image/jpeg", - url = "$userUrl/icon.jpg" - ), - publicKey = Key( - id = userEntity.keyId, - owner = userUrl, - publicKeyPem = userEntity.publicKey - ), - endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), - followers = userEntity.followers, - following = userEntity.following, - manuallyApprovesFollowers = userEntity.locked - ) - } - - override suspend fun fetchPerson(url: String, targetActor: String?, idOverride: Long?): Person = - fetchPersonWithEntity(url, targetActor, idOverride).first - - override suspend fun fetchPersonWithEntity( - url: String, - targetActor: String?, - idOverride: Long?, - ): Pair { - val userEntity = actorRepository.findByUrl(url) - - if (userEntity != null && idOverride == null) { - return entityToPerson(userEntity, userEntity.url) to userEntity - } - - val person = apResourceResolveService.resolve(url, null as Long?) - - val id = person.id - - val actor = actorRepository.findByUrlWithLock(id) - - if (actor != null && idOverride == null) { - return person to actor - } - - return person to userService.createRemoteUser( - RemoteUserCreateDto( - name = person.preferredUsername, - domain = id.substringAfter("://").substringBefore("/"), - screenName = person.name ?: person.preferredUsername, - description = person.summary.orEmpty(), - inbox = person.inbox, - outbox = person.outbox, - url = id, - publicKey = person.publicKey.publicKeyPem, - keyId = person.publicKey.id, - following = person.following, - followers = person.followers, - sharedInbox = person.endpoints["sharedInbox"], - locked = person.manuallyApprovesFollowers - ), - idOverride - ) - } - - private fun entityToPerson( - actorEntity: Actor, - id: String, - ) = Person( - type = emptyList(), - name = actorEntity.name, - id = id, - preferredUsername = actorEntity.name, - summary = actorEntity.description, - inbox = "$id/inbox", - outbox = "$id/outbox", - url = id, - icon = Image( - type = emptyList(), - mediaType = "image/jpeg", - url = "$id/icon.jpg" - ), - publicKey = Key( - id = actorEntity.keyId, - owner = id, - publicKeyPem = actorEntity.publicKey - ), - endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"), - followers = actorEntity.followers, - following = actorEntity.following, - manuallyApprovesFollowers = actorEntity.locked - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt deleted file mode 100644 index 32a09a76..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/service/webfinger/WebFingerApiService.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.webfinger - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import org.springframework.stereotype.Service - -@Service -interface WebFingerApiService { - suspend fun findByNameAndDomain(name: String, domain: String): Actor -} - -@Service -class WebFingerApiServiceImpl( - private val transaction: Transaction, - private val actorRepository: ActorRepository -) : - WebFingerApiService { - override suspend fun findByNameAndDomain(name: String, domain: String): Actor { - return transaction.transaction { - actorRepository.findByNameAndDomain(name, domain) ?: throw UserNotFoundException.withNameAndDomain( - name, - domain - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index e34fc87c..d51ae754 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -26,7 +26,6 @@ import com.nimbusds.jose.proc.SecurityContext import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer import dev.usbharu.hideout.activitypub.domain.model.StringOrObject import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt new file mode 100644 index 00000000..4854475d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.event.relationship + +import dev.usbharu.hideout.core.domain.model.relationship.Relationship2 +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody + +class RelationshipEventFactory(private val relationship2: Relationship2) { + fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent { + return DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship2)) + } +} + +class RelationshipEventBody(relationship: Relationship2) : DomainEventBody(mapOf("relationship" to relationship)) + +enum class RelationshipEvent(val eventName: String) { + follow("RelationshipFollow"), + unfollow("RelationshipUnfollow"), + block("RelationshipBlock"), + unblock("RelationshipUnblock"), + mute("RelationshipMute"), + unmute("RelationshipUnmute"), + followRequest("RelationshipFollowRequest"), + unfollowRequest("RelationshipUnfollowRequest"), +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt deleted file mode 100644 index c8861b42..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Acct.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.actor - -data class Acct(val username: String, val domain: String? = null, val isRemote: Boolean = domain == null) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt deleted file mode 100644 index 7e747110..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.actor - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.CharacterLimit -import jakarta.validation.Validator -import jakarta.validation.constraints.* -import org.hibernate.validator.constraints.URL -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Component -import java.time.Instant -import kotlin.math.max - -@Deprecated("Actor2を使ã†") -data class Actor private constructor( - @get:NotNull - @get:Positive - val id: Long, - @get:Pattern(regexp = "^[a-zA-Z0-9_-]{1,300}\$") - @get:Size(min = 1) - val name: String, - val domain: String, - val screenName: String, - val description: String, - @get:URL - val inbox: String, - @get:URL - val outbox: String, - @get:URL - val url: String, - @get:NotBlank - val publicKey: String, - val privateKey: String? = null, - @get:PastOrPresent - val createdAt: Instant, - @get:NotBlank - val keyId: String, - val followers: String? = null, - val following: String? = null, - @get:PositiveOrZero - val instance: Long, - val locked: Boolean, - val followersCount: Int = 0, - val followingCount: Int = 0, - val postsCount: Int = 0, - val lastPostDate: Instant? = null, - val emojis: List = emptyList(), -) { - - @Component - class UserBuilder( - private val characterLimit: CharacterLimit, - private val applicationConfig: ApplicationConfig, - private val validator: Validator, - ) { - - private val logger = LoggerFactory.getLogger(UserBuilder::class.java) - - @Suppress("LongParameterList", "FunctionMinLength", "LongMethod") - fun of( - id: Long, - name: String, - domain: String, - screenName: String, - description: String, - inbox: String, - outbox: String, - url: String, - publicKey: String, - privateKey: String? = null, - createdAt: Instant, - keyId: String, - following: String? = null, - followers: String? = null, - instance: Long, - locked: Boolean, - followersCount: Int = 0, - followingCount: Int = 0, - postsCount: Int = 0, - lastPostDate: Instant? = null, - emojis: List = emptyList(), - ): Actor { - if (id == 0L) { - return Actor( - id = id, - name = name, - domain = domain, - screenName = screenName, - description = description, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = privateKey, - createdAt = createdAt, - keyId = keyId, - followers = followers, - following = following, - instance = instance, - locked = locked, - followersCount = followersCount, - followingCount = followingCount, - postsCount = postsCount, - lastPostDate = lastPostDate, - emojis = emojis - ) - } - - // idã¯0未満ã§ã¯ã„ã‘ãªã„ - require(id >= 0) { "id must be greater than or equal to 0." } - - // nameã¯ç©ºæ–‡å­—以外をå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(name.isNotBlank()) { "name must contain non-blank characters." } - - // nameã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - val limitedName = if (name.length >= characterLimit.account.id) { - logger.warn("name must not exceed ${characterLimit.account.id} characters.") - name.substring(0, characterLimit.account.id) - } else { - name - } - - // domainã¯ç©ºæ–‡å­—以外をå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(domain.isNotBlank()) { "domain must contain non-blank characters." } - - // domainã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(domain.length <= characterLimit.general.domain) { - "domain must not exceed ${characterLimit.general.domain} characters." - } - - // screenNameã¯ç©ºæ–‡å­—以外をå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(screenName.isNotBlank()) { "screenName must contain non-blank characters." } - - // screenNameã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - val limitedScreenName = if (screenName.length >= characterLimit.account.name) { - logger.warn("screenName must not exceed ${characterLimit.account.name} characters.") - screenName.substring(0, characterLimit.account.name) - } else { - screenName - } - - // descriptionã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - val limitedDescription = if (description.length >= characterLimit.account.description) { - logger.warn("description must not exceed ${characterLimit.account.description} characters.") - description.substring(0, characterLimit.account.description) - } else { - description - } - - // ローカルユーザーã¯passwordã¨privateKeyã‚’nullã«ã—ã¦ã¯ã„ã‘ãªã„ - if (domain == applicationConfig.url.host) { - requireNotNull(privateKey) { "password and privateKey must not be null for local users." } - } - - // urlã¯ç©ºæ–‡å­—以外をå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(url.isNotBlank()) { "url must contain non-blank characters." } - - // urlã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(url.length <= characterLimit.general.url) { - "url must not exceed ${characterLimit.general.url} characters." - } - - // inboxã¯ç©ºæ–‡å­—以外をå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(inbox.isNotBlank()) { "inbox must contain non-blank characters." } - - // inboxã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(inbox.length <= characterLimit.general.url) { - "inbox must not exceed ${characterLimit.general.url} characters." - } - - // outboxã¯ç©ºæ–‡å­—以外をå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(outbox.isNotBlank()) { "outbox must contain non-blank characters." } - - // outboxã¯æŒ‡å®šã•ã‚ŒãŸé•·ã•ä»¥ä¸‹ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - require(outbox.length <= characterLimit.general.url) { - "outbox must not exceed ${characterLimit.general.url} characters." - } - - require(keyId.isNotBlank()) { - "keyId must contain non-blank characters." - } - - val actor = Actor( - id = id, - name = limitedName, - domain = domain, - screenName = limitedScreenName, - description = limitedDescription, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = privateKey, - createdAt = createdAt, - keyId = keyId, - followers = followers, - following = following, - instance = instance, - locked = locked, - followersCount = max(0, followersCount), - followingCount = max(0, followingCount), - postsCount = max(0, postsCount), - lastPostDate = lastPostDate, - emojis = emojis - ) - - val validate = validator.validate(actor) - - for (constraintViolation in validate) { - throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}") - } - return actor - } - } - - fun incrementFollowing(): Actor = this.copy(followingCount = this.followingCount + 1) - - fun decrementFollowing(): Actor = this.copy(followingCount = this.followingCount - 1) - - fun incrementFollowers(): Actor = this.copy(followersCount = this.followersCount + 1) - - fun decrementFollowers(): Actor = this.copy(followersCount = this.followersCount - 1) - - fun incrementPostsCount(): Actor = this.copy(postsCount = this.postsCount + 1) - - fun decrementPostsCount(): Actor = this.copy(postsCount = this.postsCount - 1) - - fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate) - override fun toString(): String { - return "Actor(" + - "id=$id, " + - "name='$name', " + - "domain='$domain', " + - "screenName='$screenName', " + - "description='$description', " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "publicKey='$publicKey', " + - "privateKey=$privateKey, " + - "createdAt=$createdAt, " + - "keyId='$keyId', " + - "followers=$followers, " + - "following=$following, " + - "instance=$instance, " + - "locked=$locked, " + - "followersCount=$followersCount, " + - "followingCount=$followingCount, " + - "postsCount=$postsCount, " + - "lastPostDate=$lastPostDate, " + - "emojis=$emojis" + - ")" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt deleted file mode 100644 index 0123961c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.actor - -import org.springframework.stereotype.Repository - -@Repository -@Suppress("TooManyFunctions") -interface ActorRepository { - suspend fun save(actor: Actor): Actor - - suspend fun findById(id: Long): Actor? - - suspend fun findByIdWithLock(id: Long): Actor? - - suspend fun findAll(limit: Int, offset: Long): List - - suspend fun findByName(name: String): List - - suspend fun findByNameAndDomain(name: String, domain: String): Actor? - - suspend fun findByNameAndDomainWithLock(name: String, domain: String): Actor? - - suspend fun findByUrl(url: String): Actor? - - suspend fun findByUrlWithLock(url: String): Actor? - - suspend fun findByIds(ids: List): List - - suspend fun findByKeyId(keyId: String): Actor? - - suspend fun delete(id: Long) - - suspend fun nextId(): Long -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt index f5d537c2..4e7e16d6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.emoji @JvmInline -value class EmojiId(private val emojiId: Long) \ No newline at end of file +value class EmojiId(val emojiId: Long) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt deleted file mode 100644 index 56e8f33d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/ExposedNotificationRepository.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.notification - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class ExposedNotificationRepository(private val idGenerateService: IdGenerateService) : NotificationRepository, - AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(notification: Notification): Notification = query { - val singleOrNull = - Notifications.selectAll().where { Notifications.id eq notification.id }.forUpdate().singleOrNull() - if (singleOrNull == null) { - Notifications.insert { - it[id] = notification.id - it[type] = notification.type - it[userId] = notification.userId - it[sourceActorId] = notification.sourceActorId - it[postId] = notification.postId - it[text] = notification.text - it[reactionId] = notification.reactionId - it[createdAt] = notification.createdAt - } - } else { - Notifications.update({ Notifications.id eq notification.id }) { - it[type] = notification.type - it[userId] = notification.userId - it[sourceActorId] = notification.sourceActorId - it[postId] = notification.postId - it[text] = notification.text - it[reactionId] = notification.reactionId - it[createdAt] = notification.createdAt - } - } - notification - } - - override suspend fun findById(id: Long): Notification? = query { - Notifications.selectAll().where { Notifications.id eq id }.singleOrNull()?.toNotifications() - } - - override suspend fun deleteById(id: Long) { - Notifications.deleteWhere { Notifications.id eq id } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedNotificationRepository::class.java) - } -} - -fun ResultRow.toNotifications() = Notification( - id = this[Notifications.id], - type = this[Notifications.type], - userId = this[Notifications.userId], - sourceActorId = this[Notifications.sourceActorId], - postId = this[Notifications.postId], - text = this[Notifications.text], - reactionId = this[Notifications.reactionId], - createdAt = this[Notifications.createdAt], -) - -object Notifications : Table("notifications") { - val id = long("id") - val type = varchar("type", 100) - val userId = long("user_id").references(Actors.id) - val sourceActorId = long("source_actor_id").references(Actors.id).nullable() - val postId = long("post_id").references(Posts.id).nullable() - val text = varchar("text", 3000).nullable() - val reactionId = long("reaction_id").references(Reactions.id).nullable() - val createdAt = timestamp("created_at") - - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt deleted file mode 100644 index 67dc170d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.post - -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.service.post.PostContentFormatter -import jakarta.validation.Validator -import jakarta.validation.constraints.Positive -import org.hibernate.validator.constraints.URL -import org.springframework.stereotype.Component -import java.time.Instant - -@Deprecated("Post2を使ã†") -data class Post private constructor( - @get:Positive - val id: Long, - @get:Positive - val actorId: Long, - val overview: String? = null, - val content: String, - val text: String, - @get:Positive - val createdAt: Long, - val visibility: Visibility, - @get:URL - val url: String, - val repostId: Long? = null, - val replyId: Long? = null, - val sensitive: Boolean = false, - @get:URL - val apId: String = url, - val mediaIds: List = emptyList(), - val deleted: Boolean = false, - val emojiIds: List = emptyList(), -) { - - @Component - class PostBuilder( - private val characterLimit: CharacterLimit, - private val postContentFormatter: PostContentFormatter, - private val validator: Validator, - ) { - @Suppress("FunctionMinLength", "LongParameterList") - fun of( - id: Long, - actorId: Long, - overview: String? = null, - content: String, - createdAt: Long, - visibility: Visibility, - url: String, - repostId: Long? = null, - replyId: Long? = null, - sensitive: Boolean = false, - apId: String = url, - mediaIds: List = emptyList(), - emojiIds: List = emptyList(), - deleted: Boolean = false, - ): Post { - require(id >= 0) { "id must be greater than or equal to 0." } - - require(actorId >= 0) { "actorId must be greater than or equal to 0." } - - val limitedOverview = if ((overview?.length ?: 0) >= characterLimit.post.overview) { - overview?.substring(0, characterLimit.post.overview) - } else { - overview - } - - val limitedText = if (content.length >= characterLimit.post.text) { - content.substring(0, characterLimit.post.text) - } else { - content - } - - val (html, content1) = postContentFormatter.format(limitedText) - - require(url.isNotBlank()) { "url must contain non-blank characters" } - require(url.length <= characterLimit.general.url) { - "url must not exceed ${characterLimit.general.url} characters." - } - - require((repostId ?: 0) >= 0) { "repostId must be greater then or equal to 0." } - require((replyId ?: 0) >= 0) { "replyId must be greater then or equal to 0." } - - val post = Post( - id = id, - actorId = actorId, - overview = limitedOverview, - content = html, - text = content1, - createdAt = createdAt, - visibility = visibility, - url = url, - repostId = repostId, - replyId = replyId, - sensitive = sensitive, - apId = apId, - mediaIds = mediaIds, - deleted = deleted, - emojiIds = emojiIds - ) - - val validate = validator.validate(post) - - for (constraintViolation in validate) { - throw IllegalArgumentException("${constraintViolation.propertyPath} : ${constraintViolation.message}") - } - - if (post.deleted) { - return post.delete() - } - - return post - } - - @Suppress("LongParameterList") - fun pureRepostOf( - id: Long, - actorId: Long, - visibility: Visibility, - createdAt: Instant, - url: String, - repost: Post, - apId: String, - ): Post { - // リãƒã‚¹ãƒˆã®å…¬é–‹ç¯„囲ã¯å…ƒã®ãƒã‚¹ãƒˆã‚ˆã‚Šåºƒãã¦ã¯ã„ã‘ãªã„ - val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { - repost.visibility - } else { - visibility - } - - val post = of( - id = id, - actorId = actorId, - overview = null, - content = "", - createdAt = createdAt.toEpochMilli(), - visibility = fixedVisibility, - url = url, - repostId = repost.id, - replyId = null, - sensitive = false, - apId = apId, - mediaIds = emptyList(), - deleted = false, - emojiIds = emptyList() - ) - return post - } - - @Suppress("LongParameterList") - fun quoteRepostOf( - id: Long, - actorId: Long, - overview: String? = null, - content: String, - createdAt: Instant, - visibility: Visibility, - url: String, - repost: Post, - replyId: Long? = null, - sensitive: Boolean = false, - apId: String = url, - mediaIds: List = emptyList(), - emojiIds: List = emptyList(), - ): Post { - // リãƒã‚¹ãƒˆã®å…¬é–‹ç¯„囲ã¯å…ƒã®ãƒã‚¹ãƒˆã‚ˆã‚Šåºƒãã¦ã¯ã„ã‘ãªã„ - val fixedVisibility = if (visibility.ordinal <= repost.visibility.ordinal) { - repost.visibility - } else { - visibility - } - - val post = of( - id = id, - actorId = actorId, - overview = overview, - content = content, - createdAt = createdAt.toEpochMilli(), - visibility = fixedVisibility, - url = url, - repostId = repost.id, - replyId = replyId, - sensitive = sensitive, - apId = apId, - mediaIds = mediaIds, - deleted = false, - emojiIds = emojiIds - ) - return post - } - } - - fun isPureRepost(): Boolean = - this.text.isEmpty() && - this.content.isEmpty() && - this.overview == null && - this.replyId == null && - this.repostId != null - - fun delete(): Post = copy(deleted = true) - - fun restore(): Post = copy(deleted = false) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt index f2bffd9a..26bdb9f0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt @@ -23,5 +23,5 @@ interface Post2Repository { suspend fun saveAll(posts: List): List suspend fun findById(id: PostId): Post2? suspend fun findByActorId(id: ActorId): List - suspend fun deleteById(id: PostId) + suspend fun delete(post: Post2) } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index 2ed4df88..ee9e4e08 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -22,6 +22,8 @@ class PostContent private constructor(val text: String, val content: String, val companion object { val empty = PostContent("", "", emptyList()) + val contentLength = 5000 + val textLength = 3000 } abstract class PostContentFactory { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt index 995329b1..1c2419bb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt @@ -17,4 +17,8 @@ package dev.usbharu.hideout.core.domain.model.post @JvmInline -value class PostOverview(val overview: String) +value class PostOverview(val overview: String) { + companion object { + val length = 100 + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt deleted file mode 100644 index 561911f9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.post - -import org.springframework.stereotype.Repository - -@Suppress("LongParameterList", "TooManyFunctions") -@Repository -interface PostRepository { - suspend fun generateId(): Long - suspend fun save(post: Post): Post - suspend fun saveAll(posts: List) - suspend fun delete(id: Long) - suspend fun findById(id: Long): Post? - suspend fun findByUrl(url: String): Post? - - suspend fun findByApId(apId: String): Post? - suspend fun existByApIdWithLock(apId: String): Boolean - suspend fun findByActorId(actorId: Long): List - suspend fun findByActorIdAndDeleted(actorId: Long, deleted: Boolean): List - - suspend fun countByActorId(actorId: Long): Int -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt new file mode 100644 index 00000000..5b735964 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.relationship + +import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent +import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventFactory +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable + +class Relationship2( + val actorId: ActorId, + val targetActorId: ActorId, + following: Boolean, + blocking: Boolean, + muting: Boolean, + followRequesting: Boolean, + mutingFollowRequest: Boolean, +) : DomainEventStorable() { + + var following: Boolean = following + private set + var blocking: Boolean = blocking + private set + + var muting: Boolean = muting + private set + var followRequesting: Boolean = followRequesting + private set + var mutingFollowRequest: Boolean = mutingFollowRequest + private set + + + fun follow() { + require(blocking.not()) + following = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.follow)) + } + + fun unfollow() { + following = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unfollow)) + } + + fun block() { + require(following.not()) + blocking = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.block)) + } + + fun unblock() { + blocking = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unblock)) + } + + fun mute() { + muting = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.mute)) + } + + fun unmute() { + muting = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unmute)) + } + + fun muteFollowRequest() { + mutingFollowRequest = true + } + + fun unmuteFollowRequest() { + mutingFollowRequest = false + } + + fun followRequest() { + require(blocking.not()) + followRequesting = true + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.followRequest)) + } + + fun unfollowRequest() { + followRequesting = false + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unfollowRequest)) + } + + fun acceptFollowRequest() { + follow() + followRequesting = false + } + + fun rejectFollowRequest() { + followRequesting = false + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt similarity index 73% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt index 14abb53e..c04e6106 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/HasName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.activitypub.domain.model +package dev.usbharu.hideout.core.domain.model.relationship -interface HasName { - val name: String -} +interface Relationship2Repository { + suspend fun save(relationship2: Relationship2): Relationship2 + suspend fun delete(relationship2: Relationship2) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt deleted file mode 100644 index 4780d30d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.relationship - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList - -/** - * [Relationship]ã®æ°¸ç¶šåŒ– - * - */ -interface RelationshipRepository { - /** - * 永続化ã—ã¾ã™ - * - * @param relationship 永続化ã™ã‚‹[Relationship] - * @return 永続化ã•ã‚ŒãŸ[Relationship] - */ - suspend fun save(relationship: Relationship): Relationship - - /** - * 永続化ã•ã‚ŒãŸã‚‚ã®ã‚’削除ã—ã¾ã™ - * - * @param relationship 削除ã™ã‚‹[Relationship] - */ - suspend fun delete(relationship: Relationship) - - /** - * userIdã¨targetUserIdã§[Relationship]ã‚’å–å¾—ã—ã¾ã™ - * - * @param actorId å–å¾—ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ID - * @param targetActorId 対象ユーザーID - * @return å–å¾—ã•ã‚ŒãŸ[Relationship] 存在ã—ãªã„å ´åˆnullãŒè¿”ã‚Šã¾ã™ - */ - suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? - - suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long) - - suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List - - suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int - - suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int - - @Suppress("FunctionMaxLength") - suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean, - page: Page.PageByMaxId, - ): PaginationList - - suspend fun findByActorIdAndMuting( - actorId: Long, - muting: Boolean, - page: Page.PageByMaxId, - ): PaginationList -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 481c3dd6..5e6b7886 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -20,7 +20,6 @@ import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.dao.id.LongIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt deleted file mode 100644 index a0da8d06..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventStorable.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.shared.domainevent - -abstract class DomainEventStorable { - private val domainEvents: MutableList = mutableListOf() - - protected fun addDomainEvent(domainEvent: DomainEvent) { - domainEvents.add(domainEvent) - } - - fun clearDomainEvents() = domainEvents.clear() - - fun getDomainEvents(): List = domainEvents.toList() -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt deleted file mode 100644 index c9598fb1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsEmojis -import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia -import org.jetbrains.exposed.sql.Query -import org.springframework.stereotype.Component - -@Component -class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : QueryMapper { - override fun map(query: Query): List { - return query.groupBy { it[Posts.id] } - .map { it.value } - .map { - it.first().let(postResultRowMapper::map) - .copy( - mediaIds = it.mapNotNull { resultRow -> resultRow.getOrNull(PostsMedia.mediaId) }, - emojiIds = it.mapNotNull { resultRow -> resultRow.getOrNull(PostsEmojis.emojiId) } - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt deleted file mode 100644 index b18690e3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts -import org.jetbrains.exposed.sql.ResultRow -import org.springframework.stereotype.Component - -@Component -class PostResultRowMapper(private val postBuilder: Post.PostBuilder) : ResultRowMapper { - override fun map(resultRow: ResultRow): Post { - return postBuilder.of( - id = resultRow[Posts.id], - actorId = resultRow[Posts.actorId], - overview = resultRow[Posts.overview], - content = resultRow[Posts.text], - createdAt = resultRow[Posts.createdAt], - visibility = Visibility.values().first { visibility -> visibility.ordinal == resultRow[Posts.visibility] }, - url = resultRow[Posts.url], - repostId = resultRow[Posts.repostId], - replyId = resultRow[Posts.replyId], - sensitive = resultRow[Posts.sensitive], - apId = resultRow[Posts.apId], - deleted = resultRow[Posts.deleted], - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt deleted file mode 100644 index 2c976d4c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserQueryMapper.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.actor.Actor -import org.jetbrains.exposed.sql.Query -import org.springframework.stereotype.Component - -@Component -class UserQueryMapper(private val actorResultRowMapper: ResultRowMapper) : QueryMapper { - override fun map(query: Query): List = query.map(actorResultRowMapper::map) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt deleted file mode 100644 index 2f6a02d2..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposed - -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import org.jetbrains.exposed.sql.ResultRow -import org.springframework.stereotype.Component -import java.time.Instant - -@Component -class UserResultRowMapper(private val actorBuilder: Actor.UserBuilder) : ResultRowMapper { - override fun map(resultRow: ResultRow): Actor { - return actorBuilder.of( - id = resultRow[Actors.id], - name = resultRow[Actors.name], - domain = resultRow[Actors.domain], - screenName = resultRow[Actors.screenName], - description = resultRow[Actors.description], - inbox = resultRow[Actors.inbox], - outbox = resultRow[Actors.outbox], - url = resultRow[Actors.url], - publicKey = resultRow[Actors.publicKey], - privateKey = resultRow[Actors.privateKey], - createdAt = Instant.ofEpochMilli((resultRow[Actors.createdAt])), - keyId = resultRow[Actors.keyId], - followers = resultRow[Actors.followers], - following = resultRow[Actors.following], - instance = resultRow[Actors.instance], - locked = resultRow[Actors.locked], - followingCount = resultRow[Actors.followingCount], - followersCount = resultRow[Actors.followersCount], - postsCount = resultRow[Actors.postsCount], - lastPostDate = resultRow[Actors.lastPostAt], - emojis = resultRow[Actors.emojis].split(",").filter { it.isNotEmpty() }.map { it.toLong() } - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt deleted file mode 100644 index 770ad559..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedquery - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.query.FollowerQueryService -import org.springframework.stereotype.Repository - -@Repository -class FollowerQueryServiceImpl( - private val relationshipRepository: RelationshipRepository, - private val actorRepository: ActorRepository -) : FollowerQueryService { - override suspend fun findFollowersById(id: Long): List { - return actorRepository.findByIds( - relationshipRepository.findByTargetIdAndFollowing(id, true).map { it.actorId } - ) - } - - override suspend fun alreadyFollow(actorId: Long, followerId: Long): Boolean = - relationshipRepository.findByUserIdAndTargetUserId(followerId, actorId)?.following ?: false -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt deleted file mode 100644 index 08bc5de4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ActorRepositoryImpl.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class ActorRepositoryImpl( - private val idGenerateService: IdGenerateService, - private val actorResultRowMapper: ResultRowMapper, - private val actorQueryMapper: QueryMapper -) : ActorRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun save(actor: Actor): Actor = query { - val singleOrNull = Actors.selectAll().where { Actors.id eq actor.id }.forUpdate().empty() - if (singleOrNull) { - Actors.insert { - it[id] = actor.id - it[name] = actor.name - it[domain] = actor.domain - it[screenName] = actor.screenName - it[description] = actor.description - it[inbox] = actor.inbox - it[outbox] = actor.outbox - it[url] = actor.url - it[createdAt] = actor.createdAt.toEpochMilli() - it[publicKey] = actor.publicKey - it[privateKey] = actor.privateKey - it[keyId] = actor.keyId - it[following] = actor.following - it[followers] = actor.followers - it[instance] = actor.instance - it[locked] = actor.locked - it[followersCount] = actor.followersCount - it[followingCount] = actor.followingCount - it[postsCount] = actor.postsCount - it[lastPostAt] = actor.lastPostDate - it[emojis] = actor.emojis.joinToString(",") - } - } else { - Actors.update({ Actors.id eq actor.id }) { - it[name] = actor.name - it[domain] = actor.domain - it[screenName] = actor.screenName - it[description] = actor.description - it[inbox] = actor.inbox - it[outbox] = actor.outbox - it[url] = actor.url - it[createdAt] = actor.createdAt.toEpochMilli() - it[publicKey] = actor.publicKey - it[privateKey] = actor.privateKey - it[keyId] = actor.keyId - it[following] = actor.following - it[followers] = actor.followers - it[instance] = actor.instance - it[locked] = actor.locked - it[followersCount] = actor.followersCount - it[followingCount] = actor.followingCount - it[postsCount] = actor.postsCount - it[lastPostAt] = actor.lastPostDate - it[emojis] = actor.emojis.joinToString(",") - } - } - return@query actor - } - - override suspend fun findById(id: Long): Actor? = query { - return@query Actors.selectAll().where { Actors.id eq id }.singleOrNull()?.let(actorResultRowMapper::map) - } - - override suspend fun findByIdWithLock(id: Long): Actor? = query { - return@query Actors.selectAll().where { Actors.id eq id }.forUpdate().singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findAll(limit: Int, offset: Long): List = query { - return@query Actors.selectAll().limit(limit, offset).let(actorQueryMapper::map) - } - - override suspend fun findByName(name: String): List = query { - return@query Actors.selectAll().where { Actors.name eq name }.let(actorQueryMapper::map) - } - - override suspend fun findByNameAndDomain(name: String, domain: String): Actor? = query { - return@query Actors.selectAll().where { Actors.name eq name and (Actors.domain eq domain) }.singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findByNameAndDomainWithLock(name: String, domain: String): Actor? = query { - return@query Actors.selectAll().where { Actors.name eq name and (Actors.domain eq domain) }.forUpdate() - .singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findByUrl(url: String): Actor? = query { - return@query Actors.selectAll().where { Actors.url eq url }.singleOrNull()?.let(actorResultRowMapper::map) - } - - override suspend fun findByUrlWithLock(url: String): Actor? = query { - return@query Actors.selectAll().where { Actors.url eq url }.forUpdate().singleOrNull() - ?.let(actorResultRowMapper::map) - } - - override suspend fun findByIds(ids: List): List = query { - return@query Actors.selectAll().where { Actors.id inList ids }.let(actorQueryMapper::map) - } - - override suspend fun findByKeyId(keyId: String): Actor? = query { - return@query Actors.selectAll().where { Actors.keyId eq keyId }.singleOrNull()?.let(actorResultRowMapper::map) - } - - override suspend fun delete(id: Long): Unit = query { - Actors.deleteWhere { Actors.id.eq(id) } - } - - override suspend fun nextId(): Long = idGenerateService.generateId() - - companion object { - private val logger = LoggerFactory.getLogger(ActorRepositoryImpl::class.java) - } -} - -object Actors : Table("actors") { - val id: Column = long("id") - val name: Column = varchar("name", length = 300) - val domain: Column = varchar("domain", length = 1000) - val screenName: Column = varchar("screen_name", length = 300) - val description: Column = varchar( - "description", - length = 10000 - ) - val inbox: Column = varchar("inbox", length = 1000).uniqueIndex() - val outbox: Column = varchar("outbox", length = 1000).uniqueIndex() - val url: Column = varchar("url", length = 1000).uniqueIndex() - val publicKey: Column = varchar("public_key", length = 10000) - val privateKey: Column = varchar( - "private_key", - length = 10000 - ).nullable() - val createdAt: Column = long("created_at") - val keyId = varchar("key_id", length = 1000) - val following = varchar("following", length = 1000).nullable() - val followers = varchar("followers", length = 1000).nullable() - val instance = long("instance").references(Instance.id) - val locked = bool("locked") - val followingCount = integer("following_count") - val followersCount = integer("followers_count") - val postsCount = integer("posts_count") - val lastPostAt = timestamp("last_post_at").nullable() - val emojis = varchar("emojis", 3000) - override val primaryKey: PrimaryKey = PrimaryKey(id) - - init { - uniqueIndex(name, domain) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt new file mode 100644 index 00000000..9abb2bbe --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.shared.domainevent.DomainEventPublisher +import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.actorId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.apId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.content +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.createdAt +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.deleted +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.hide +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.id +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.moveTo +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.overview +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.replyId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.repostId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.sensitive +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.text +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.url +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.visibility +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.javatime.timestamp +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class ExposedPost2Repository(override val domainEventPublisher: DomainEventPublisher) : Post2Repository, + AbstractRepository(), DomainEventPublishableRepository { + override suspend fun save(post: Post2): Post2 { + query { + Posts2.upsert { + it[id] = post.id.id + it[actorId] = post.actorId.id + it[overview] = post.overview?.overview + it[content] = post.content.content + it[text] = post.content.text + it[createdAt] = post.createdAt + it[visibility] = post.visibility.name + it[url] = post.url.toString() + it[repostId] = post.repostId?.id + it[replyId] = post.replyId?.id + it[sensitive] = post.sensitive + it[apId] = post.apId.toString() + it[deleted] = post.deleted + it[hide] = post.hide + it[moveTo] = post.moveTo?.id + } + PostsMedia.deleteWhere { + postId eq post.id.id + } + PostsEmojis.deleteWhere { + postId eq post.id.id + } + PostsMedia.batchInsert(post.mediaIds) { + this[PostsMedia.postId] = post.id.id + this[PostsMedia.mediaId] = it.id + } + PostsEmojis.batchInsert(post.emojiIds) { + this[PostsEmojis.postId] = post.id.id + this[PostsEmojis.emojiId] = it.emojiId + } + } + update(post) + return post + } + + override suspend fun saveAll(posts: List): List { + query { + Posts2.batchUpsert(posts, id) { + this[id] = it.id.id + this[actorId] = it.actorId.id + this[overview] = it.overview?.overview + this[content] = it.content.content + this[text] = it.content.text + this[createdAt] = it.createdAt + this[visibility] = it.visibility.name + this[url] = it.url.toString() + this[repostId] = it.repostId?.id + this[replyId] = it.replyId?.id + this[sensitive] = it.sensitive + this[apId] = it.apId.toString() + this[deleted] = it.deleted + this[hide] = it.hide + this[moveTo] = it.moveTo?.id + } + val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id.id to it.id } } + PostsMedia.batchUpsert(mediaIds, PostsMedia.postId) { + this[PostsMedia.postId] = it.first + this[PostsMedia.mediaId] = it.second + } + val emojiIds = posts.flatMap { post -> post.emojiIds.map { post.id.id to it.emojiId } } + PostsEmojis.batchUpsert(emojiIds, PostsEmojis.postId) { + this[PostsEmojis.postId] = it.first + this[PostsEmojis.emojiId] = it.second + } + } + posts.forEach { + update(it) + } + return posts + } + + override suspend fun findById(id: PostId): Post2? { + TODO("Not yet implemented") + } + + override suspend fun findByActorId(id: ActorId): List { + TODO("Not yet implemented") + } + + override suspend fun delete(post: Post2) { + query { + Posts2.deleteWhere { + id eq post.id.id + } + } + update(post) + } + + override val logger: Logger = Companion.logger + + companion object { + private val logger = LoggerFactory.getLogger(ExposedPost2Repository::class.java) + } +} + +object Posts2 : Table("posts") { + val id = long("id") + val actorId = long("actor_id").references(Actors2.id) + val overview = varchar("overview", PostOverview.length).nullable() + val content = varchar("content", PostContent.contentLength) + val text = varchar("text", PostContent.textLength) + val createdAt = timestamp("created_at") + val visibility = varchar("visibility", 100) + val url = varchar("url", 1000) + val repostId = long("repost_id").references(id).nullable() + val replyId = long("reply_id").references(id).nullable() + val sensitive = bool("sensitive") + val apId = varchar("ap_id", 1000) + val deleted = bool("deleted") + val hide = bool("hide") + val moveTo = long("move_to").references(id).nullable() + +} + +object PostsMedia : Table("posts_media") { + val postId = long("post_id").references(id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + val mediaId = long("media_id").references(Media.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + override val primaryKey = PrimaryKey(postId, mediaId) +} + +object PostsEmojis : Table("posts_emojis") { + val postId = long("post_id").references(id) + val emojiId = long("emoji_id").references(CustomEmojis.id) + override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt deleted file mode 100644 index 96180244..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/PostRepositoryImpl.kt +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.actorId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.apId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.content -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.createdAt -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.deleted -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.id -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.overview -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.replyId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.repostId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.sensitive -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.text -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.url -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.visibility -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 PostRepositoryImpl( - private val idGenerateService: IdGenerateService, - private val postQueryMapper: QueryMapper, -) : PostRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(post: Post): Post = query { - val singleOrNull = Posts.selectAll().where { id eq post.id }.forUpdate().singleOrNull() - if (singleOrNull == null) { - Posts.insert { - it[id] = post.id - it[actorId] = post.actorId - it[overview] = post.overview - it[content] = post.content - it[text] = post.text - it[createdAt] = post.createdAt - it[visibility] = post.visibility.ordinal - it[url] = post.url - it[repostId] = post.repostId - it[replyId] = post.replyId - it[sensitive] = post.sensitive - it[apId] = post.apId - it[deleted] = post.deleted - } - PostsMedia.batchInsert(post.mediaIds) { - this[PostsMedia.postId] = post.id - this[PostsMedia.mediaId] = it - } - PostsEmojis.batchInsert(post.emojiIds) { - this[PostsEmojis.postId] = post.id - this[PostsEmojis.emojiId] = it - } - } else { - PostsMedia.deleteWhere { - postId eq post.id - } - PostsEmojis.deleteWhere { - postId eq post.id - } - PostsMedia.batchInsert(post.mediaIds) { - this[PostsMedia.postId] = post.id - this[PostsMedia.mediaId] = it - } - PostsEmojis.batchInsert(post.emojiIds) { - this[PostsEmojis.postId] = post.id - this[PostsEmojis.emojiId] = it - } - Posts.update({ id eq post.id }) { - it[actorId] = post.actorId - it[overview] = post.overview - it[content] = post.content - it[text] = post.text - it[createdAt] = post.createdAt - it[visibility] = post.visibility.ordinal - it[url] = post.url - it[repostId] = post.repostId - it[replyId] = post.replyId - it[sensitive] = post.sensitive - it[apId] = post.apId - it[deleted] = post.deleted - } - } - return@query post - } - - override suspend fun saveAll(posts: List) { - Posts.batchUpsert( - posts, - id, - ) { - this[id] = it.id - this[actorId] = it.actorId - this[overview] = it.overview - this[content] = it.content - this[text] = it.text - this[createdAt] = it.createdAt - this[visibility] = it.visibility.ordinal - this[url] = it.url - this[repostId] = it.repostId - this[replyId] = it.replyId - this[sensitive] = it.sensitive - this[apId] = it.apId - this[deleted] = it.deleted - } - val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id to it } } - PostsMedia.batchUpsert( - mediaIds, - PostsMedia.postId - ) { - this[PostsMedia.postId] = it.first - this[PostsMedia.mediaId] = it.second - } - - val emojiIds = posts.flatMap { post -> post.emojiIds.map { post.id to it } } - PostsEmojis.batchUpsert(emojiIds, PostsEmojis.postId) { - this[PostsEmojis.postId] = it.first - this[PostsEmojis.emojiId] = it.second - } - } - - override suspend fun findById(id: Long): Post? = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.id eq id } - .let(postQueryMapper::map) - .singleOrNull() - } - - override suspend fun findByUrl(url: String): Post? = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.url eq url } - .let(postQueryMapper::map) - .singleOrNull() - } - - override suspend fun findByApId(apId: String): Post? = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.apId eq apId } - .let(postQueryMapper::map) - .singleOrNull() - } - - override suspend fun existByApIdWithLock(apId: String): Boolean = query { - return@query Posts.selectAll().where { Posts.apId eq apId }.forUpdate().empty().not() - } - - override suspend fun findByActorId(actorId: Long): List = query { - return@query Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .selectAll().where { Posts.actorId eq actorId }.let(postQueryMapper::map) - } - - override suspend fun findByActorIdAndDeleted(actorId: Long, deleted: Boolean): List { - TODO("Not yet implemented") - } - - override suspend fun countByActorId(actorId: Long): Int = query { - return@query Posts - .selectAll() - .where { Posts.actorId eq actorId } - .count() - .toInt() - } - - override suspend fun delete(id: Long): Unit = query { - Posts.deleteWhere { Posts.id eq id } - } - - companion object { - private val logger = LoggerFactory.getLogger(PostRepositoryImpl::class.java) - } -} - -object Posts : Table() { - val id: Column = long("id") - val actorId: Column = long("actor_id").references(Actors.id) - val overview: Column = varchar("overview", 100).nullable() - val content = varchar("content", 5000) - val text: Column = varchar("text", 3000) - val createdAt: Column = long("created_at") - val visibility: Column = integer("visibility").default(0) - val url: Column = varchar("url", 500) - val repostId: Column = long("repost_id").references(id).nullable() - val replyId: Column = long("reply_id").references(id).nullable() - val sensitive: Column = bool("sensitive").default(false) - val apId: Column = varchar("ap_id", 100).uniqueIndex() - val deleted = bool("deleted").default(false) - override val primaryKey: PrimaryKey = PrimaryKey(id) -} - -object PostsMedia : Table("posts_media") { - val postId = long("post_id").references(id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) - val mediaId = long("media_id").references(Media.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) - override val primaryKey = PrimaryKey(postId, mediaId) -} - -object PostsEmojis : Table("posts_emojis") { - val postId = long("post_id").references(id) - val emojiId = long("emoji_id").references(CustomEmojis.id) - override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index 6ec16ddd..578da4ad 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt index 4a91c24a..6481007b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import kotlinx.coroutines.runBlocking import org.springframework.security.core.userdetails.UserDetails diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 5951928c..bd61adac 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.core.interfaces.api.auth import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CaptchaConfig -import dev.usbharu.hideout.core.service.auth.AuthApiService import dev.usbharu.hideout.core.service.auth.RegisterAccountDto import org.springframework.stereotype.Controller import org.springframework.ui.Model diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt deleted file mode 100644 index 64b2f8c2..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/FollowerQueryService.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.query - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -@Deprecated("Use RelationshipQueryService") -interface FollowerQueryService { - suspend fun findFollowersById(id: Long): List - suspend fun alreadyFollow(actorId: Long, followerId: Long): Boolean -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt deleted file mode 100644 index 130ef8a8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.auth - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -interface AuthApiService { - suspend fun registerAccount(registerAccountDto: RegisterAccountDto): Actor -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt deleted file mode 100644 index b1a2c6ed..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/AuthApiServiceImpl.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.auth - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.CaptchaConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.service.user.UserCreateDto -import dev.usbharu.hideout.core.service.user.UserService -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class AuthApiServiceImpl( - private val httpClient: HttpClient, - private val captchaConfig: CaptchaConfig, - private val objectMapper: ObjectMapper, - private val userService: UserService, -) : - AuthApiService { - override suspend fun registerAccount(registerAccountDto: RegisterAccountDto): Actor { - if (captchaConfig.reCaptchaSecretKey != null && captchaConfig.reCaptchaSiteKey != null) { - val get = - httpClient.get( - "https://www.google.com/recaptcha/api/siteverify?secret=" + - captchaConfig.reCaptchaSecretKey + "&response=" + registerAccountDto.recaptchaResponse - ) - val recaptchaResult = objectMapper.readValue(get.bodyAsText()) - logger.debug("reCAPTCHA: {}", recaptchaResult) - require(recaptchaResult.success) - require(!(recaptchaResult.score < 0.5)) - } - - val createLocalUser = userService.createLocalUser( - UserCreateDto( - registerAccountDto.username, - registerAccountDto.username, - "", - registerAccountDto.password - ) - ) - - return createLocalUser - } - - companion object { - private val logger = LoggerFactory.getLogger(AuthApiServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt deleted file mode 100644 index 8bba0722..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessService.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.query.model.FilterQueryModel - -interface MuteProcessService { - suspend fun processMute(post: Post, context: List, filters: List): FilterResult? - suspend fun processMutes( - posts: List, - context: List, - filters: List - ): Map -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt deleted file mode 100644 index 491b3985..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImpl.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterMode.* -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class MuteProcessServiceImpl : MuteProcessService { - override suspend fun processMute( - post: Post, - context: List, - filters: List - ): FilterResult? { - val preprocess = preprocess(context, filters) - - return processMute(post, preprocess) - } - - private suspend fun processMute( - post: Post, - preprocess: List - ): FilterResult? { - logger.trace("process mute post: {}", post) - if (post.overview != null) { - val processMute = processMute(post.overview, preprocess) - - if (processMute != null) { - return processMute - } - } - - val processMute = processMute(post.text, preprocess) - - if (processMute != null) { - return processMute - } - - return null - } - - override suspend fun processMutes( - posts: List, - context: List, - filters: List - ): Map { - val preprocess = preprocess(context, filters) - - return posts.mapNotNull { it to (processMute(it, preprocess) ?: return@mapNotNull null) }.toMap() - } - - private suspend fun processMute(string: String, filters: List): FilterResult? { - for (filter in filters) { - val matchEntire = filter.regex.find(string) - - if (matchEntire != null) { - return FilterResult(filter.filter, matchEntire.value) - } - } - - return null - } - - private fun preprocess(context: List, filters: List): List { - val filterQueryModelList = filters - .filter { it.context.any(context::contains) } - .map { - PreProcessedFilter( - it, - precompileRegex(it) - ) - } - - return filterQueryModelList - } - - private fun precompileRegex(filter: FilterQueryModel): Regex { - logger.trace("precompile regex. filter: {}", filter) - - val regexList = mutableListOf() - - val noneRegexStrings = mutableListOf() - val wholeRegexStrings = mutableListOf() - - for (keyword in filter.keywords) { - when (keyword.mode) { - WHOLE_WORD -> wholeRegexStrings.add(keyword.keyword) - REGEX -> regexList.add(Regex(keyword.keyword)) - NONE -> noneRegexStrings.add(keyword.keyword) - } - } - - val noneRegex = noneRegexStrings.joinToString("|", "(", ")") - val wholeRegex = wholeRegexStrings.joinToString("|", "\\b(", ")\\b") - - val regex = if (noneRegexStrings.isNotEmpty() && wholeRegexStrings.isNotEmpty()) { - Regex("$noneRegex|$wholeRegex") - } else if (noneRegexStrings.isNotEmpty()) { - noneRegex.toRegex() - } else if (wholeRegexStrings.isNotEmpty()) { - wholeRegex.toRegex() - } else { - null - } - - if (regex != null) { - regexList.add(regex) - } - - val pattern = regexList.joinToString(")|(", "(", ")") - logger.trace("precompiled regex {}", pattern) - - return Regex(pattern) - } - - data class PreProcessedFilter(val filter: FilterQueryModel, val regex: Regex) - - companion object { - private val logger = LoggerFactory.getLogger(MuteProcessServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt deleted file mode 100644 index 2e85a805..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/follow/SendFollowDto.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.follow - -import dev.usbharu.hideout.core.domain.model.actor.Actor - -data class SendFollowDto(val actorId: Actor, val followTargetActorId: Actor) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt index 37cc9fb4..189ac6fc 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt @@ -17,12 +17,9 @@ package dev.usbharu.hideout.core.service.notification import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.notification.Notification import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.Instant diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt deleted file mode 100644 index 3f03f549..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationStore.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.notification.Notification -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.reaction.Reaction - -interface NotificationStore { - suspend fun publishNotification( - notification: Notification, - user: Actor, - sourceActor: Actor?, - post: Post?, - reaction: Reaction? - ): Boolean - - suspend fun unpulishNotification(notificationId: Long): Boolean -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt deleted file mode 100644 index 1a177666..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostService.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.core.domain.model.post.Post -import org.springframework.stereotype.Service - -@Service -interface PostService { - suspend fun createLocal(post: PostCreateDto): Post - suspend fun createRemote(post: Post): Post - suspend fun deleteLocal(post: Post) - suspend fun deleteRemote(post: Post) - suspend fun deleteByActor(actorId: Long) - suspend fun restoreByRemoteActor(actorId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt deleted file mode 100644 index d1d7a136..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImpl.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.activitypub.service.activity.create.ApSendCreateService -import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.exception.resource.PostNotFoundException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.service.timeline.TimelineService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class PostServiceImpl( - private val postRepository: PostRepository, - private val actorRepository: ActorRepository, - private val timelineService: TimelineService, - private val postBuilder: Post.PostBuilder, - private val apSendCreateService: ApSendCreateService, - private val reactionRepository: ReactionRepository, - private val apSendDeleteService: APSendDeleteService, -) : PostService { - - override suspend fun createLocal(post: PostCreateDto): Post { - logger.info("START Create Local Post user: {}, media: {}", post.userId, post.mediaIds.size) - val create = internalCreate(post, true) - apSendCreateService.createNote(create) - logger.info("SUCCESS Create Local Post url: {}", create.url) - return create - } - - override suspend fun createRemote(post: Post): Post { - logger.info("START Create Remote Post user: {}, remote url: {}", post.actorId, post.apId) - val actor = - actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) - val createdPost = internalCreate(post, false) - logger.info("SUCCESS Create Remote Post url: {}", createdPost.url) - return createdPost - } - - override suspend fun deleteLocal(post: Post) { - if (post.deleted) { - return - } - reactionRepository.deleteByPostId(post.id) - postRepository.save(post.delete()) - val actor = actorRepository.findById(post.actorId) - ?: throw IllegalStateException("actor: ${post.actorId} was not found.") - - apSendDeleteService.sendDeleteNote(post) - - actorRepository.save(actor.decrementPostsCount()) - } - - override suspend fun deleteRemote(post: Post) { - if (post.deleted) { - return - } - reactionRepository.deleteByPostId(post.id) - postRepository.save(post.delete()) - - val actor = actorRepository.findById(post.actorId) - ?: throw IllegalStateException("actor: ${post.actorId} was not found.") - - actorRepository.save(actor.decrementPostsCount()) - } - - override suspend fun deleteByActor(actorId: Long) { - val actor = actorRepository.findById(actorId) - ?: throw IllegalStateException("actor: $actorId was not found.") - - postRepository.findByActorId(actorId).filterNot { it.deleted }.forEach { postRepository.save(it.delete()) } - - actorRepository.save(actor.copy(postsCount = 0, lastPostDate = null)) - } - - override suspend fun restoreByRemoteActor(actorId: Long) { - val actor = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - - val postList = postRepository.findByActorIdAndDeleted(actorId, true).map { it.restore() } - - postRepository.saveAll(postList) - - actorRepository.save(actor.copy(postsCount = actor.postsCount.plus(postList.size))) - } - - private suspend fun internalCreate(post: Post, isLocal: Boolean): Post { - return try { - val save = postRepository.save(post) - timelineService.publishTimeline(post, isLocal) - save - } catch (_: DuplicateException) { - postRepository.findByApId(post.apId) ?: throw PostNotFoundException.withApId(post.apId) - } - } - - private suspend fun internalCreate(post: PostCreateDto, isLocal: Boolean): Post { - val user = actorRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found") - val id = postRepository.generateId() - val createPost = postBuilder.of( - id = id, - actorId = post.userId, - overview = post.overview, - content = post.text, - createdAt = Instant.now().toEpochMilli(), - visibility = post.visibility, - url = "${user.url}/posts/$id", - mediaIds = post.mediaIds, - replyId = post.repolyId, - repostId = post.repostId, - ) - return internalCreate(createPost, isLocal) - } - - companion object { - private val logger = LoggerFactory.getLogger(PostServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt index a2ccd6aa..26fe8d61 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.service.reaction import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException import dev.usbharu.hideout.core.domain.model.emoji.Emoji -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.service.notification.NotificationService diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt deleted file mode 100644 index 513ade8c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipService.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.relationship - -interface RelationshipService { - suspend fun followRequest(actorId: Long, targetId: Long) - suspend fun block(actorId: Long, targetId: Long) - - /** - * フォローリクエストを承èªã—ã¾ã™ - * [actorId]ãŒ[targetId]ã‹ã‚‰ã®ãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’承èªã—ã¾ã™ - * - * @param actorId 承èªæ“作をã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ - * @param targetId 承èªã™ã‚‹ãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’é€ã£ã¦ããŸãƒ¦ãƒ¼ã‚¶ãƒ¼ - * @param force 強制的ã«Acceptアクティビティを発行ã™ã‚‹ - */ - suspend fun acceptFollowRequest(actorId: Long, targetId: Long, force: Boolean = false) - suspend fun rejectFollowRequest(actorId: Long, targetId: Long) - suspend fun ignoreFollowRequest(actorId: Long, targetId: Long) - suspend fun unfollow(actorId: Long, targetId: Long) - suspend fun unblock(actorId: Long, targetId: Long) - suspend fun mute(actorId: Long, targetId: Long) - suspend fun unmute(actorId: Long, targetId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt deleted file mode 100644 index 057a5224..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImpl.kt +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.relationship - -import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService -import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService -import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService -import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.relationship.Relationship -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.follow.SendFollowDto -import dev.usbharu.hideout.core.service.notification.FollowNotificationRequest -import dev.usbharu.hideout.core.service.notification.FollowRequestNotificationRequest -import dev.usbharu.hideout.core.service.notification.NotificationService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class RelationshipServiceImpl( - private val applicationConfig: ApplicationConfig, - private val relationshipRepository: RelationshipRepository, - private val apSendFollowService: APSendFollowService, - private val apSendAcceptService: ApSendAcceptService, - private val apSendRejectService: ApSendRejectService, - private val apSendUndoService: APSendUndoService, - private val actorRepository: ActorRepository, - private val notificationService: NotificationService, -) : RelationshipService { - override suspend fun followRequest(actorId: Long, targetId: Long) { - logger.info("START Follow Request userId: {} targetId: {}", actorId, targetId) - - val relationship = - relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)?.copy(followRequest = true) - ?: Relationship( - actorId = actorId, - targetActorId = targetId, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) ?: Relationship( - actorId = targetId, - targetActorId = actorId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - if (inverseRelationship.blocking) { - logger.debug("FAILED Blocked by target. userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.blocking) { - logger.debug("FAILED Blocking user. userId: {} targetId: {}", actorId, targetId) - return - } - if (relationship.ignoreFollowRequestToTarget) { - logger.debug("SUCCESS Ignore Follow Request. userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.following) { - logger.debug("SUCCESS User already follow. userId: {} targetId: {}", actorId, targetId) - acceptFollowRequest(targetId, actorId, true) - return - } - - relationshipRepository.save(relationship) - - val remoteUser = isRemoteUser(targetId) - - if (remoteUser != null) { - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - apSendFollowService.sendFollow(SendFollowDto(user, remoteUser)) - } else { - val target = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - if (target.locked.not()) { - acceptFollowRequest(targetId, actorId) - } else { - notificationService.publishNotify(FollowRequestNotificationRequest(targetId, actorId)) - } - } - - logger.info("SUCCESS Follow Request userId: {} targetId: {}", actorId, targetId) - } - - override suspend fun block(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - val targetActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - if (relationship?.following == true) { - actorRepository.save(user.decrementFollowing()) - actorRepository.save(targetActor.decrementFollowers()) - } - - val blockedRelationship = relationship - ?.copy(blocking = true, followRequest = false, following = false) ?: Relationship( - actorId = actorId, - targetActorId = targetId, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - - if (inverseRelationship?.following == true) { - actorRepository.save(targetActor.decrementFollowing()) - actorRepository.save(user.decrementFollowers()) - } - - val blockedInverseRelationship = inverseRelationship - ?.copy(followRequest = false, following = false) - - relationshipRepository.save(blockedRelationship) - if (blockedInverseRelationship != null) { - relationshipRepository.save(blockedInverseRelationship) - } - } - - override suspend fun acceptFollowRequest(actorId: Long, targetId: Long, force: Boolean) { - logger.info("START Accept follow request userId: {} targetId: {}", actorId, targetId) - - val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) ?: Relationship( - actorId = targetId, - targetActorId = actorId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - if (relationship == null) { - logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.followRequest.not() && force.not()) { - logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.blocking) { - logger.warn("FAILED Blocking user userId: {} targetId: {}", actorId, targetId) - throw IllegalStateException( - "Cannot accept a follow request from a blocked user. userId: $actorId targetId: $targetId" - ) - } - - if (inverseRelationship.blocking) { - logger.warn("FAILED BLocked by user userId: {} targetId: {}", actorId, targetId) - throw IllegalStateException( - "Cannot accept a follow request from a blocking user. userId: $actorId targetId: $targetId" - ) - } - - val copy = relationship.copy(followRequest = false, following = true, blocking = false) - - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - - actorRepository.save(user.incrementFollowers()) - - relationshipRepository.save(copy) - - val remoteActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - - actorRepository.save(remoteActor.incrementFollowing()) - - if (isRemoteActor(remoteActor)) { - apSendAcceptService.sendAcceptFollow(user, remoteActor) - } - notificationService.publishNotify(FollowNotificationRequest(actorId, targetId)) - } - - override suspend fun rejectFollowRequest(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - - if (relationship == null) { - logger.warn("FAILED Follow Request Not Found. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.followRequest.not() && relationship.following.not()) { - logger.warn("FAILED Follow Request Not Found. (Follow Request) userId: {} targetId: {}", actorId, targetId) - return - } - - val copy = relationship.copy(followRequest = false, following = false) - - relationshipRepository.save(copy) - - val remoteUser = isRemoteUser(targetId) - - if (remoteUser != null) { - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - apSendRejectService.sendRejectFollow(user, remoteUser) - } - } - - override suspend fun ignoreFollowRequest(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, actorId) - ?.copy(ignoreFollowRequestToTarget = true) - ?: Relationship( - actorId = targetId, - targetActorId = actorId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = true - ) - - relationshipRepository.save(relationship) - } - - override suspend fun unfollow(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - - if (relationship == null) { - logger.warn("FAILED Unfollow. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - val user = actorRepository.findById(actorId) ?: throw UserNotFoundException.withId(actorId) - val targetActor = actorRepository.findById(targetId) ?: throw UserNotFoundException.withId(targetId) - - if (relationship.following) { - actorRepository.save(user.decrementFollowing()) - actorRepository.save(targetActor.decrementFollowers()) - } - - if (relationship.following.not()) { - logger.warn("SUCCESS User already unfollow. userId: {} targetId: {}", actorId, targetId) - return - } - - val copy = relationship.copy(following = false) - - relationshipRepository.save(copy) - - val remoteUser = isRemoteUser(targetId) - - if (remoteUser != null) { - apSendUndoService.sendUndoFollow(user, remoteUser) - } - } - - override suspend fun unblock(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId) - - if (relationship == null) { - logger.warn("FAILED Unblock. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - if (relationship.blocking.not()) { - logger.warn("SUCCESS User is not blocking. userId: {] targetId: {}", actorId, targetId) - return - } - - val copy = relationship.copy(blocking = false) - relationshipRepository.save(copy) - } - - override suspend fun mute(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)?.copy(muting = true) - ?: Relationship( - actorId = actorId, - targetActorId = targetId, - following = false, - blocking = false, - muting = true, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - relationshipRepository.save(relationship) - } - - override suspend fun unmute(actorId: Long, targetId: Long) { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(actorId, targetId)?.copy(muting = false) - - if (relationship == null) { - logger.warn("FAILED Mute. (Relationship) userId: {} targetId: {}", actorId, targetId) - return - } - - relationshipRepository.save(relationship) - } - - private fun isRemoteActor(actor: Actor): Boolean = actor.domain != applicationConfig.url.host - - private suspend fun isRemoteUser(userId: Long): Actor? { - logger.trace("isRemoteUser({})", userId) - val user = - actorRepository.findById(userId) ?: throw UserNotFoundException.withId(userId) - - logger.trace("user info {}", user) - - if (user.domain == applicationConfig.url.host) { - logger.trace("user: {} is local user", userId) - return null - } - logger.trace("user: {} is remote user", userId) - return user - } - - companion object { - private val logger = LoggerFactory.getLogger(RelationshipServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt deleted file mode 100644 index 1bdec5c0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineService.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.timeline - -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -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.TimelineRepository -import dev.usbharu.hideout.core.query.FollowerQueryService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class TimelineService( - private val followerQueryService: FollowerQueryService, - private val timelineRepository: TimelineRepository, - private val actorRepository: ActorRepository -) { - suspend fun publishTimeline(post: Post, isLocal: Boolean) { - val findFollowersById = followerQueryService.findFollowersById(post.actorId).toMutableList() - if (isLocal) { - // 自分自身もå«ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ - val user = actorRepository.findById(post.actorId) ?: throw UserNotFoundException.withId(post.actorId) - findFollowersById.add(user) - } - val timelines = findFollowersById.map { - Timeline( - id = timelineRepository.generateId(), - userId = it.id, - timelineId = 0, - postId = post.id, - postActorId = post.actorId, - createdAt = post.createdAt, - replyId = post.replyId, - repostId = post.repostId, - visibility = post.visibility, - sensitive = post.sensitive, - isLocal = isLocal, - isPureRepost = post.repostId == null || (post.text.isBlank() && post.overview.isNullOrBlank()), - mediaIds = post.mediaIds, - emojiIds = post.emojiIds - ) - }.toMutableList() - if (post.visibility == Visibility.PUBLIC) { - timelines.add( - Timeline( - id = timelineRepository.generateId(), - userId = 0, - timelineId = 0, - postId = post.id, - postActorId = post.actorId, - createdAt = post.createdAt, - replyId = post.replyId, - repostId = post.repostId, - visibility = post.visibility, - sensitive = post.sensitive, - isLocal = isLocal, - isPureRepost = post.repostId == null || (post.text.isBlank() && post.overview.isNullOrBlank()), - mediaIds = post.mediaIds, - emojiIds = post.emojiIds - ) - ) - } - timelineRepository.saveAll(timelines) - logger.debug("SUCCESS Timeline published. {}", timelines.size) - } - - companion object { - private val logger = LoggerFactory.getLogger(TimelineService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt index ad372b37..77c90ace 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.stereotype.Service import java.security.* diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt deleted file mode 100644 index f1d42b42..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import org.springframework.stereotype.Service - -@Service -interface UserService { - - suspend fun usernameAlreadyUse(username: String): Boolean - - suspend fun createLocalUser(user: UserCreateDto): Actor - - suspend fun createRemoteUser(user: RemoteUserCreateDto, idOverride: Long? = null): Actor - - suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) - - suspend fun deleteRemoteActor(actorId: Long) - - suspend fun restorationRemoteActor(actorId: Long) - - suspend fun deleteLocalUser(userId: Long) - - suspend fun updateUserStatistics(userId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt deleted file mode 100644 index fbc80538..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import dev.usbharu.hideout.core.external.job.UpdateActorTask -import dev.usbharu.hideout.core.service.instance.InstanceService -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.owl.producer.api.OwlProducer -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -@Suppress("LongParameterList") -class UserServiceImpl( - private val actorRepository: ActorRepository, - private val userAuthService: UserAuthService, - private val actorBuilder: Actor.UserBuilder, - private val applicationConfig: ApplicationConfig, - private val instanceService: InstanceService, - private val userDetailRepository: UserDetailRepository, - private val deletedActorRepository: DeletedActorRepository, - private val reactionRepository: ReactionRepository, - private val relationshipRepository: RelationshipRepository, - private val postService: PostService, - private val apSendDeleteService: APSendDeleteService, - private val postRepository: PostRepository, - private val owlProducer: OwlProducer, -) : - UserService { - - override suspend fun usernameAlreadyUse(username: String): Boolean { - val findByNameAndDomain = actorRepository.findByNameAndDomain(username, applicationConfig.url.host) - return findByNameAndDomain != null - } - - override suspend fun createLocalUser(user: UserCreateDto): Actor { - if (applicationConfig.private) { - throw IllegalStateException("Instance is a private mode.") - } - - val nextId = actorRepository.nextId() - val hashedPassword = userAuthService.hash(user.password) - val keyPair = userAuthService.generateKeyPair() - val userUrl = "${applicationConfig.url}/users/${user.name}" - val userEntity = actorBuilder.of( - id = nextId, - name = user.name, - domain = applicationConfig.url.host, - screenName = user.screenName, - description = user.description, - inbox = "$userUrl/inbox", - outbox = "$userUrl/outbox", - url = userUrl, - publicKey = keyPair.public.toPem(), - privateKey = keyPair.private.toPem(), - createdAt = Instant.now(), - following = "$userUrl/following", - followers = "$userUrl/followers", - keyId = "$userUrl#pubkey", - locked = false, - instance = 0 - ) - val save = actorRepository.save(userEntity) - userDetailRepository.save(UserDetail(nextId, hashedPassword, true)) - return save - } - - override suspend fun createRemoteUser(user: RemoteUserCreateDto, idOverride: Long?): Actor { - logger.info("START Create New remote user. name: {} url: {}", user.name, user.url) - - val deletedActor = deletedActorRepository.findByNameAndDomain(user.name, user.domain) - - if (deletedActor != null) { - logger.warn("FAILED Deleted actor. user: ${user.name} domain: ${user.domain}") - throw IllegalStateException("Cannot create Deleted actor.") - } - - val instance = instanceService.fetchInstance(user.url, user.sharedInbox) - - val nextId = actorRepository.nextId() - val userEntity = actorBuilder.of( - id = idOverride ?: nextId, - name = user.name, - domain = user.domain, - screenName = user.screenName, - description = user.description, - inbox = user.inbox, - outbox = user.outbox, - url = user.url, - publicKey = user.publicKey, - createdAt = Instant.now(), - followers = user.followers, - following = user.following, - keyId = user.keyId, - instance = instance.id, - locked = user.locked ?: false - ) - return try { - val save = actorRepository.save(userEntity) - logger.warn("SUCCESS Create New remote user. id: {} name: {} url: {}", userEntity.id, user.name, user.url) - save - } catch (_: DuplicateException) { - actorRepository.findByUrl(user.url)!! - } - } - - override suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) { - val userDetail = userDetailRepository.findByActorId(userId) - ?: throw IllegalArgumentException("userId: $userId was not found.") - - val actor = actorRepository.findById(userId) ?: throw IllegalArgumentException("userId $userId was not found.") - - actorRepository.save( - actor.copy( - screenName = updateUserDto.screenName, - description = updateUserDto.description, - locked = updateUserDto.locked - ) - ) - - userDetailRepository.save( - userDetail.copy( - autoAcceptFolloweeFollowRequest = updateUserDto.autoAcceptFolloweeFollowRequest - ) - ) - } - - override suspend fun deleteRemoteActor(actorId: Long) { - val actor = actorRepository.findByIdWithLock(actorId) ?: throw UserNotFoundException.withId(actorId) - val deletedActor = DeletedActor( - id = actor.id, - name = actor.name, - domain = actor.domain, - apId = actor.url, - publicKey = actor.publicKey, - deletedAt = Instant.now() - ) - relationshipRepository.deleteByActorIdOrTargetActorId(actorId, actorId) - - reactionRepository.deleteByActorId(actorId) - - postService.deleteByActor(actorId) - - actorRepository.delete(actor.id) - deletedActorRepository.save(deletedActor) - } - - override suspend fun restorationRemoteActor(actorId: Long) { - val deletedActor = deletedActorRepository.findById(actorId) - ?: return - - deletedActorRepository.delete(deletedActor) - - owlProducer.publishTask(UpdateActorTask(deletedActor.id, deletedActor.apId)) - } - - override suspend fun deleteLocalUser(userId: Long) { - val actor = actorRepository.findByIdWithLock(userId) ?: throw UserNotFoundException.withId(userId) - apSendDeleteService.sendDeleteActor(actor) - val deletedActor = DeletedActor( - id = actor.id, - name = actor.name, - domain = actor.domain, - apId = actor.url, - publicKey = actor.publicKey, - deletedAt = Instant.now() - ) - relationshipRepository.deleteByActorIdOrTargetActorId(userId, userId) - - reactionRepository.deleteByActorId(actor.id) - - postService.deleteByActor(actor.id) - actorRepository.delete(actor.id) - val userDetail = - userDetailRepository.findByActorId(actor.id) ?: throw IllegalStateException("user detail not found.") - userDetailRepository.delete(userDetail) - deletedActorRepository.save(deletedActor) - } - - override suspend fun updateUserStatistics(userId: Long) { - val actor = actorRepository.findByIdWithLock(userId) ?: throw UserNotFoundException.withId(userId) - - val followerCount = relationshipRepository.countByTargetIdAndFollowing(userId, true) - val followingCount = relationshipRepository.countByUserIdAndFollowing(userId, true) - val postsCount = postRepository.countByActorId(userId) - - actorRepository.save( - actor.copy( - followersCount = followerCount, - followingCount = followingCount, - postsCount = postsCount - ) - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt deleted file mode 100644 index c4bce048..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonApiSecurityConfig.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.config - -import dev.usbharu.hideout.application.infrastructure.springframework.RoleHierarchyAuthorizationManagerFactory -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.core.annotation.Order -import org.springframework.http.HttpMethod.* -import org.springframework.security.access.hierarchicalroles.RoleHierarchy -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.invoke -import org.springframework.security.web.SecurityFilterChain - -@Configuration -class MastodonApiSecurityConfig { - @Bean - @Order(4) - @Suppress("LongMethod") - fun mastodonApiSecurityFilterChain( - http: HttpSecurity, - rf: RoleHierarchyAuthorizationManagerFactory, - ): SecurityFilterChain { - http { - securityMatcher("/api/v1/**", "/api/v2/**") - authorizeHttpRequests { - authorize(POST, "/api/v1/apps", permitAll) - authorize(GET, "/api/v1/instance/**", permitAll) - authorize(POST, "/api/v1/accounts", authenticated) - - authorize(GET, "/api/v1/accounts/verify_credentials", rf.hasScope("read:accounts")) - authorize(GET, "/api/v1/accounts/relationships", rf.hasScope("read:follows")) - authorize(GET, "/api/v1/accounts/*", permitAll) - authorize(GET, "/api/v1/accounts/*/statuses", permitAll) - authorize(POST, "/api/v1/accounts/*/follow", rf.hasScope("write:follows")) - authorize(POST, "/api/v1/accounts/*/unfollow", rf.hasScope("write:follows")) - authorize(POST, "/api/v1/accounts/*/block", rf.hasScope("write:blocks")) - authorize(POST, "/api/v1/accounts/*/unblock", rf.hasScope("write:blocks")) - authorize(POST, "/api/v1/accounts/*/mute", rf.hasScope("write:mutes")) - authorize(POST, "/api/v1/accounts/*/unmute", rf.hasScope("write:mutes")) - authorize(GET, "/api/v1/mutes", rf.hasScope("read:mutes")) - - authorize(POST, "/api/v1/media", rf.hasScope("write:media")) - authorize(POST, "/api/v1/statuses", rf.hasScope("write:statuses")) - authorize(GET, "/api/v1/statuses/*", permitAll) - authorize(POST, "/api/v1/statuses/*/favourite", rf.hasScope("write:favourites")) - authorize(POST, "/api/v1/statuses/*/unfavourite", rf.hasScope("write:favourites")) - authorize(PUT, "/api/v1/statuses/*/emoji_reactions/*", rf.hasScope("write:favourites")) - - authorize(GET, "/api/v1/timelines/public", permitAll) - authorize(GET, "/api/v1/timelines/home", rf.hasScope("read:statuses")) - - authorize(GET, "/api/v2/filters", rf.hasScope("read:filters")) - authorize(POST, "/api/v2/filters", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/*", rf.hasScope("read:filters")) - authorize(PUT, "/api/v2/filters/*", rf.hasScope("write:filters")) - authorize(DELETE, "/api/v2/filters/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/*/keywords", rf.hasScope("read:filters")) - authorize(POST, "/api/v2/filters/*/keywords", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/keywords/*", rf.hasScope("read:filters")) - authorize(PUT, "/api/v2/filters/keywords/*", rf.hasScope("write:filters")) - authorize(DELETE, "/api/v2/filters/keywords/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/*/statuses", rf.hasScope("read:filters")) - authorize(POST, "/api/v2/filters/*/statuses", rf.hasScope("write:filters")) - - authorize(GET, "/api/v2/filters/statuses/*", rf.hasScope("read:filters")) - authorize(DELETE, "/api/v2/filters/statuses/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v1/filters", rf.hasScope("read:filters")) - authorize(POST, "/api/v1/filters", rf.hasScope("write:filters")) - - authorize(GET, "/api/v1/filters/*", rf.hasScope("read:filters")) - authorize(POST, "/api/v1/filters/*", rf.hasScope("write:filters")) - authorize(DELETE, "/api/v1/filters/*", rf.hasScope("write:filters")) - - authorize(GET, "/api/v1/notifications", rf.hasScope("read:notifications")) - authorize(GET, "/api/v1/notifications/*", rf.hasScope("read:notifications")) - authorize(POST, "/api/v1/notifications/clear", rf.hasScope("write:notifications")) - authorize(POST, "/api/v1/notifications/*/dismiss", rf.hasScope("write:notifications")) - - authorize(anyRequest, authenticated) - } - - oauth2ResourceServer { - jwt { } - } - - csrf { - ignoringRequestMatchers("/api/v1/apps") - } - } - - return http.build() - } - - @Bean - fun roleHierarchy(): RoleHierarchy { - val roleHierarchyImpl = RoleHierarchyImpl() - - roleHierarchyImpl.setHierarchy( - """ - SCOPE_read > SCOPE_read:accounts - SCOPE_read > SCOPE_read:accounts - SCOPE_read > SCOPE_read:blocks - SCOPE_read > SCOPE_read:bookmarks - SCOPE_read > SCOPE_read:favourites - SCOPE_read > SCOPE_read:filters - SCOPE_read > SCOPE_read:follows - SCOPE_read > SCOPE_read:lists - SCOPE_read > SCOPE_read:mutes - SCOPE_read > SCOPE_read:notifications - SCOPE_read > SCOPE_read:search - SCOPE_read > SCOPE_read:statuses - SCOPE_write > SCOPE_write:accounts - SCOPE_write > SCOPE_write:blocks - SCOPE_write > SCOPE_write:bookmarks - SCOPE_write > SCOPE_write:conversations - SCOPE_write > SCOPE_write:favourites - SCOPE_write > SCOPE_write:filters - SCOPE_write > SCOPE_write:follows - SCOPE_write > SCOPE_write:lists - SCOPE_write > SCOPE_write:media - SCOPE_write > SCOPE_write:mutes - SCOPE_write > SCOPE_write:notifications - SCOPE_write > SCOPE_write:reports - SCOPE_write > SCOPE_write:statuses - SCOPE_follow > SCOPE_write:blocks - SCOPE_follow > SCOPE_write:follows - SCOPE_follow > SCOPE_write:mutes - SCOPE_follow > SCOPE_read:blocks - SCOPE_follow > SCOPE_read:follows - SCOPE_follow > SCOPE_read:mutes - SCOPE_admin > SCOPE_admin:read - SCOPE_admin > SCOPE_admin:write - SCOPE_admin:read > SCOPE_admin:read:accounts - SCOPE_admin:read > SCOPE_admin:read:reports - SCOPE_admin:read > SCOPE_admin:read:domain_allows - SCOPE_admin:read > SCOPE_admin:read:domain_blocks - SCOPE_admin:read > SCOPE_admin:read:ip_blocks - SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks - SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks - SCOPE_admin:write > SCOPE_admin:write:accounts - SCOPE_admin:write > SCOPE_admin:write:reports - SCOPE_admin:write > SCOPE_admin:write:domain_allows - SCOPE_admin:write > SCOPE_admin:write:domain_blocks - SCOPE_admin:write > SCOPE_admin:write:ip_blocks - SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks - SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks - """.trimIndent() - ) - - return roleHierarchyImpl - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt deleted file mode 100644 index 8f6d5b79..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/AccountNotFoundException.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -class AccountNotFoundException : ClientException { - constructor(response: MastodonApiErrorResponse) : super(response) - constructor(message: String?, response: MastodonApiErrorResponse) : super(message, response) - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse) : super( - message, - cause, - response - ) - - constructor(cause: Throwable?, response: MastodonApiErrorResponse) : super(cause, response) - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse, - ) : super(message, cause, enableSuppression, writableStackTrace, response) - - fun getTypedResponse(): MastodonApiErrorResponse = - response as MastodonApiErrorResponse - - companion object { - fun ofId(id: Long): AccountNotFoundException = AccountNotFoundException( - "id: $id was not found.", - MastodonApiErrorResponse( - NotFoundResponse( - "Record not found" - ), - 404 - ), - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt deleted file mode 100644 index 3414889a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ClientException.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -open class ClientException : MastodonApiException { - constructor(response: MastodonApiErrorResponse<*>) : super(response) - constructor(message: String?, response: MastodonApiErrorResponse<*>) : super(message, response) - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse<*>) : super( - message, - cause, - response - ) - - constructor(cause: Throwable?, response: MastodonApiErrorResponse<*>) : super(cause, response) - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse<*>, - ) : super(message, cause, enableSuppression, writableStackTrace, response) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt deleted file mode 100644 index c3afd55a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/MastodonApiException.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -@Suppress("UnnecessaryAbstractClass") -abstract class MastodonApiException : RuntimeException { - - val response: MastodonApiErrorResponse<*> - - constructor(response: MastodonApiErrorResponse<*>) : super() { - this.response = response - } - - constructor(message: String?, response: MastodonApiErrorResponse<*>) : super(message) { - this.response = response - } - - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse<*>) : super(message, cause) { - this.response = response - } - - constructor(cause: Throwable?, response: MastodonApiErrorResponse<*>) : super(cause) { - this.response = response - } - - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse<*>, - ) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) { - this.response = response - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt deleted file mode 100644 index d2d6b187..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/ServerException.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -open class ServerException(response: MastodonApiErrorResponse<*>) : MastodonApiException(response) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt deleted file mode 100644 index 934403ec..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/exception/StatusNotFoundException.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.exception - -import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse -import dev.usbharu.hideout.mastodon.domain.model.MastodonApiErrorResponse - -class StatusNotFoundException : ClientException { - - constructor(response: MastodonApiErrorResponse) : super(response) - - constructor(message: String?, response: MastodonApiErrorResponse) : super(message, response) - constructor(message: String?, cause: Throwable?, response: MastodonApiErrorResponse) : super( - message, - cause, - response - ) - constructor(cause: Throwable?, response: MastodonApiErrorResponse) : super(cause, response) - - constructor( - message: String?, - cause: Throwable?, - enableSuppression: Boolean, - writableStackTrace: Boolean, - response: MastodonApiErrorResponse, - ) : super(message, cause, enableSuppression, writableStackTrace, response) - - fun getTypedResponse(): MastodonApiErrorResponse = - response as MastodonApiErrorResponse - - companion object { - fun ofId(id: Long): StatusNotFoundException = StatusNotFoundException( - "id: $id was not found.", - MastodonApiErrorResponse( - NotFoundResponse( - "Record not found" - ), - 404 - ), - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt deleted file mode 100644 index ee19ebcf..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonApiErrorResponse.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -data class MastodonApiErrorResponse(val response: R, val statusCode: Int) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt deleted file mode 100644 index e334b04a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotification.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -import org.springframework.data.annotation.Id -import org.springframework.data.mongodb.core.mapping.Document -import java.time.Instant - -@Document -data class MastodonNotification( - @Id - val id: Long, - val userId: Long, - val type: NotificationType, - val createdAt: Instant, - val accountId: Long, - val statusId: Long?, - val reportId: Long?, - val relationshipServeranceEvent: Long? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt deleted file mode 100644 index d1e0ac54..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList - -interface MastodonNotificationRepository { - suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification - suspend fun deleteById(id: Long) - suspend fun findById(id: Long): MastodonNotification? - - @Suppress("FunctionMaxLength") - suspend fun findByUserIdAndInTypesAndInSourceActorId( - loginUser: Long, - types: List, - accountId: List, - page: Page - ): PaginationList - - suspend fun deleteByUserId(userId: Long) - suspend fun deleteByUserIdAndId(userId: Long, id: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt deleted file mode 100644 index a38cfa82..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationType.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -@Suppress("EnumEntryName", "EnumNaming", "EnumEntryNameCase") -enum class NotificationType { - mention, - status, - reblog, - follow, - follow_request, - favourite, - poll, - update, - admin_sign_up, - admin_report, - severed_relationships; - - companion object { - fun parse(string: String): NotificationType? = when (string) { - "mention" -> mention - "status" -> status - "reblog" -> reblog - "follow" -> follow - "follow_request" -> follow_request - "favourite" -> favourite - "poll" -> poll - "update" -> update - "admin.sign_up" -> admin_sign_up - "admin.report" -> admin_report - "servered_relationships" -> severed_relationships - else -> null - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt deleted file mode 100644 index 75cea253..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/AccountQueryServiceImpl.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.exposedquery - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.mastodon.query.AccountQueryService -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.selectAll -import org.springframework.stereotype.Repository -import java.time.Instant - -@Repository -class AccountQueryServiceImpl(private val applicationConfig: ApplicationConfig) : AccountQueryService { - override suspend fun findById(accountId: Long): Account? { - val query = Actors.selectAll().where { Actors.id eq accountId } - - return query - .singleOrNull() - ?.let { toAccount(it) } - } - - override suspend fun findByIds(accountIds: List): List { - val query = Actors.selectAll().where { Actors.id inList accountIds } - - return query - .map { toAccount(it) } - } - - private fun toAccount( - resultRow: ResultRow - ): Account { - val userUrl = "${applicationConfig.url}/users/${resultRow[Actors.id]}" - - return Account( - id = resultRow[Actors.id].toString(), - username = resultRow[Actors.name], - acct = "${resultRow[Actors.name]}@${resultRow[Actors.domain]}", - url = resultRow[Actors.url], - displayName = resultRow[Actors.screenName], - note = resultRow[Actors.description], - avatar = userUrl + "/icon.jpg", - avatarStatic = userUrl + "/icon.jpg", - header = userUrl + "/header.jpg", - headerStatic = userUrl + "/header.jpg", - locked = resultRow[Actors.locked], - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = Instant.ofEpochMilli(resultRow[Actors.createdAt]).toString(), - lastStatusAt = resultRow[Actors.lastPostAt]?.toString(), - statusesCount = resultRow[Actors.postsCount], - followersCount = resultRow[Actors.followersCount], - followingCount = resultRow[Actors.followingCount], - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt deleted file mode 100644 index 3194f368..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.exposedquery - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.withPagination -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji -import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments -import dev.usbharu.hideout.core.infrastructure.exposedrepository.* -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.domain.mastodon.model.generated.Status.Visibility.* -import dev.usbharu.hideout.mastodon.interfaces.api.status.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.selectAll -import org.springframework.stereotype.Repository -import java.time.Instant -import dev.usbharu.hideout.domain.mastodon.model.generated.CustomEmoji as MastodonEmoji - -@Suppress("IncompleteDestructuring") -@Repository -class StatusQueryServiceImpl : StatusQueryService { - override suspend fun findByPostIds(ids: List): List = findByPostIdsWithMedia(ids) - - override suspend fun findByPostIdsWithMediaIds(statusQueries: List): List { - val postIdSet = mutableSetOf() - postIdSet.addAll(statusQueries.flatMap { listOfNotNull(it.postId, it.replyId, it.repostId) }) - val mediaIdSet = mutableSetOf() - mediaIdSet.addAll(statusQueries.flatMap { it.mediaIds }) - - val emojiIdSet = mutableSetOf() - emojiIdSet.addAll(statusQueries.flatMap { it.emojiIds }) - - val postMap = Posts - .leftJoin(Actors) - .selectAll().where { Posts.id inList postIdSet } - .associate { it[Posts.id] to toStatus(it) } - val mediaMap = Media.selectAll().where { Media.id inList mediaIdSet } - .associate { - it[Media.id] to it.toMedia().toMediaAttachments() - } - - val emojiMap = CustomEmojis.selectAll().where { CustomEmojis.id inList emojiIdSet }.associate { - it[CustomEmojis.id] to it.toCustomEmoji().toMastodonEmoji() - } - return statusQueries.mapNotNull { statusQuery -> - postMap[statusQuery.postId]?.copy( - inReplyToId = statusQuery.replyId?.toString(), - inReplyToAccountId = postMap[statusQuery.replyId]?.account?.id, - reblog = postMap[statusQuery.repostId], - mediaAttachments = statusQuery.mediaIds.mapNotNull { mediaMap[it] }, - emojis = statusQuery.emojiIds.mapNotNull { emojiMap[it] } - ) - } - } - - override suspend fun accountsStatus( - accountId: Long, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - includeFollowers: Boolean, - page: Page - ): PaginationList { - val query = Posts - .leftJoin(PostsMedia) - .leftJoin(Actors) - .leftJoin(Media) - .selectAll().where { Posts.actorId eq accountId } - - if (onlyMedia) { - query.andWhere { PostsMedia.mediaId.isNotNull() } - } - if (excludeReplies) { - query.andWhere { Posts.replyId.isNotNull() } - } - if (excludeReblogs) { - query.andWhere { Posts.repostId.isNotNull() } - } - if (includeFollowers) { - query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal, private.ordinal) } - } else { - query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal) } - } - - val pairs = query - .withPagination(page, Posts.id) - .groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - } - ) to it.first()[Posts.repostId] - } - - val statuses = resolveReplyAndRepost(pairs) - return PaginationList( - statuses, - statuses.firstOrNull()?.id?.toLongOrNull(), - statuses.lastOrNull()?.id?.toLongOrNull() - ) - } - - override suspend fun findByPostId(id: Long): Status? { - val map = Posts - .leftJoin(PostsMedia) - .leftJoin(Actors) - .leftJoin(Media) - .selectAll() - .where { Posts.id eq id } - .groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - }, - emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() } - ) to it.first()[Posts.repostId] - } - return resolveReplyAndRepost(map).singleOrNull() - } - - private fun resolveReplyAndRepost(pairs: List>): List { - val statuses = pairs.map { it.first } - return pairs - .map { - if (it.second != null) { - it.first.copy(reblog = statuses.find { (id) -> id == it.second.toString() }) - } else { - it.first - } - } - .map { - if (it.inReplyToId != null) { - println("statuses trace: $statuses") - println("inReplyToId trace: ${it.inReplyToId}") - it.copy(inReplyToAccountId = statuses.find { (id) -> id == it.inReplyToId }?.account?.id) - } else { - it - } - } - } - - private suspend fun findByPostIdsWithMedia(ids: List): List { - val pairs = Posts - .leftJoin(PostsMedia) - .leftJoin(PostsEmojis) - .leftJoin(CustomEmojis) - .leftJoin(Actors) - .leftJoin(Media) - .selectAll().where { Posts.id inList ids } - .groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - }, - emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() } - ) to it.first()[Posts.repostId] - } - return resolveReplyAndRepost(pairs) - } -} - -private fun CustomEmoji.toMastodonEmoji(): MastodonEmoji = MastodonEmoji( - shortcode = this.name, - url = this.url, - staticUrl = this.url, - visibleInPicker = true, - category = this.category.orEmpty() -) - -private fun toStatus(it: ResultRow) = Status( - id = it[Posts.id].toString(), - uri = it[Posts.apId], - createdAt = Instant.ofEpochMilli(it[Posts.createdAt]).toString(), - account = Account( - id = it[Actors.id].toString(), - username = it[Actors.name], - acct = "${it[Actors.name]}@${it[Actors.domain]}", - url = it[Actors.url], - displayName = it[Actors.screenName], - note = it[Actors.description], - avatar = it[Actors.url] + "/icon.jpg", - avatarStatic = it[Actors.url] + "/icon.jpg", - header = it[Actors.url] + "/header.jpg", - headerStatic = it[Actors.url] + "/header.jpg", - locked = it[Actors.locked], - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = Instant.ofEpochMilli(it[Actors.createdAt]).toString(), - lastStatusAt = it[Actors.lastPostAt]?.toString(), - statusesCount = it[Actors.postsCount], - followersCount = it[Actors.followersCount], - followingCount = it[Actors.followingCount], - noindex = false, - moved = false, - suspendex = false, - limited = false - ), - content = it[Posts.text], - visibility = when (it[Posts.visibility]) { - 0 -> public - 1 -> unlisted - 2 -> private - 3 -> direct - else -> public - }, - sensitive = it[Posts.sensitive], - spoilerText = it[Posts.overview].orEmpty(), - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = it[Posts.apId], - inReplyToId = it[Posts.replyId]?.toString(), - inReplyToAccountId = null, - language = null, - text = it[Posts.text], - editedAt = null -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt deleted file mode 100644 index 7f928b08..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.withPagination -import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.stereotype.Repository - -@Repository -@Qualifier("jdbc") -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true) -class ExposedMastodonNotificationRepository : MastodonNotificationRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification = query { - val singleOrNull = - MastodonNotifications.selectAll().where { MastodonNotifications.id eq mastodonNotification.id } - .singleOrNull() - if (singleOrNull == null) { - MastodonNotifications.insert { - it[id] = mastodonNotification.id - it[type] = mastodonNotification.type.name - it[createdAt] = mastodonNotification.createdAt - it[accountId] = mastodonNotification.accountId - it[statusId] = mastodonNotification.statusId - it[reportId] = mastodonNotification.reportId - it[relationshipServeranceEventId] = - mastodonNotification.relationshipServeranceEvent - } - } else { - MastodonNotifications.update({ MastodonNotifications.id eq mastodonNotification.id }) { - it[type] = mastodonNotification.type.name - it[createdAt] = mastodonNotification.createdAt - it[accountId] = mastodonNotification.accountId - it[statusId] = mastodonNotification.statusId - it[reportId] = mastodonNotification.reportId - it[relationshipServeranceEventId] = - mastodonNotification.relationshipServeranceEvent - } - } - mastodonNotification - } - - override suspend fun deleteById(id: Long): Unit = query { - MastodonNotifications.deleteWhere { - MastodonNotifications.id eq id - } - } - - override suspend fun findById(id: Long): MastodonNotification? = query { - MastodonNotifications.selectAll().where { MastodonNotifications.id eq id }.singleOrNull() - ?.toMastodonNotification() - } - - override suspend fun findByUserIdAndInTypesAndInSourceActorId( - loginUser: Long, - types: List, - accountId: List, - page: Page - ): PaginationList = query { - val query = MastodonNotifications.selectAll().where { MastodonNotifications.userId eq loginUser } - val result = query.withPagination(page, MastodonNotifications.id) - - return@query PaginationList(result.map { it.toMastodonNotification() }, result.next, result.prev) - } - - override suspend fun deleteByUserId(userId: Long) { - MastodonNotifications.deleteWhere { - MastodonNotifications.userId eq userId - } - } - - override suspend fun deleteByUserIdAndId(userId: Long, id: Long) { - MastodonNotifications.deleteWhere { - MastodonNotifications.userId eq userId and (MastodonNotifications.id eq id) - } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedMastodonNotificationRepository::class.java) - } -} - -fun ResultRow.toMastodonNotification(): MastodonNotification = MastodonNotification( - id = this[MastodonNotifications.id], - userId = this[MastodonNotifications.userId], - type = NotificationType.valueOf(this[MastodonNotifications.type]), - createdAt = this[MastodonNotifications.createdAt], - accountId = this[MastodonNotifications.accountId], - statusId = this[MastodonNotifications.statusId], - reportId = this[MastodonNotifications.reportId], - relationshipServeranceEvent = this[MastodonNotifications.relationshipServeranceEventId], -) - -object MastodonNotifications : Table("mastodon_notifications") { - val id = long("id") - val userId = long("user_id") - val type = varchar("type", 100) - val createdAt = timestamp("created_at") - val accountId = long("account_id") - val statusId = long("status_id").nullable() - val reportId = long("report_id").nullable() - val relationshipServeranceEventId = long("relationship_serverance_event_id").nullable() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt deleted file mode 100644 index e95d204f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepository.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.mongorepository - -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import org.springframework.data.mongodb.repository.MongoRepository - -interface MongoMastodonNotificationRepository : MongoRepository { - - fun deleteByUserId(userId: Long): Long - - fun deleteByIdAndUserId(id: Long, userId: Long): Long -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt deleted file mode 100644 index 4fd243e9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.mongorepository - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.data.domain.Sort -import org.springframework.data.mongodb.core.MongoTemplate -import org.springframework.data.mongodb.core.query.Criteria -import org.springframework.data.mongodb.core.query.Query -import org.springframework.stereotype.Repository -import kotlin.jvm.optionals.getOrNull - -@Repository -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false) -class MongoMastodonNotificationRepositoryWrapper( - private val mongoMastodonNotificationRepository: MongoMastodonNotificationRepository, - private val mongoTemplate: MongoTemplate -) : - MastodonNotificationRepository { - override suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification = - mongoMastodonNotificationRepository.save(mastodonNotification) - - override suspend fun deleteById(id: Long) = mongoMastodonNotificationRepository.deleteById(id) - - override suspend fun findById(id: Long): MastodonNotification? = - mongoMastodonNotificationRepository.findById(id).getOrNull() - - override suspend fun findByUserIdAndInTypesAndInSourceActorId( - loginUser: Long, - types: List, - accountId: List, - page: Page - ): PaginationList { - val query = Query() - - page.limit?.let { query.limit(it) } - - val mastodonNotifications = if (page.minId != null) { - query.with(Sort.by(Sort.Direction.ASC, "id")) - page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) } - page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } - mongoTemplate.find(query, MastodonNotification::class.java).reversed() - } else { - query.with(Sort.by(Sort.Direction.DESC, "id")) - page.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) } - page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } - mongoTemplate.find(query, MastodonNotification::class.java) - } - - return PaginationList( - mastodonNotifications, - mastodonNotifications.firstOrNull()?.id, - mastodonNotifications.lastOrNull()?.id - ) - } - - override suspend fun deleteByUserId(userId: Long) { - mongoMastodonNotificationRepository.deleteByUserId(userId) - } - - override suspend fun deleteByUserIdAndId(userId: Long, id: Long) { - mongoMastodonNotificationRepository.deleteByIdAndUserId(id, userId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt deleted file mode 100644 index b154f52b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/springweb/MastodonApiControllerAdvice.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.infrastructure.springweb - -import dev.usbharu.hideout.domain.mastodon.model.generated.NotFoundResponse -import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponse -import dev.usbharu.hideout.domain.mastodon.model.generated.UnprocessableEntityResponseDetails -import dev.usbharu.hideout.mastodon.domain.exception.AccountNotFoundException -import dev.usbharu.hideout.mastodon.domain.exception.StatusNotFoundException -import dev.usbharu.hideout.mastodon.interfaces.api.account.MastodonAccountApiController -import dev.usbharu.hideout.mastodon.interfaces.api.apps.MastodonAppsApiController -import dev.usbharu.hideout.mastodon.interfaces.api.filter.MastodonFilterApiController -import dev.usbharu.hideout.mastodon.interfaces.api.instance.MastodonInstanceApiController -import dev.usbharu.hideout.mastodon.interfaces.api.media.MastodonMediaApiController -import dev.usbharu.hideout.mastodon.interfaces.api.notification.MastodonNotificationApiController -import dev.usbharu.hideout.mastodon.interfaces.api.status.MastodonStatusesApiContoller -import dev.usbharu.hideout.mastodon.interfaces.api.timeline.MastodonTimelineApiController -import org.slf4j.LoggerFactory -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.validation.BindException -import org.springframework.validation.FieldError -import org.springframework.web.bind.annotation.ControllerAdvice -import org.springframework.web.bind.annotation.ExceptionHandler - -@ControllerAdvice( - assignableTypes = [ - MastodonAccountApiController::class, - MastodonAppsApiController::class, - MastodonFilterApiController::class, - MastodonInstanceApiController::class, - MastodonMediaApiController::class, - MastodonNotificationApiController::class, - MastodonStatusesApiContoller::class, - MastodonTimelineApiController::class - ] -) -class MastodonApiControllerAdvice { - - @ExceptionHandler(BindException::class) - fun handleException(ex: BindException): ResponseEntity { - logger.debug("Failed bind entity.", ex) - - val details = mutableMapOf>() - - ex.allErrors.forEach { - val defaultMessage = it.defaultMessage - when { - it is FieldError -> { - val code = when (it.code) { - "Email" -> "ERR_INVALID" - "Pattern" -> "ERR_INVALID" - else -> "ERR_INVALID" - } - details.getOrPut(it.field) { - mutableListOf() - }.add(UnprocessableEntityResponseDetails(code, defaultMessage.orEmpty())) - } - - defaultMessage?.startsWith("Parameter specified as non-null is null:") == true -> { - val parameter = defaultMessage.substringAfterLast("parameter ") - - details.getOrPut(parameter) { - mutableListOf() - }.add(UnprocessableEntityResponseDetails("ERR_BLANK", "can't be blank")) - } - - else -> { - logger.warn("Unknown validation error", ex) - } - } - } - - val message = details.map { - it.key + " " + it.value.joinToString { responseDetails -> responseDetails.description } - }.joinToString() - - return ResponseEntity.unprocessableEntity() - .body(UnprocessableEntityResponse(message, details)) - } - - @ExceptionHandler(StatusNotFoundException::class) - fun handleException(ex: StatusNotFoundException): ResponseEntity { - logger.warn("Status not found.", ex) - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response) - } - - @ExceptionHandler(AccountNotFoundException::class) - fun handleException(ex: AccountNotFoundException): ResponseEntity { - logger.warn("Account not found.", ex) - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getTypedResponse().response) - } - - companion object { - private val logger = LoggerFactory.getLogger(MastodonApiControllerAdvice::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt deleted file mode 100644 index a2e391af..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.account - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader -import dev.usbharu.hideout.controller.mastodon.generated.AccountApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.core.service.user.UserCreateDto -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.service.account.AccountApiService -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.runBlocking -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import java.net.URI - -@Controller -class MastodonAccountApiController( - private val accountApiService: AccountApiService, - private val transaction: Transaction, - private val loginUserContextHolder: LoginUserContextHolder, - private val applicationConfig: ApplicationConfig -) : AccountApi { - - override suspend fun apiV1AccountsIdFollowPost( - id: String, - followRequestBody: FollowRequestBody? - ): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - return ResponseEntity.ok(accountApiService.follow(userid, id.toLong())) - } - - override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity = - ResponseEntity.ok(accountApiService.account(id.toLong())) - - override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity = ResponseEntity( - accountApiService.verifyCredentials(loginUserContextHolder.getLoginUserId()), - HttpStatus.OK - ) - - override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity { - transaction.transaction { - accountApiService.registerAccount( - UserCreateDto( - accountsCreateRequest.username, - accountsCreateRequest.username, - "", - accountsCreateRequest.password - ) - ) - } - val httpHeaders = HttpHeaders() - httpHeaders.location = URI("/users/${accountsCreateRequest.username}") - return ResponseEntity(Unit, httpHeaders, HttpStatus.FOUND) - } - - override fun apiV1AccountsIdStatusesGet( - id: String, - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String? - ): ResponseEntity> = runBlocking { - val userid = loginUserContextHolder.getLoginUserIdOrNull() - val statuses = accountApiService.accountsStatuses( - userid = id.toLong(), - onlyMedia = onlyMedia, - excludeReplies = excludeReplies, - excludeReblogs = excludeReblogs, - pinned = pinned, - tagged = tagged, - loginUser = userid, - page = Page.of( - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - minId?.toLongOrNull(), - limit.coerceIn(0, 80) - ) - ) - val httpHeader = statuses.toHttpHeader( - { "${applicationConfig.url}/api/v1/accounts/$id/statuses?min_id=$it" }, - { "${applicationConfig.url}/api/v1/accounts/$id/statuses?max_id=$it" }, - ) - - if (httpHeader != null) { - return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(statuses.asFlow()) - } - - ResponseEntity.ok(statuses.asFlow()) - } - - override fun apiV1AccountsRelationshipsGet( - id: List?, - withSuspended: Boolean - ): ResponseEntity> = runBlocking { - val userid = loginUserContextHolder.getLoginUserId() - - ResponseEntity.ok( - accountApiService.relationships(userid, id.orEmpty().mapNotNull { it.toLongOrNull() }, withSuspended) - .asFlow() - ) - } - - override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val block = accountApiService.block(userid, id.toLong()) - - return ResponseEntity.ok(block) - } - - override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val unblock = accountApiService.unblock(userid, id.toLong()) - - return ResponseEntity.ok(unblock) - } - - override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val unfollow = accountApiService.unfollow(userid, id.toLong()) - - return ResponseEntity.ok(unfollow) - } - - override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val removeFromFollowers = accountApiService.removeFromFollowers(userid, id.toLong()) - - return ResponseEntity.ok(removeFromFollowers) - } - - override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val removeFromFollowers = accountApiService.updateProfile(userid, updateCredentials) - - return ResponseEntity.ok(removeFromFollowers) - } - - override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val acceptFollowRequest = accountApiService.acceptFollowRequest(userid, accountId.toLong()) - - return ResponseEntity.ok(acceptFollowRequest) - } - - override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val rejectFollowRequest = accountApiService.rejectFollowRequest(userid, accountId.toLong()) - - return ResponseEntity.ok(rejectFollowRequest) - } - - override fun apiV1FollowRequestsGet(maxId: String?, sinceId: String?, limit: Int?): ResponseEntity> = - runBlocking { - val userid = loginUserContextHolder.getLoginUserId() - - val followRequests = accountApiService.followRequests( - userid, - false, - Page.PageByMaxId( - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - limit?.coerceIn(0, 80) ?: 40 - ) - - ) - - val httpHeader = followRequests.toHttpHeader( - { "${applicationConfig.url}/api/v1/follow_requests?max_id=$it" }, - { "${applicationConfig.url}/api/v1/follow_requests?min_id=$it" }, - ) - - if (httpHeader != null) { - return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(followRequests.asFlow()) - } - - ResponseEntity.ok(followRequests.asFlow()) - } - - override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val mute = accountApiService.mute(userid, id.toLong()) - - return ResponseEntity.ok(mute) - } - - override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - val unmute = accountApiService.unmute(userid, id.toLong()) - - return ResponseEntity.ok(unmute) - } - - override fun apiV1MutesGet(maxId: String?, sinceId: String?, limit: Int?): ResponseEntity> = - runBlocking { - val userid = loginUserContextHolder.getLoginUserId() - - val mutes = - accountApiService.mutesAccount( - userid, - Page.PageByMaxId(maxId?.toLongOrNull(), sinceId?.toLongOrNull(), limit?.coerceIn(0, 80) ?: 40) - ) - - val httpHeader = mutes.toHttpHeader( - { "${applicationConfig.url}/api/v1/mutes?max_id=$it" }, - { "${applicationConfig.url}/api/v1/mutes?since_id=$it" }, - ) - - if (httpHeader != null) { - return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(mutes.asFlow()) - } - - return@runBlocking ResponseEntity.ok(mutes.asFlow()) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt deleted file mode 100644 index 8424d6d9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiController.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.apps - -import dev.usbharu.hideout.controller.mastodon.generated.AppApi -import dev.usbharu.hideout.domain.mastodon.model.generated.Application -import dev.usbharu.hideout.domain.mastodon.model.generated.AppsRequest -import dev.usbharu.hideout.mastodon.service.app.AppApiService -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RequestParam - -@Controller -class MastodonAppsApiController(private val appApiService: AppApiService) : AppApi { - override suspend fun apiV1AppsPost(appsRequest: AppsRequest): ResponseEntity { - return ResponseEntity( - appApiService.createApp(appsRequest), - HttpStatus.OK - ) - } - - @RequestMapping( - method = [RequestMethod.POST], - value = ["/api/v1/apps"], - produces = ["application/json"], - consumes = ["application/x-www-form-urlencoded"] - ) - suspend fun apiV1AppsPost(@RequestParam map: Map): ResponseEntity { - val appsRequest = - AppsRequest(map.getValue("client_name"), map.getValue("redirect_uris"), map["scopes"], map["website"]) - return ResponseEntity( - appApiService.createApp(appsRequest), - HttpStatus.OK - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt deleted file mode 100644 index 28f5d3df..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/filter/MastodonFilterApiController.kt +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.filter - -import dev.usbharu.hideout.controller.mastodon.generated.FilterApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.service.filter.MastodonFilterApiService -import kotlinx.coroutines.flow.Flow -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonFilterApiController( - private val mastodonFilterApiService: MastodonFilterApiService, - private val loginUserContextHolder: LoginUserContextHolder -) : FilterApi { - - override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteV1FilterById(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV1FiltersIdGet( - id: String - ): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.getV1FilterById( - loginUserContextHolder.getLoginUserId(), - id.toLong() - ) - ) - } - - override suspend fun apiV1FiltersIdPut( - id: String, - phrase: String?, - context: List?, - irreversible: Boolean?, - wholeWord: Boolean?, - expiresIn: Int? - ): ResponseEntity = super.apiV1FiltersIdPut(id, phrase, context, irreversible, wholeWord, expiresIn) - - override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.createByV1Filter(loginUserContextHolder.getLoginUserId(), v1FilterPostRequest) - ) - } - - override suspend fun apiV2FiltersFilterIdKeywordsPost( - filterId: String, - filterKeywordsPostRequest: FilterKeywordsPostRequest - ): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.addKeyword( - loginUserContextHolder.getLoginUserId(), - filterId.toLong(), - filterKeywordsPostRequest - ) - ) - } - - override suspend fun apiV2FiltersFilterIdStatusesPost( - filterId: String, - filterStatusRequest: FilterStatusRequest - ): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.addFilterStatus( - loginUserContextHolder.getLoginUserId(), - filterId.toLong(), - filterStatusRequest - ) - ) - } - - override fun apiV1FiltersGet(): ResponseEntity> = - ResponseEntity.ok(mastodonFilterApiService.v1Filters(loginUserContextHolder.getLoginUserId())) - - override fun apiV2FiltersFilterIdKeywordsGet(filterId: String): ResponseEntity> { - return ResponseEntity.ok( - mastodonFilterApiService.filterKeywords( - loginUserContextHolder.getLoginUserId(), - filterId.toLong() - ) - ) - } - - override fun apiV2FiltersFilterIdStatusesGet(filterId: String): ResponseEntity> { - return ResponseEntity.ok( - mastodonFilterApiService.filterStatuses( - loginUserContextHolder.getLoginUserId(), - filterId.toLong() - ) - ) - } - - override fun apiV2FiltersGet(): ResponseEntity> = - ResponseEntity.ok(mastodonFilterApiService.filters(loginUserContextHolder.getLoginUserId())) - - override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteById(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity = - ResponseEntity.ok(mastodonFilterApiService.getById(loginUserContextHolder.getLoginUserId(), id.toLong())) - - override suspend fun apiV2FiltersIdPut( - id: String, - title: String?, - context: List?, - filterAction: String?, - expiresIn: Int?, - keywordsAttributes: List? - ): ResponseEntity = - super.apiV2FiltersIdPut(id, title, context, filterAction, expiresIn, keywordsAttributes) - - override suspend fun apiV2FiltersKeywordsIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteKeyword(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV2FiltersKeywordsIdGet(id: String): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.getKeywordById( - loginUserContextHolder.getLoginUserId(), - id.toLong() - ) - ) - } - - override suspend fun apiV2FiltersKeywordsIdPut( - id: String, - keyword: String?, - wholeWord: Boolean?, - regex: Boolean? - ): ResponseEntity = super.apiV2FiltersKeywordsIdPut(id, keyword, wholeWord, regex) - - override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity = - ResponseEntity.ok( - mastodonFilterApiService.createFilter( - loginUserContextHolder.getLoginUserId(), - filterPostRequest - ) - ) - - override suspend fun apiV2FiltersStatusesIdDelete(id: String): ResponseEntity { - mastodonFilterApiService.deleteFilterStatusById(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok().build() - } - - override suspend fun apiV2FiltersStatusesIdGet(id: String): ResponseEntity { - return ResponseEntity.ok( - mastodonFilterApiService.getFilterStatusById( - loginUserContextHolder.getLoginUserId(), - id.toLong() - ) - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt deleted file mode 100644 index 220625ce..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiController.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.instance - -import dev.usbharu.hideout.controller.mastodon.generated.InstanceApi -import dev.usbharu.hideout.domain.mastodon.model.generated.V1Instance -import dev.usbharu.hideout.mastodon.service.instance.InstanceApiService -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonInstanceApiController(private val instanceApiService: InstanceApiService) : InstanceApi { - override suspend fun apiV1InstanceGet(): ResponseEntity = - ResponseEntity(instanceApiService.v1Instance(), HttpStatus.OK) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt deleted file mode 100644 index adcdb770..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiController.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.media - -import dev.usbharu.hideout.controller.mastodon.generated.MediaApi -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.service.media.MediaApiService -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller -import org.springframework.web.multipart.MultipartFile - -@Controller -class MastodonMediaApiController(private val mediaApiService: MediaApiService) : MediaApi { - override suspend fun apiV1MediaPost( - file: MultipartFile, - thumbnail: MultipartFile?, - description: String?, - focus: String? - ): ResponseEntity { - return ResponseEntity.ok( - mediaApiService.postMedia( - MediaRequest( - file, - thumbnail, - description, - focus - ) - ) - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt deleted file mode 100644 index d9637193..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MediaRequest.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.media - -import org.springframework.web.multipart.MultipartFile - -data class MediaRequest( - val file: MultipartFile, - val thumbnail: MultipartFile?, - val description: String?, - val focus: String? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt deleted file mode 100644 index 9b0a466a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.notification - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader -import dev.usbharu.hideout.controller.mastodon.generated.NotificationsApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import dev.usbharu.hideout.mastodon.service.notification.NotificationApiService -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.runBlocking -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonNotificationApiController( - private val loginUserContextHolder: LoginUserContextHolder, - private val notificationApiService: NotificationApiService, - private val applicationConfig: ApplicationConfig -) : NotificationsApi { - override suspend fun apiV1NotificationsClearPost(): ResponseEntity { - notificationApiService.clearAll(loginUserContextHolder.getLoginUserId()) - return ResponseEntity.ok(null) - } - - override fun apiV1NotificationsGet( - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int?, - types: List?, - excludeTypes: List?, - accountId: List? - ): ResponseEntity> = runBlocking { - val notifications = notificationApiService.notifications( - loginUser = loginUserContextHolder.getLoginUserId(), - types = types.orEmpty().mapNotNull { NotificationType.parse(it) }, - excludeTypes = excludeTypes.orEmpty().mapNotNull { NotificationType.parse(it) }, - accountId = accountId.orEmpty().mapNotNull { it.toLongOrNull() }, - page = Page.of( - maxId?.toLongOrNull(), - sinceId?.toLongOrNull(), - minId?.toLongOrNull(), - limit?.coerceIn(0, 80) ?: 40 - ) - ) - - val httpHeader = notifications.toHttpHeader( - { "${applicationConfig.url}/api/v1/notifications?min_id=$it" }, - { "${applicationConfig.url}/api/v1/notifications?max_id=$it" } - ) ?: return@runBlocking ResponseEntity.ok( - notifications.asFlow() - ) - - ResponseEntity.ok().header("Link", httpHeader).body(notifications.asFlow()) - } - - override suspend fun apiV1NotificationsIdDismissPost(id: String): ResponseEntity { - notificationApiService.dismiss(loginUserContextHolder.getLoginUserId(), id.toLong()) - return ResponseEntity.ok(null) - } - - override suspend fun apiV1NotificationsIdGet(id: String): ResponseEntity { - val notification = notificationApiService.fingById(loginUserContextHolder.getLoginUserId(), id.toLong()) - - return ResponseEntity.ok(notification) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt deleted file mode 100644 index 20858ec6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiContoller.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -import dev.usbharu.hideout.controller.mastodon.generated.StatusApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.service.status.StatusesApiService -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonStatusesApiContoller( - private val statusesApiService: StatusesApiService, - private val loginUserContextHolder: LoginUserContextHolder -) : StatusApi { - override suspend fun apiV1StatusesPost( - devUsbharuHideoutDomainModelMastodonStatusesRequest: StatusesRequest - ): ResponseEntity { - val userid = loginUserContextHolder.getLoginUserId() - - return ResponseEntity( - statusesApiService.postStatus( - devUsbharuHideoutDomainModelMastodonStatusesRequest, - userid - ), - HttpStatus.OK - ) - } - - override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity { - val uid = - loginUserContextHolder.getLoginUserId() - - return ResponseEntity.ok(statusesApiService.removeEmojiReactions(id.toLong(), uid, emoji)) - } - - override suspend fun apiV1StatusesIdEmojiReactionsEmojiPut(id: String, emoji: String): ResponseEntity { - val uid = - loginUserContextHolder.getLoginUserId() - - return ResponseEntity.ok(statusesApiService.emojiReactions(id.toLong(), uid, emoji)) - } - - override suspend fun apiV1StatusesIdGet(id: String): ResponseEntity { - val uid = loginUserContextHolder.getLoginUserIdOrNull() - - return ResponseEntity.ok(statusesApiService.findById(id.toLong(), uid)) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt deleted file mode 100644 index 19d2cc8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusQuery.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -data class StatusQuery( - val postId: Long, - val replyId: Long?, - val repostId: Long?, - val mediaIds: List, - val emojiIds: List -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt deleted file mode 100644 index 1005680d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -import com.fasterxml.jackson.annotation.JsonProperty -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequestPoll -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest.Visibility.* - -@Suppress("VariableNaming", "EnumEntryName") -class StatusesRequest { - @JsonProperty("status") - var status: String? = null - - @JsonProperty("media_ids") - var media_ids: List = emptyList() - - @JsonProperty("poll") - var poll: StatusesRequestPoll? = null - - @JsonProperty("in_reply_to_id") - var in_reply_to_id: String? = null - - @JsonProperty("sensitive") - var sensitive: Boolean? = null - - @JsonProperty("spoiler_text") - var spoiler_text: String? = null - - @JsonProperty("visibility") - var visibility: Visibility? = null - - @JsonProperty("language") - var language: String? = null - - @JsonProperty("scheduled_at") - var scheduled_at: String? = null - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is StatusesRequest) return false - - if (status != other.status) return false - if (media_ids != other.media_ids) return false - if (poll != other.poll) return false - if (in_reply_to_id != other.in_reply_to_id) return false - if (sensitive != other.sensitive) return false - if (spoiler_text != other.spoiler_text) return false - if (visibility != other.visibility) return false - if (language != other.language) return false - if (scheduled_at != other.scheduled_at) return false - - return true - } - - override fun hashCode(): Int { - var result = status?.hashCode() ?: 0 - result = 31 * result + media_ids.hashCode() - result = 31 * result + (poll?.hashCode() ?: 0) - result = 31 * result + (in_reply_to_id?.hashCode() ?: 0) - result = 31 * result + (sensitive?.hashCode() ?: 0) - result = 31 * result + (spoiler_text?.hashCode() ?: 0) - result = 31 * result + (visibility?.hashCode() ?: 0) - result = 31 * result + (language?.hashCode() ?: 0) - result = 31 * result + (scheduled_at?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "StatusesRequest(" + - "status=$status, " + - "media_ids=$media_ids, " + - "poll=$poll, " + - "in_reply_to_id=$in_reply_to_id, " + - "sensitive=$sensitive, " + - "spoiler_text=$spoiler_text, " + - "visibility=$visibility, " + - "language=$language, " + - "scheduled_at=$scheduled_at" + - ")" - } - - @Suppress("EnumNaming", "EnumEntryNameCase") - enum class Visibility { - `public`, - unlisted, - private, - direct - } -} - -fun StatusesRequest.Visibility?.toPostVisibility(): Visibility { - return when (this) { - public -> Visibility.PUBLIC - unlisted -> Visibility.UNLISTED - private -> Visibility.FOLLOWERS - direct -> Visibility.DIRECT - null -> Visibility.PUBLIC - } -} - -fun StatusesRequest.Visibility?.toStatusVisibility(): Status.Visibility { - return when (this) { - public -> Status.Visibility.public - unlisted -> Status.Visibility.unlisted - private -> Status.Visibility.private - direct -> Status.Visibility.direct - null -> Status.Visibility.public - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt deleted file mode 100644 index 7bbc8f8d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.timeline - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader -import dev.usbharu.hideout.controller.mastodon.generated.TimelineApi -import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.service.timeline.TimelineApiService -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.runBlocking -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Controller - -@Controller -class MastodonTimelineApiController( - private val timelineApiService: TimelineApiService, - private val loginUserContextHolder: LoginUserContextHolder, - private val applicationConfig: ApplicationConfig, -) : TimelineApi { - override fun apiV1TimelinesHomeGet( - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int? - ): ResponseEntity> = runBlocking { - val homeTimeline = timelineApiService.homeTimeline( - userId = loginUserContextHolder.getLoginUserId(), - page = Page.of( - maxId = maxId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - limit = limit?.coerceIn(0, 80) ?: 40 - ) - ) - - val httpHeader = homeTimeline.toHttpHeader( - { "${applicationConfig.url}/api/v1/home?max_id=$it" }, - { "${applicationConfig.url}/api/v1/home?min_id=$it" } - ) ?: return@runBlocking ResponseEntity( - homeTimeline.asFlow(), - HttpStatus.OK - ) - ResponseEntity.ok().header("Link", httpHeader).body(homeTimeline.asFlow()) - } - - override fun apiV1TimelinesPublicGet( - local: Boolean?, - remote: Boolean?, - onlyMedia: Boolean?, - maxId: String?, - sinceId: String?, - minId: String?, - limit: Int? - ): ResponseEntity> = runBlocking { - val publicTimeline = timelineApiService.publicTimeline( - localOnly = local ?: false, - remoteOnly = remote ?: false, - mediaOnly = onlyMedia ?: false, - page = Page.of( - maxId = maxId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - limit = limit?.coerceIn(0, 80) ?: 40 - ) - ) - - val httpHeader = publicTimeline.toHttpHeader( - { "${applicationConfig.url}/api/v1/public?max_id=$it" }, - { "${applicationConfig.url}/api/v1/public?min_id=$it" } - ) ?: return@runBlocking ResponseEntity( - publicTimeline.asFlow(), - HttpStatus.OK - ) - ResponseEntity.ok().header("Link", httpHeader).body(publicTimeline.asFlow()) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt deleted file mode 100644 index 61b49950..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.query - -import dev.usbharu.hideout.domain.mastodon.model.generated.Account - -interface AccountQueryService { - suspend fun findById(accountId: Long): Account? - suspend fun findByIds(accountIds: List): List -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt deleted file mode 100644 index e5640509..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.query - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery - -interface StatusQueryService { - suspend fun findByPostIds(ids: List): List - suspend fun findByPostIdsWithMediaIds(statusQueries: List): List - - @Suppress("LongParameterList") - suspend fun accountsStatus( - accountId: Long, - onlyMedia: Boolean = false, - excludeReplies: Boolean = false, - excludeReblogs: Boolean = false, - pinned: Boolean = false, - tagged: String?, - includeFollowers: Boolean = false, - page: Page - ): PaginationList - - suspend fun findByPostId(id: Long): Status? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt deleted file mode 100644 index 0e3afc2f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.account - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UpdateUserDto -import dev.usbharu.hideout.core.service.user.UserCreateDto -import dev.usbharu.hideout.core.service.user.UserService -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.domain.exception.AccountNotFoundException -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import kotlin.math.min - -@Service -@Suppress("TooManyFunctions") -interface AccountApiService { - - @Suppress("LongParameterList") - suspend fun accountsStatuses( - userid: Long, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - loginUser: Long?, - page: Page - ): PaginationList - - suspend fun verifyCredentials(userid: Long): CredentialAccount - suspend fun registerAccount(userCreateDto: UserCreateDto): Unit - suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship - suspend fun account(id: Long): Account - suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List - - /** - * ブロックæ“作を行ㆠ- * - * @param userid ブロックæ“作を行ã£ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼id - * @param target ブロック対象ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼id - * @return ブロック後ã®ãƒ–ロック対象ユーザーã¨ã®[Relationship] - */ - suspend fun block(userid: Long, target: Long): Relationship - suspend fun unblock(userid: Long, target: Long): Relationship - suspend fun unfollow(userid: Long, target: Long): Relationship - suspend fun removeFromFollowers(userid: Long, target: Long): Relationship - suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account - - suspend fun followRequests( - loginUser: Long, - withIgnore: Boolean, - pageByMaxId: Page.PageByMaxId - ): PaginationList - - suspend fun acceptFollowRequest(loginUser: Long, target: Long): Relationship - suspend fun rejectFollowRequest(loginUser: Long, target: Long): Relationship - suspend fun mute(userid: Long, target: Long): Relationship - suspend fun unmute(userid: Long, target: Long): Relationship - suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList -} - -@Service -class AccountApiServiceImpl( - private val accountService: AccountService, - private val transaction: Transaction, - private val userService: UserService, - private val statusQueryService: StatusQueryService, - private val relationshipService: RelationshipService, - private val relationshipRepository: RelationshipRepository, - private val mediaService: MediaService -) : - AccountApiService { - - override suspend fun accountsStatuses( - userid: Long, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - loginUser: Long?, - page: Page - ): PaginationList { - val canViewFollowers = if (loginUser == null) { - false - } else if (loginUser == userid) { - true - } else { - transaction.transaction { - isFollowing(loginUser, userid) - } - } - - return transaction.transaction { - statusQueryService.accountsStatus( - accountId = userid, - onlyMedia = onlyMedia, - excludeReplies = excludeReplies, - excludeReblogs = excludeReblogs, - pinned = pinned, - tagged = tagged, - includeFollowers = canViewFollowers, - page = page - ) - } - } - - override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction { - userService.updateUserStatistics(userid) - - val account = accountService.findById(userid) - from(account) - } - - override suspend fun registerAccount(userCreateDto: UserCreateDto) { - userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password)) - } - - override suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship = transaction.transaction { - relationshipService.followRequest(loginUser, followTargetUserId) - - return@transaction fetchRelationship(loginUser, followTargetUserId) - } - - override suspend fun account(id: Long): Account { - return try { - transaction.transaction { - userService.updateUserStatistics(id) - return@transaction accountService.findById(id) - } - } catch (_: UserNotFoundException) { - throw AccountNotFoundException.ofId(id) - } - } - - override suspend fun relationships(userid: Long, id: List, withSuspended: Boolean): List = - transaction.transaction { - if (id.isEmpty()) { - return@transaction emptyList() - } - - logger.warn("id is too long! ({}) truncate to 20", id.size) - - val subList = id.subList(0, min(id.size, 20)) - - return@transaction subList.map { - fetchRelationship(userid, it) - } - } - - override suspend fun block(userid: Long, target: Long) = transaction.transaction { - relationshipService.block(userid, target) - - fetchRelationship(userid, target) - } - - override suspend fun unblock(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.unblock(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun unfollow(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.unfollow(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun removeFromFollowers(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.rejectFollowRequest(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account = - transaction.transaction { - val avatarMedia = if (updateCredentials?.avatar != null) { - mediaService.uploadLocalMedia( - MediaRequest( - updateCredentials.avatar, - null, - null, - null - ) - ) - } else { - null - } - - val headerMedia = if (updateCredentials?.header != null) { - mediaService.uploadLocalMedia( - MediaRequest( - updateCredentials.header, - null, - null, - null - ) - ) - } else { - null - } - - val account = accountService.findById(userid) - - val updateUserDto = UpdateUserDto( - screenName = updateCredentials?.displayName ?: account.displayName, - description = updateCredentials?.note ?: account.note, - avatarMedia = avatarMedia, - headerMedia = headerMedia, - locked = updateCredentials?.locked ?: account.locked, - autoAcceptFolloweeFollowRequest = false - ) - userService.updateUser(userid, updateUserDto) - - accountService.findById(userid) - } - - override suspend fun followRequests( - loginUser: Long, - withIgnore: Boolean, - pageByMaxId: Page.PageByMaxId - ): PaginationList = transaction.transaction { - val request = - relationshipRepository.findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - loginUser, - true, - withIgnore, - pageByMaxId - ) - val actorIds = request.map { it.actorId } - - return@transaction PaginationList(accountService.findByIds(actorIds), request.next, request.prev) - } - - override suspend fun acceptFollowRequest(loginUser: Long, target: Long): Relationship = transaction.transaction { - relationshipService.acceptFollowRequest(loginUser, target) - - return@transaction fetchRelationship(loginUser, target) - } - - override suspend fun rejectFollowRequest(loginUser: Long, target: Long): Relationship = transaction.transaction { - relationshipService.rejectFollowRequest(loginUser, target) - - return@transaction fetchRelationship(loginUser, target) - } - - override suspend fun mute(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.mute(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun unmute(userid: Long, target: Long): Relationship = transaction.transaction { - relationshipService.mute(userid, target) - - return@transaction fetchRelationship(userid, target) - } - - override suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList { - val mutedAccounts = relationshipRepository.findByActorIdAndMuting(userid, true, pageByMaxId) - - return PaginationList( - accountService.findByIds(mutedAccounts.map { it.targetActorId }), - mutedAccounts.next, - mutedAccounts.prev - ) - } - - private fun from(account: Account): CredentialAccount { - return CredentialAccount( - id = account.id, - username = account.username, - acct = account.acct, - url = account.url, - displayName = account.displayName, - note = account.note, - avatar = account.avatar, - avatarStatic = account.avatarStatic, - header = account.header, - headerStatic = account.headerStatic, - locked = account.locked, - fields = account.fields, - emojis = account.emojis, - bot = account.bot, - group = account.group, - discoverable = account.discoverable, - createdAt = account.createdAt, - lastStatusAt = account.lastStatusAt, - statusesCount = account.statusesCount, - followersCount = account.followersCount, - noindex = account.noindex, - moved = account.moved, - suspendex = account.suspendex, - limited = account.limited, - followingCount = account.followingCount, - source = AccountSource( - account.note, - account.fields, - AccountSource.Privacy.public, - false, - 0 - ), - role = Role(0, "Admin", "", 32) - ) - } - - private suspend fun fetchRelationship(userid: Long, targetId: Long): Relationship { - val relationship = relationshipRepository.findByUserIdAndTargetUserId(userid, targetId) - ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = userid, - targetActorId = targetId, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - val inverseRelationship = relationshipRepository.findByUserIdAndTargetUserId(targetId, userid) - ?: dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = targetId, - targetActorId = userid, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - - userService.updateUserStatistics(userid) - userService.updateUserStatistics(targetId) - - return Relationship( - id = targetId.toString(), - following = relationship.following, - showingReblogs = true, - notifying = false, - followedBy = inverseRelationship.following, - blocking = relationship.blocking, - blockedBy = inverseRelationship.blocking, - muting = relationship.muting, - mutingNotifications = relationship.muting, - requested = relationship.followRequest, - domainBlocking = false, - endorsed = false, - note = "" - ) - } - - private suspend fun isFollowing(userid: Long, target: Long): Boolean = - relationshipRepository.findByUserIdAndTargetUserId(userid, target)?.following ?: false - - companion object { - private val logger = LoggerFactory.getLogger(AccountApiServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt deleted file mode 100644 index e5a51f47..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountService.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.account - -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.mastodon.query.AccountQueryService -import org.springframework.stereotype.Service - -@Service -interface AccountService { - suspend fun findById(id: Long): Account - suspend fun findByIds(ids: List): List -} - -@Service -class AccountServiceImpl( - private val accountQueryService: AccountQueryService -) : AccountService { - override suspend fun findById(id: Long): Account = - accountQueryService.findById(id) ?: throw IllegalArgumentException("Account $id not found.") - - override suspend fun findByIds(ids: List): List = accountQueryService.findByIds(ids) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt deleted file mode 100644 index 3127d169..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/app/AppApiService.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.app - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator -import dev.usbharu.hideout.domain.mastodon.model.generated.Application -import dev.usbharu.hideout.domain.mastodon.model.generated.AppsRequest -import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.security.oauth2.core.AuthorizationGrantType -import org.springframework.security.oauth2.core.ClientAuthenticationMethod -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository -import org.springframework.security.oauth2.server.authorization.settings.ClientSettings -import org.springframework.security.oauth2.server.authorization.settings.TokenSettings -import org.springframework.stereotype.Service -import java.time.Duration -import java.util.* - -@Service -interface AppApiService { - suspend fun createApp(appsRequest: AppsRequest): Application -} - -@Service -class AppApiServiceImpl( - private val registeredClientRepository: RegisteredClientRepository, - private val secureTokenGenerator: SecureTokenGenerator, - private val passwordEncoder: PasswordEncoder, - private val transaction: Transaction -) : AppApiService { - override suspend fun createApp(appsRequest: AppsRequest): Application { - return transaction.transaction { - val id = UUID.randomUUID().toString() - val clientSecret = secureTokenGenerator.generate() - val registeredClient = RegisteredClient.withId(id) - .clientId(id) - .clientSecret(passwordEncoder.encode(clientSecret)) - .clientName(appsRequest.clientName) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - .redirectUri(appsRequest.redirectUris) - .tokenSettings( - TokenSettings.builder() - .accessTokenTimeToLive( - Duration.ofSeconds(31536000000) - ) - .build() - ) - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .scopes { it.addAll(parseScope(appsRequest.scopes.orEmpty())) } - .build() - registeredClientRepository.save(registeredClient) - - Application( - name = appsRequest.clientName, - vapidKey = "invalid-vapid-key", - website = appsRequest.website, - clientId = id, - clientSecret = clientSecret, - redirectUri = appsRequest.redirectUris - ) - } - } - - private fun parseScope(string: String): Set = string.split(" ").toSet() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt deleted file mode 100644 index 0f4c4157..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/filter/MastodonFilterApiService.kt +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterAction.hide -import dev.usbharu.hideout.core.domain.model.filter.FilterAction.warn -import dev.usbharu.hideout.core.domain.model.filter.FilterMode -import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.filter.FilterType.* -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import dev.usbharu.hideout.core.query.model.FilterQueryService -import dev.usbharu.hideout.core.service.filter.MuteService -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequest.FilterAction -import dev.usbharu.hideout.domain.mastodon.model.generated.V1Filter.Context -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.runBlocking -import org.springframework.stereotype.Service - -@Suppress("TooManyFunctions") -interface MastodonFilterApiService { - fun v1Filters(userId: Long): Flow - - suspend fun deleteV1FilterById(userId: Long, id: Long) - - suspend fun getV1FilterById(userId: Long, id: Long): V1Filter? - - suspend fun createByV1Filter(userId: Long, v1FilterRequest: V1FilterPostRequest): V1Filter - - fun filterKeywords(userId: Long, filterId: Long): Flow - - suspend fun addKeyword(userId: Long, filterId: Long, keyword: FilterKeywordsPostRequest): FilterKeyword - - fun filterStatuses(userId: Long, filterId: Long): Flow - - suspend fun addFilterStatus(userId: Long, filterId: Long, filterStatusRequest: FilterStatusRequest): FilterStatus - - fun filters(userId: Long): Flow - - suspend fun deleteById(userId: Long, filterId: Long) - - suspend fun getById(userId: Long, filterId: Long): Filter? - - suspend fun deleteKeyword(userId: Long, keywordId: Long) - - suspend fun getKeywordById(userId: Long, keywordId: Long): FilterKeyword? - - suspend fun createFilter(userId: Long, filterPostRequest: FilterPostRequest): Filter - - suspend fun deleteFilterStatusById(userId: Long, filterPostsId: Long) - - suspend fun getFilterStatusById(userId: Long, filterPostsId: Long): FilterStatus? -} - -@Service -class MastodonFilterApiServiceImpl( - private val muteService: MuteService, - private val filterQueryService: FilterQueryService, - private val filterRepository: FilterRepository, - private val filterKeywordRepository: FilterKeywordRepository -) : MastodonFilterApiService { - override fun v1Filters(userId: Long): Flow { - return runBlocking { filterQueryService.findByUserId(userId) }.flatMap { filterQueryModel -> - filterQueryModel.keywords.map { - V1Filter( - id = it.id.toString(), - phrase = it.keyword, - context = filterQueryModel.context.map { filterType -> - when (filterType) { - home -> Context.home - notifications -> Context.notifications - public -> Context.public - thread -> Context.thread - account -> Context.account - } - }, - expiresAt = null, - irreversible = false, - wholeWord = (it.mode != FilterMode.WHOLE_WORD).not() - ) - } - }.asFlow() - } - - override suspend fun deleteV1FilterById(userId: Long, id: Long) { - val keywordId = filterQueryService.findByUserIdAndKeywordId(userId, id)?.keywords?.singleOrNull()?.id ?: return - - filterKeywordRepository.deleteById(keywordId) - } - - override suspend fun getV1FilterById(userId: Long, id: Long): V1Filter? { - val filterQueryModel = filterQueryService.findByUserIdAndKeywordId(userId, id) ?: return null - - val filterKeyword = filterQueryModel.keywords.firstOrNull() ?: return null - - return v1Filter(filterQueryModel, filterKeyword) - } - - private fun v1Filter( - filterQueryModel: FilterQueryModel, - filterKeyword: dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword - ) = V1Filter( - id = filterQueryModel.id.toString(), - phrase = filterKeyword.keyword, - context = filterQueryModel.context.map { - when (it) { - home -> Context.home - notifications -> Context.notifications - public -> Context.public - thread -> Context.thread - account -> Context.account - } - }, - expiresAt = null, - irreversible = false, - wholeWord = filterKeyword.mode == FilterMode.WHOLE_WORD - ) - - override suspend fun createByV1Filter(userId: Long, v1FilterRequest: V1FilterPostRequest): V1Filter { - val createFilter = muteService.createFilter( - title = v1FilterRequest.phrase, - context = v1FilterRequest.context.map { - when (it) { - V1FilterPostRequest.Context.home -> home - V1FilterPostRequest.Context.notifications -> notifications - V1FilterPostRequest.Context.public -> public - V1FilterPostRequest.Context.thread -> thread - V1FilterPostRequest.Context.account -> account - } - }, - action = warn, - keywords = listOf( - dev.usbharu.hideout.core.service.filter.FilterKeyword( - v1FilterRequest.phrase, - if (v1FilterRequest.wholeWord == true) { - FilterMode.WHOLE_WORD - } else { - FilterMode.NONE - } - ) - ), - loginUser = userId - ) - - return v1Filter(createFilter, createFilter.keywords.first()) - } - - override fun filterKeywords(userId: Long, filterId: Long): Flow = - runBlocking { filterQueryService.findByUserIdAndId(userId, filterId) } - ?.keywords - ?.map { - toFilterKeyword( - it - ) - } - .orEmpty() - .asFlow() - - override suspend fun addKeyword(userId: Long, filterId: Long, keyword: FilterKeywordsPostRequest): FilterKeyword { - val id = filterQueryService.findByUserIdAndId(userId, filterId)?.id - ?: throw IllegalArgumentException("filter not found.") - - val filterKeyword = filterKeywordRepository.save( - dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword( - id = filterKeywordRepository.generateId(), - filterId = id, - keyword = keyword.keyword, - mode = if (keyword.regex == true) { - FilterMode.REGEX - } else if (keyword.wholeWord == true) { - FilterMode.WHOLE_WORD - } else { - FilterMode.NONE - } - ) - ) - - return toFilterKeyword(filterKeyword) - } - - override fun filterStatuses(userId: Long, filterId: Long): Flow = emptyFlow() - - override suspend fun addFilterStatus( - userId: Long, - filterId: Long, - filterStatusRequest: FilterStatusRequest - ): FilterStatus { - TODO() - } - - override fun filters(userId: Long): Flow = - runBlocking { filterQueryService.findByUserId(userId) }.map { filterQueryModel -> - toFilter(filterQueryModel) - }.asFlow() - - private fun toFilter(filterQueryModel: FilterQueryModel) = Filter( - id = filterQueryModel.id.toString(), - title = filterQueryModel.name, - context = filterQueryModel.context.map { - when (it) { - home -> Filter.Context.home - notifications -> Filter.Context.notifications - public -> Filter.Context.public - thread -> Filter.Context.thread - account -> Filter.Context.account - } - }, - expiresAt = null, - filterAction = when (filterQueryModel.filterAction) { - warn -> Filter.FilterAction.warn - hide -> Filter.FilterAction.hide - }, - keywords = filterQueryModel.keywords.map { - toFilterKeyword(it) - }, - statuses = null - ) - - private fun toFilterKeyword(it: dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword) = FilterKeyword( - it.id.toString(), - it.keyword, - it.mode == FilterMode.WHOLE_WORD - ) - - override suspend fun deleteById(userId: Long, filterId: Long) = - filterRepository.deleteByUserIdAndId(userId, filterId) - - override suspend fun getById(userId: Long, filterId: Long): Filter? = - filterQueryService.findByUserIdAndId(userId, filterId)?.let { toFilter(it) } - - override suspend fun deleteKeyword(userId: Long, keywordId: Long) { - val id = filterQueryService.findByUserIdAndKeywordId(userId, keywordId)?.keywords?.singleOrNull()?.id ?: return - - filterKeywordRepository.deleteById(id) - } - - override suspend fun getKeywordById(userId: Long, keywordId: Long): FilterKeyword? { - return filterQueryService - .findByUserIdAndKeywordId(userId, keywordId) - ?.keywords - ?.firstOrNull() - ?.let { toFilterKeyword(it) } - } - - override suspend fun createFilter(userId: Long, filterPostRequest: FilterPostRequest): Filter { - val keywords = filterPostRequest.keywordsAttributes.orEmpty().map { - dev.usbharu.hideout.core.service.filter.FilterKeyword( - it.keyword, - if (it.regex == true) { - FilterMode.REGEX - } else if (it.wholeWord == true) { - FilterMode.WHOLE_WORD - } else { - FilterMode.NONE - } - ) - } - return toFilter( - muteService.createFilter( - title = filterPostRequest.title, - context = filterPostRequest.context.map { - when (it) { - FilterPostRequest.Context.home -> home - FilterPostRequest.Context.notifications -> notifications - FilterPostRequest.Context.public -> public - FilterPostRequest.Context.thread -> thread - FilterPostRequest.Context.account -> account - } - }, - action = when (filterPostRequest.filterAction) { - FilterAction.warn -> warn - FilterAction.hide -> warn - null -> warn - }, - keywords = keywords, - loginUser = userId - ) - ) - } - - override suspend fun deleteFilterStatusById(userId: Long, filterPostsId: Long) = Unit - - override suspend fun getFilterStatusById(userId: Long, filterPostsId: Long): FilterStatus? = null -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt deleted file mode 100644 index d24fe791..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.instance - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import org.springframework.stereotype.Service - -@Service -interface InstanceApiService { - suspend fun v1Instance(): V1Instance -} - -@Service -class InstanceApiServiceImpl(private val applicationConfig: ApplicationConfig) : InstanceApiService { - @Suppress("LongMethod") - override suspend fun v1Instance(): V1Instance { - val url = applicationConfig.url - return V1Instance( - uri = url.host, - title = "Hideout Server", - shortDescription = "Hideout test server", - description = "This server is operated for testing of Hideout." + - " We are not responsible for any events that occur when associating with this server", - email = "i@usbharu.dev", - version = "0.0.1", - urls = V1InstanceUrls("wss://${url.host}"), - stats = V1InstanceStats(1, 0, 0), - thumbnail = null, - languages = listOf("ja-JP"), - registrations = false, - approvalRequired = false, - invitesEnabled = false, - configuration = V1InstanceConfiguration( - accounts = V1InstanceConfigurationAccounts(1), - statuses = V1InstanceConfigurationStatuses( - 300, - 4, - 23 - ), - mediaAttachments = V1InstanceConfigurationMediaAttachments( - emptyList(), - 0, - 0, - 0, - 0 - ), - polls = V1InstanceConfigurationPolls( - 0, - 0, - 0, - 0 - ) - ), - contactAccount = Account( - id = "0", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = false, - createdAt = "0", - lastStatusAt = "0", - statusesCount = 1, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - rules = emptyList() - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt deleted file mode 100644 index 8e806d4d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiService.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.media - -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import org.springframework.stereotype.Service - -@Service -interface MediaApiService { - suspend fun postMedia(mediaRequest: MediaRequest): MediaAttachment -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt deleted file mode 100644 index 05f70e8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/media/MediaApiServiceImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.media - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import org.springframework.stereotype.Service - -@Service -class MediaApiServiceImpl(private val mediaService: MediaService, private val transaction: Transaction) : - MediaApiService { - - override suspend fun postMedia(mediaRequest: MediaRequest): MediaAttachment { - return transaction.transaction { - val uploadLocalMedia = mediaService.uploadLocalMedia(mediaRequest) - val type = when (uploadLocalMedia.type) { - FileType.Image -> MediaAttachment.Type.image - FileType.Video -> MediaAttachment.Type.video - FileType.Audio -> MediaAttachment.Type.audio - FileType.Unknown -> MediaAttachment.Type.unknown - } - return@transaction MediaAttachment( - id = uploadLocalMedia.id.toString(), - type = type, - url = uploadLocalMedia.url, - previewUrl = uploadLocalMedia.thumbnailUrl, - description = mediaRequest.description, - blurhash = uploadLocalMedia.blurHash, - textUrl = uploadLocalMedia.url - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt deleted file mode 100644 index d632e04c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/MastodonNotificationStore.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.notification - -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.notification.Notification -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.service.notification.NotificationStore -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Component - -@Component -class MastodonNotificationStore(private val mastodonNotificationRepository: MastodonNotificationRepository) : - NotificationStore { - override suspend fun publishNotification( - notification: Notification, - user: Actor, - sourceActor: Actor?, - post: Post?, - reaction: Reaction? - ): Boolean { - val notificationType = when (notification.type) { - "mention" -> NotificationType.mention - "post" -> NotificationType.status - "repost" -> NotificationType.reblog - "follow" -> NotificationType.follow - "follow-request" -> NotificationType.follow_request - "reaction" -> NotificationType.favourite - else -> { - logger.debug("Notification type does not support. type: {}", notification.type) - return false - } - } - - if (notification.sourceActorId == null) { - logger.debug("Notification does not support. notification.sourceActorId is null") - return false - } - - val mastodonNotification = MastodonNotification( - id = notification.id, - userId = notification.userId, - type = notificationType, - createdAt = notification.createdAt, - accountId = notification.sourceActorId, - statusId = notification.postId, - reportId = null, - relationshipServeranceEvent = null - ) - - mastodonNotificationRepository.save(mastodonNotification) - - return true - } - - override suspend fun unpulishNotification(notificationId: Long): Boolean { - mastodonNotificationRepository.deleteById(notificationId) - return true - } - - companion object { - private val logger = LoggerFactory.getLogger(MastodonNotificationStore::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt deleted file mode 100644 index 32c762c6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.notification - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.NotificationType - -interface NotificationApiService { - - suspend fun notifications( - loginUser: Long, - types: List, - excludeTypes: List, - accountId: List, - page: Page - ): PaginationList - - suspend fun fingById(loginUser: Long, notificationId: Long): Notification? - - suspend fun clearAll(loginUser: Long) - - suspend fun dismiss(loginUser: Long, notificationId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt deleted file mode 100644 index 52caed8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.notification - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import dev.usbharu.hideout.mastodon.domain.model.NotificationType.* -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import dev.usbharu.hideout.mastodon.service.account.AccountService -import org.springframework.stereotype.Service - -@Service -class NotificationApiServiceImpl( - private val mastodonNotificationRepository: MastodonNotificationRepository, - private val transaction: Transaction, - private val accountService: AccountService, - private val statusQueryService: StatusQueryService -) : - NotificationApiService { - - override suspend fun notifications( - loginUser: Long, - types: List, - excludeTypes: List, - accountId: List, - page: Page - ): PaginationList = transaction.transaction { - val typesTmp = mutableListOf() - - typesTmp.addAll(types) - typesTmp.removeAll(excludeTypes) - - val mastodonNotifications = - mastodonNotificationRepository.findByUserIdAndInTypesAndInSourceActorId( - loginUser, - typesTmp, - accountId, - page - ) - - val accounts = accountService.findByIds( - mastodonNotifications.map { - it.accountId - } - ).associateBy { it.id.toLong() } - - val statuses = statusQueryService.findByPostIds(mastodonNotifications.mapNotNull { it.statusId }) - .associateBy { it.id.toLong() } - - val notifications = mastodonNotifications.map { - Notification( - id = it.id.toString(), - type = convertNotificationType(it.type), - createdAt = it.createdAt.toString(), - account = accounts.getValue(it.accountId), - status = statuses[it.statusId], - report = null, - relationshipSeveranceEvent = null - ) - } - - return@transaction PaginationList(notifications, mastodonNotifications.next, mastodonNotifications.prev) - } - - override suspend fun fingById(loginUser: Long, notificationId: Long): Notification? { - val findById = mastodonNotificationRepository.findById(notificationId) ?: return null - - if (findById.userId != loginUser) { - return null - } - - val account = accountService.findById(findById.accountId) - val status = findById.statusId?.let { statusQueryService.findByPostId(it) } - - return Notification( - id = findById.id.toString(), - type = convertNotificationType(findById.type), - createdAt = findById.createdAt.toString(), - account = account, - status = status, - report = null, - relationshipSeveranceEvent = null - ) - } - - override suspend fun clearAll(loginUser: Long) { - mastodonNotificationRepository.deleteByUserId(loginUser) - } - - override suspend fun dismiss(loginUser: Long, notificationId: Long) { - mastodonNotificationRepository.deleteByUserIdAndId(loginUser, notificationId) - } - - private fun convertNotificationType(notificationType: NotificationType): Notification.Type = - when (notificationType) { - mention -> Notification.Type.mention - status -> Notification.Type.status - reblog -> Notification.Type.reblog - follow -> Notification.Type.follow - follow_request -> Notification.Type.follow - favourite -> Notification.Type.follow_request - poll -> Notification.Type.poll - update -> Notification.Type.update - admin_sign_up -> Notification.Type.adminPeriodSign_up - admin_report -> Notification.Type.adminPeriodReport - severed_relationships -> Notification.Type.severed_relationships - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt deleted file mode 100644 index 07ae96c8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/status/StatusesApiService.kt +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.status - -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments -import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.post.PostCreateDto -import dev.usbharu.hideout.core.service.post.PostService -import dev.usbharu.hideout.core.service.reaction.ReactionService -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.domain.mastodon.model.generated.Status.Visibility.* -import dev.usbharu.hideout.mastodon.domain.exception.StatusNotFoundException -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest -import dev.usbharu.hideout.mastodon.interfaces.api.status.toPostVisibility -import dev.usbharu.hideout.mastodon.interfaces.api.status.toStatusVisibility -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import dev.usbharu.hideout.mastodon.service.account.AccountService -import dev.usbharu.hideout.util.EmojiUtil -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -interface StatusesApiService { - suspend fun postStatus( - statusesRequest: StatusesRequest, - userId: Long, - ): Status - - suspend fun findById( - id: Long, - userId: Long?, - ): Status - - suspend fun emojiReactions( - postId: Long, - userId: Long, - emojiName: String, - ): Status - - suspend fun removeEmojiReactions( - postId: Long, - userId: Long, - emojiName: String, - ): Status -} - -@Service -@Suppress("LongParameterList") -class StatsesApiServiceImpl( - private val postService: PostService, - private val accountService: AccountService, - private val mediaRepository: MediaRepository, - private val transaction: Transaction, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val statusQueryService: StatusQueryService, - private val relationshipRepository: RelationshipRepository, - private val reactionService: ReactionService, - private val emojiService: EmojiService, -) : - StatusesApiService { - override suspend fun postStatus( - statusesRequest: StatusesRequest, - userId: Long, - ): Status = transaction.transaction { - logger.debug("START create post by mastodon api. {}", statusesRequest) - - val post = postService.createLocal( - PostCreateDto( - text = statusesRequest.status.orEmpty(), - overview = statusesRequest.spoiler_text, - visibility = statusesRequest.visibility.toPostVisibility(), - repolyId = statusesRequest.in_reply_to_id?.toLong(), - userId = userId, - mediaIds = statusesRequest.media_ids.map { it.toLong() } - ) - ) - val account = accountService.findById(userId) - - val replyUser = if (post.replyId != null) { - val findById = postRepository.findById(post.replyId) - if (findById == null) { - null - } else { - actorRepository.findById(findById.actorId)?.id - } - } else { - null - } - - // TODO: n+1解消 - val mediaAttachment = post.mediaIds.mapNotNull { mediaId -> - mediaRepository.findById(mediaId) - }.map { - it.toMediaAttachments() - } - - Status( - id = post.id.toString(), - uri = post.apId, - createdAt = Instant.ofEpochMilli(post.createdAt).toString(), - account = account, - content = post.text, - visibility = statusesRequest.visibility.toStatusVisibility(), - sensitive = post.sensitive, - spoilerText = post.overview.orEmpty(), - mediaAttachments = mediaAttachment, - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = post.url, - inReplyToId = post.replyId?.toString(), - inReplyToAccountId = replyUser?.toString(), - language = null, - text = post.text, - editedAt = null, - ) - } - - override suspend fun findById(id: Long, userId: Long?): Status = transaction.transaction { - val status = statusQueryService.findByPostId(id) ?: statusNotFound(id) - - return@transaction status(status, userId) - } - - private fun accessDenied(id: String): Nothing { - logger.debug("Access Denied $id") - throw StatusNotFoundException.ofId(id.toLong()) - } - - private fun statusNotFound(id: Long): Nothing { - logger.debug("Status Not Found $id") - throw StatusNotFoundException.ofId(id) - } - - private suspend fun status( - status: Status, - userId: Long?, - ): Status { - return when (status.visibility) { - public -> status - unlisted -> status - private -> { - if (userId == null) { - accessDenied(status.id) - } - - val relationship = - relationshipRepository.findByUserIdAndTargetUserId(userId, status.account.id.toLong()) - ?: accessDenied(status.id) - if (relationship.following) { - return status - } - accessDenied(status.id) - } - - direct -> accessDenied(status.id) - } - } - - override suspend fun emojiReactions(postId: Long, userId: Long, emojiName: String): Status { - status(statusQueryService.findByPostId(postId) ?: statusNotFound(postId), userId) - - val emoji = try { - if (EmojiUtil.isEmoji(emojiName)) { - UnicodeEmoji(emojiName) - } else { - emojiService.findByEmojiName(emojiName)!! - } - } catch (_: IllegalStateException) { - UnicodeEmoji("â¤") - } catch (_: NullPointerException) { - UnicodeEmoji("â¤") - } - reactionService.sendReaction(emoji, userId, postId) - return statusQueryService.findByPostId(postId) ?: statusNotFound(postId) - } - - override suspend fun removeEmojiReactions(postId: Long, userId: Long, emojiName: String): Status { - reactionService.removeReaction(userId, postId) - - return status(statusQueryService.findByPostId(postId) ?: statusNotFound(postId), userId) - } - - companion object { - private val logger = LoggerFactory.getLogger(StatusesApiService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt deleted file mode 100644 index 3bfd728d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.timeline - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.service.timeline.GenerateTimelineService -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import org.springframework.stereotype.Service - -@Suppress("LongParameterList") -interface TimelineApiService { - - suspend fun publicTimeline( - localOnly: Boolean = false, - remoteOnly: Boolean = false, - mediaOnly: Boolean = false, - page: Page - ): PaginationList - - suspend fun homeTimeline( - userId: Long, - page: Page - ): PaginationList -} - -@Service -class TimelineApiServiceImpl( - private val generateTimelineService: GenerateTimelineService, - private val transaction: Transaction -) : TimelineApiService { - - override suspend fun publicTimeline( - localOnly: Boolean, - remoteOnly: Boolean, - mediaOnly: Boolean, - page: Page - ): PaginationList = transaction.transaction { - return@transaction generateTimelineService.getTimeline(forUserId = 0, localOnly, mediaOnly, page) - } - - override suspend fun homeTimeline(userId: Long, page: Page): PaginationList = - transaction.transaction { - return@transaction generateTimelineService.getTimeline(forUserId = userId, page = page) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt deleted file mode 100644 index 36c8f322..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/AcctUtil.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import dev.usbharu.hideout.core.domain.model.actor.Acct - -object AcctUtil { - fun parse(string: String): Acct { - if (string.isBlank()) { - throw IllegalArgumentException("Invalid acct.(Blank)") - } - return when (string.count { c -> c == '@' }) { - 0 -> { - Acct(string) - } - - 1 -> { - if (string.startsWith("@")) { - Acct(string.substring(1 until string.length)) - } else { - Acct(string.substringBefore("@"), string.substringAfter("@")) - } - } - - 2 -> { - if (string.startsWith("@")) { - val substring = string.substring(1 until string.length) - val userName = substring.substringBefore("@") - val domain = substring.substringAfter("@") - Acct( - userName, - domain.ifBlank { null } - ) - } else { - throw IllegalArgumentException("Invalid acct.(@ are in the wrong position)") - } - } - - else -> { - throw IllegalArgumentException("Invalid acct. (Too many @)") - } - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt index 9396a079..f211ffff 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt @@ -23,8 +23,6 @@ import dev.usbharu.hideout.activitypub.domain.model.Like import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityType import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt index fbe5ffcf..ab8fee9d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.activitypub.service.activity.follow import dev.usbharu.hideout.activitypub.domain.model.Follow import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.service.follow.SendFollowDto import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.mockito.kotlin.eq diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt index 7c71a55a..bb96522a 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt @@ -16,7 +16,6 @@ package dev.usbharu.hideout.activitypub.service.common -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.service.resource.InMemoryCacheManager import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt index 87bfbae4..33e745a8 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt @@ -32,11 +32,8 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.HtmlSanitizeConfig import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter -import dev.usbharu.hideout.core.service.post.PostService import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.plugins.* diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt index aab94666..c1daea13 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt @@ -18,12 +18,9 @@ package dev.usbharu.hideout.core.service.notification import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.notification.Notification import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt index 80cbb571..8d7c1676 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt @@ -21,11 +21,7 @@ import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteServi import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.HtmlSanitizeConfig import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.post.Post -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.service.timeline.TimelineService import jakarta.validation.Validation import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt index e619f0f0..c02bfa10 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt @@ -20,7 +20,6 @@ package dev.usbharu.hideout.core.service.reaction import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.Reaction import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.service.notification.NotificationService diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index 4af69e07..0d82e8b5 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -21,10 +21,7 @@ import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowServi import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.service.follow.SendFollowDto import dev.usbharu.hideout.core.service.notification.NotificationService import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt index e5806fc5..29db21c2 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt @@ -17,12 +17,9 @@ package dev.usbharu.hideout.core.service.timeline import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository 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.TimelineRepository -import dev.usbharu.hideout.core.query.FollowerQueryService import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt index 6a574dc6..bfd88778 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt @@ -21,16 +21,11 @@ package dev.usbharu.hideout.core.service.user import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.domain.model.actor.Actor -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository import dev.usbharu.hideout.core.domain.model.instance.Instance -import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.service.instance.InstanceService -import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.owl.producer.api.OwlProducer import jakarta.validation.Validation import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index de1477c6..e1676e7a 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -19,12 +19,7 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.relationship.RelationshipService -import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship import dev.usbharu.hideout.domain.mastodon.model.generated.Status diff --git a/hideout-core/src/test/kotlin/utils/PostBuilder.kt b/hideout-core/src/test/kotlin/utils/PostBuilder.kt index 4ddd2e89..41b9f746 100644 --- a/hideout-core/src/test/kotlin/utils/PostBuilder.kt +++ b/hideout-core/src/test/kotlin/utils/PostBuilder.kt @@ -19,7 +19,6 @@ package utils import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.config.HtmlSanitizeConfig import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.model.post.Visibility import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter import jakarta.validation.Validation diff --git a/hideout-core/src/test/kotlin/utils/UserBuilder.kt b/hideout-core/src/test/kotlin/utils/UserBuilder.kt index 0aff8e44..fd929c81 100644 --- a/hideout-core/src/test/kotlin/utils/UserBuilder.kt +++ b/hideout-core/src/test/kotlin/utils/UserBuilder.kt @@ -19,7 +19,6 @@ package utils import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CharacterLimit import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.actor.Actor import jakarta.validation.Validation import kotlinx.coroutines.runBlocking import java.net.URL diff --git a/hideout-mastodon/build.gradle.kts b/hideout-mastodon/build.gradle.kts new file mode 100644 index 00000000..45be2756 --- /dev/null +++ b/hideout-mastodon/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") version "1.9.23" +} + +group = "dev.usbharu" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/hideout-mastodon/gradle/wrapper/gradle-wrapper.jar b/hideout-mastodon/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/hideout-mastodon/gradlew.bat b/hideout-mastodon/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/hideout-mastodon/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/hideout-mastodon/settings.gradle.kts b/hideout-mastodon/settings.gradle.kts new file mode 100644 index 00000000..c023f31a --- /dev/null +++ b/hideout-mastodon/settings.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" +} +rootProject.name = "hideout-mastodon" + diff --git a/hideout-mastodon/src/main/kotlin/Main.kt b/hideout-mastodon/src/main/kotlin/Main.kt new file mode 100644 index 00000000..27f6ee1a --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/Main.kt @@ -0,0 +1,5 @@ +package dev.usbharu + +fun main() { + println("Hello World!") +} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt index b557e259..7d939be0 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverAcceptTask import dev.usbharu.hideout.core.external.job.DeliverAcceptTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt index 7780d00d..9d880986 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverCreateTask import dev.usbharu.hideout.core.external.job.DeliverCreateTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt index 92961616..9ce3dad7 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverDeleteTask import dev.usbharu.hideout.core.external.job.DeliverDeleteTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt index 6408a73c..4867056a 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverReactionTask import dev.usbharu.hideout.core.external.job.DeliverReactionTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt index 7558197b..73179f07 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverRejectTask import dev.usbharu.hideout.core.external.job.DeliverRejectTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt index c811d2a6..949435ba 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.DeliverUndoTask import dev.usbharu.hideout.core.external.job.DeliverUndoTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt index 458f4f4c..7839bc01 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt @@ -19,10 +19,8 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.ReceiveFollowTask import dev.usbharu.hideout.core.external.job.ReceiveFollowTaskDef -import dev.usbharu.hideout.core.service.relationship.RelationshipService import dev.usbharu.owl.consumer.AbstractTaskRunner import dev.usbharu.owl.consumer.TaskRequest import dev.usbharu.owl.consumer.TaskResult diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt index f25b8dce..9faec4c6 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt @@ -20,7 +20,6 @@ import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.external.job.UpdateActorTask import dev.usbharu.hideout.core.external.job.UpdateActorTaskDef -import dev.usbharu.hideout.core.service.post.PostService import dev.usbharu.owl.consumer.AbstractTaskRunner import dev.usbharu.owl.consumer.TaskRequest import dev.usbharu.owl.consumer.TaskResult From e13858e06816f94fd4e8316609662cccfd52e98c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 1 Jun 2024 20:47:36 +0900 Subject: [PATCH 11/54] wip --- .../interfaces/api/auth/AuthController.kt | 1 - .../core/service/auth/RecaptchaResult.kt | 25 -------- .../core/service/auth/RegisterAccountDto.kt | 23 ------- .../core/service/user/RemoteUserCreateDto.kt | 33 ---------- .../core/service/user/UpdateUserDto.kt | 28 --------- .../core/service/user/UserAuthService.kt | 29 --------- .../core/service/user/UserAuthServiceImpl.kt | 61 ------------------- 7 files changed, 200 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index bd61adac..22b6fd61 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -18,7 +18,6 @@ package dev.usbharu.hideout.core.interfaces.api.auth import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.CaptchaConfig -import dev.usbharu.hideout.core.service.auth.RegisterAccountDto import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.validation.annotation.Validated diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt deleted file mode 100644 index 956fbcd9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RecaptchaResult.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.auth - -data class RecaptchaResult( - val success: Boolean, - val challenge_ts: String, - val hostname: String, - val score: Float, - val action: String -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt deleted file mode 100644 index c44c291c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/auth/RegisterAccountDto.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.auth - -data class RegisterAccountDto( - val username: String, - val password: String, - val recaptchaResponse: String, -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt deleted file mode 100644 index e02c259c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -data class RemoteUserCreateDto( - val name: String, - val domain: String, - val screenName: String, - val description: String, - val inbox: String, - val outbox: String, - val url: String, - val publicKey: String, - val keyId: String, - val followers: String?, - val following: String?, - val sharedInbox: String?, - val locked: Boolean? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt deleted file mode 100644 index 93e4959c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.core.domain.model.media.Media - -data class UpdateUserDto( - val screenName: String, - val description: String, - val avatarMedia: Media?, - val headerMedia: Media?, - val locked: Boolean, - val autoAcceptFolloweeFollowRequest: Boolean -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthService.kt deleted file mode 100644 index ee69cca9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthService.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import org.springframework.stereotype.Service -import java.security.KeyPair - -@Service -interface UserAuthService { - fun hash(password: String): String - - suspend fun usernameAlreadyUse(username: String): Boolean - - suspend fun generateKeyPair(): KeyPair -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt deleted file mode 100644 index 77c90ace..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserAuthServiceImpl.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.application.config.ApplicationConfig -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder -import org.springframework.stereotype.Service -import java.security.* -import java.util.* - -@Service -class UserAuthServiceImpl( - private val actorRepository: ActorRepository, - private val applicationConfig: ApplicationConfig -) : UserAuthService { - - override fun hash(password: String): String = BCryptPasswordEncoder().encode(password) - - override suspend fun usernameAlreadyUse(username: String): Boolean { - actorRepository.findByNameAndDomain(username, applicationConfig.url.host) ?: return false - return true - } - - override suspend fun generateKeyPair(): KeyPair { - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(keySize) - return keyPairGenerator.generateKeyPair() - } - - companion object { - val sha256: MessageDigest = MessageDigest.getInstance("SHA-256") - const val keySize: Int = 2048 - const val pemSize: Int = 64 - } -} - -fun PublicKey.toPem(): String { - return "-----BEGIN PUBLIC KEY-----\n" + - Base64.getEncoder().encodeToString(encoded).chunked(UserAuthServiceImpl.pemSize).joinToString("\n") + - "\n-----END PUBLIC KEY-----\n" -} - -fun PrivateKey.toPem(): String { - return "-----BEGIN PRIVATE KEY-----\n" + - Base64.getEncoder().encodeToString(encoded).chunked(UserAuthServiceImpl.pemSize).joinToString("\n") + - "\n-----END PRIVATE KEY-----\n" -} From 922bdb4991e35155b8563aed4d1be623bb933763 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:04:30 +0900 Subject: [PATCH 12/54] wip --- .../activitypub/webfinger/WebFingerTest.kt | 2 +- .../kotlin/mastodon/status/StatusTest.kt | 2 - .../intTest/kotlin/util/TestTransaction.kt | 2 +- ...WithHttpSignatureSecurityContextFactory.kt | 2 +- .../application/config/SecurityConfig.kt | 2 +- .../application/external/OwlProducerRunner.kt | 1 - .../exposed/ExposedTransaction.kt | 2 +- .../application/service/init/MetaService.kt | 2 - .../service/init/MetaServiceImpl.kt | 5 +- .../init/ServerInitialiseServiceImpl.kt | 80 ------ .../DeleteLocalActorApplicationService.kt | 4 +- .../MigrationLocalActorApplicationService.kt | 4 +- .../actor/RegisterLocalActor.kt | 2 +- .../RegisterLocalActorApplicationService.kt | 4 +- ...AlsoKnownAsLocalActorApplicationService.kt | 2 +- .../SuspendLocalActorApplicationService.kt | 4 +- .../UnsuspendLocalActorApplicationService.kt | 4 +- .../post/DeleteLocalPostApplicationService.kt | 2 +- .../post/RegisterLocalPost.kt | 2 +- .../RegisterLocalPostApplicationService.kt | 2 +- .../post/UpdateLocalNote.kt | 2 +- .../post/UpdateLocalNoteApplicationService.kt | 4 +- .../application/shared}/Transaction.kt | 2 +- .../core/domain/model/emoji/CustomEmoji.kt | 17 +- .../core/domain/model/filter/Filter.kt | 25 -- .../core/domain/model/filter/FilterAction.kt | 23 -- .../domain/model/filter/FilterRepository.kt | 30 --- .../core/domain/model/filter/FilterType.kt | 26 -- .../model/filterkeyword/FilterKeyword.kt | 26 -- .../filterkeyword/FilterKeywordRepository.kt | 26 -- .../core/domain/model/instance/Nodeinfo.kt | 54 ---- .../core/domain/model/instance/Nodeinfo2_0.kt | 87 ------- .../model}/media/FileType.kt | 2 +- .../hideout/core/domain/model/media/Media.kt | 33 +-- .../FilterMode.kt => media/MediaBlurHash.kt} | 9 +- .../model/media/MediaDescription.kt} | 8 +- .../{meta/Meta.kt => media/MediaName.kt} | 5 +- .../model}/media/MimeType.kt | 2 +- .../hideout/core/domain/model/meta/Jwt.kt | 21 -- .../core/domain/model/meta/MetaRepository.kt | 27 -- .../domain/model/notification/Notification.kt | 30 --- .../notification/NotificationRepository.kt | 24 -- .../core/domain/model/reaction/Reaction.kt | 21 -- .../model/reaction/ReactionRepository.kt | 40 --- .../domain/model/relationship/Relationship.kt | 38 --- .../RelationshipRepositoryImpl.kt | 178 -------------- .../model/shared/domainevent/DomainEvent.kt | 37 --- .../shared/domainevent/DomainEventBody.kt | 23 -- .../core/domain/model/timeline/Timeline.kt | 42 ---- .../model/timeline/TimelineRepository.kt | 25 -- .../ExposedTimelineRepository.kt | 2 - .../exposedrepository/MediaRepositoryImpl.kt | 5 +- .../exposedrepository/MetaRepositoryImpl.kt | 2 - .../ReactionRepositoryImpl.kt | 232 ------------------ .../MongoTimelineRepository.kt | 35 --- .../MongoTimelineRepositoryWrapper.kt | 67 ----- .../HttpSignatureUserDetailsService.kt | 2 +- ...xposedOAuth2AuthorizationConsentService.kt | 2 +- .../ExposedOAuth2AuthorizationService.kt | 2 +- .../oauth2/UserDetailsServiceImpl.kt | 2 +- .../interfaces/api/auth/AuthController.kt | 8 +- .../query/model/ExposedFilterQueryService.kt | 76 ------ .../core/query/model/FilterQueryModel.kt | 43 ---- .../core/query/model/FilterQueryService.kt | 26 -- .../core/service/filter/FilterKeyword.kt | 24 -- .../core/service/filter/FilterResult.kt | 24 -- .../core/service/filter/MuteService.kt | 33 --- .../core/service/filter/MuteServiceImpl.kt | 66 ----- .../service/instance/InstanceCreateDto.kt | 27 -- .../core/service/instance/InstanceService.kt | 124 ---------- ...ApatcheTikaFileTypeDeterminationService.kt | 105 -------- .../media/FileTypeDeterminationService.kt | 24 -- .../media/LocalFileSystemMediaDataStore.kt | 131 ---------- .../service/media/MediaBlurhashService.kt | 23 -- .../service/media/MediaBlurhashServiceImpl.kt | 26 -- .../core/service/media/MediaDataStore.kt | 47 ---- .../service/media/MediaFileRenameService.kt | 30 --- .../hideout/core/service/media/MediaSave.kt | 49 ---- .../core/service/media/MediaSaveRequest.kt | 26 -- .../core/service/media/MediaService.kt | 25 -- .../core/service/media/MediaServiceImpl.kt | 218 ---------------- .../core/service/media/ProcessedFile.kt | 40 --- .../core/service/media/ProcessedMedia.kt | 22 -- .../core/service/media/ProcessedMediaPath.kt | 26 -- .../hideout/core/service/media/RemoteMedia.kt | 24 -- .../media/RemoteMediaDownloadService.kt | 23 -- .../media/RemoteMediaDownloadServiceImpl.kt | 61 ----- .../core/service/media/S3MediaDataStore.kt | 139 ----------- .../hideout/core/service/media/SavedMedia.kt | 87 ------- .../service/media/ThumbnailGenerateService.kt | 24 -- .../media/ThumbnailGenerateServiceImpl.kt | 42 ---- .../media/UUIDMediaFileRenameService.kt | 32 --- .../service/media/converter/MediaConverter.kt | 26 -- .../media/converter/MediaConverterRoot.kt | 30 --- .../media/converter/MediaConverterRootImpl.kt | 48 ---- .../media/converter/MediaProcessService.kt | 42 ---- .../converter/MediaProcessServiceImpl.kt | 74 ------ .../image/ImageMediaProcessService.kt | 131 ---------- .../image/ImageMediaProcessorConfiguration.kt | 33 --- .../movie/MovieMediaProcessService.kt | 143 ----------- .../notification/NotificationRequest.kt | 136 ---------- .../notification/NotificationService.kt | 24 -- .../notification/NotificationServiceImpl.kt | 110 --------- ...lationshipNotificationManagementService.kt | 23 -- ...onshipNotificationManagementServiceImpl.kt | 26 -- .../post/DefaultPostContentFormatter.kt | 99 -------- .../core/service/post/PostContentFormatter.kt | 21 -- .../core/service/post/PostCreateDto.kt | 29 --- .../core/service/reaction/ReactionService.kt | 28 --- .../service/reaction/ReactionServiceImpl.kt | 101 -------- .../core/service/resource/CacheManager.kt | 22 -- .../service/resource/InMemoryCacheManager.kt | 71 ------ .../service/resource/KtorResolveResponse.kt | 66 ----- .../resource/KtorResourceResolveService.kt | 53 ---- .../core/service/resource/ResolveResponse.kt | 28 --- .../resource/ResourceResolveService.kt | 21 -- .../ExposedGenerateTimelineService.kt | 68 ----- .../timeline/GenerateTimelineService.kt | 34 --- .../timeline/MongoGenerateTimelineService.kt | 89 ------- .../core/service/user/UserCreateDto.kt | 24 -- .../usbharu/hideout/util/CollectionUtil.kt | 29 --- .../dev/usbharu/hideout/util/HttpUtil.kt | 54 ---- .../usbharu/hideout/util/InstantParseUtil.kt | 34 --- .../dev/usbharu/hideout/util/LruCache.kt | 35 --- .../dev/usbharu/hideout/util/RsaUtil.kt | 5 - .../dev/usbharu/hideout/util/ServerUtil.kt | 22 -- .../dev/usbharu/hideout/util/TempFileUtil.kt | 39 --- .../service/init/MetaServiceImplTest.kt | 3 - .../init/ServerInitialiseServiceImplTest.kt | 73 ------ ...ipNotificationManagementServiceImplTest.kt | 2 +- .../RelationshipServiceImplTest.kt | 2 +- .../service/timeline/TimelineServiceTest.kt | 2 - .../core/service/user/ActorServiceTest.kt | 1 - .../account/AccountApiServiceImplTest.kt | 2 +- .../src/test/kotlin/utils/TestTransaction.kt | 2 +- .../hideout/worker/DeliverAcceptTaskRunner.kt | 2 +- .../hideout/worker/DeliverCreateTaskRunner.kt | 2 +- .../hideout/worker/DeliverRejectTaskRunner.kt | 2 +- .../hideout/worker/DeliverUndoTaskRunner.kt | 2 +- .../usbharu/hideout/worker/InboxTaskRunner.kt | 3 +- .../hideout/worker/ReceiveFollowTaskRunner.kt | 2 +- .../hideout/worker/UpdateActorWorker.kt | 2 +- 142 files changed, 72 insertions(+), 4891 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImpl.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/DeleteLocalActorApplicationService.kt (91%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/MigrationLocalActorApplicationService.kt (94%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/RegisterLocalActor.kt (93%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/RegisterLocalActorApplicationService.kt (96%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/SetAlsoKnownAsLocalActorApplicationService.kt (77%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/SuspendLocalActorApplicationService.kt (91%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/actor/UnsuspendLocalActorApplicationService.kt (90%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/post/DeleteLocalPostApplicationService.kt (95%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/post/RegisterLocalPost.kt (94%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/post/RegisterLocalPostApplicationService.kt (97%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/post/UpdateLocalNote.kt (93%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{usecase => application}/post/UpdateLocalNoteApplicationService.kt (93%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application/external => core/application/shared}/Transaction.kt (94%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterType.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeyword.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeywordRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{service => domain/model}/media/FileType.kt (92%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/{filter/FilterMode.kt => media/MediaBlurHash.kt} (83%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{service/post/FormattedPostContent.kt => domain/model/media/MediaDescription.kt} (82%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/{meta/Meta.kt => media/MediaName.kt} (85%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{service => domain/model}/media/MimeType.kt (92%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Jwt.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/MetaRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/NotificationRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/Timeline.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/TimelineRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/ExposedFilterQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryModel.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterKeyword.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterResult.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileTypeDeterminationService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaFileRenameService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSaveRequest.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMedia.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMediaPath.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMedia.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/SavedMedia.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/UUIDMediaFileRenameService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverter.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRoot.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRootImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatter.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostCreateDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserCreateDto.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/CollectionUtil.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/InstantParseUtil.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/ServerUtil.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/util/TempFileUtil.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImplTest.kt diff --git a/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt b/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt index a12bbfd4..e7532b18 100644 --- a/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt +++ b/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt @@ -17,7 +17,7 @@ package activitypub.webfinger import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.owl.producer.api.OwlProducer import kotlinx.coroutines.runBlocking import org.flywaydb.core.Flyway diff --git a/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt b/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt index 817d5dfc..d3028a0d 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt @@ -20,8 +20,6 @@ import dev.usbharu.hideout.SpringApplication import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction import dev.usbharu.owl.producer.api.OwlProducer import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat diff --git a/hideout-core/src/intTest/kotlin/util/TestTransaction.kt b/hideout-core/src/intTest/kotlin/util/TestTransaction.kt index 72c3b42b..0f8b6317 100644 --- a/hideout-core/src/intTest/kotlin/util/TestTransaction.kt +++ b/hideout-core/src/intTest/kotlin/util/TestTransaction.kt @@ -16,7 +16,7 @@ package util -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction object TestTransaction : Transaction { override suspend fun transaction(block: suspend () -> T): T = block() diff --git a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt index f20da72f..ab161ca7 100644 --- a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt +++ b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt @@ -16,7 +16,7 @@ package util -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser import dev.usbharu.httpsignature.common.HttpHeaders diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index d51ae754..837a261c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -25,7 +25,7 @@ import com.nimbusds.jose.jwk.source.JWKSource import com.nimbusds.jose.proc.SecurityContext import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer import dev.usbharu.hideout.activitypub.domain.model.StringOrObject -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt index ca86bce1..5df20246 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt @@ -35,7 +35,6 @@ class OwlProducerRunner(private val owlProducer: OwlProducer, private val taskDe } override fun destroy() { - System.err.println("destroy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") runBlocking { owlProducer.stop() } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt index bdc84fba..8e380cb6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.application.infrastructure.exposed -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import kotlinx.coroutines.runBlocking import kotlinx.coroutines.slf4j.MDCContext import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt index 6d394617..32615016 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt @@ -16,8 +16,6 @@ package dev.usbharu.hideout.application.service.init -import dev.usbharu.hideout.core.domain.model.meta.Jwt -import dev.usbharu.hideout.core.domain.model.meta.Meta import org.springframework.stereotype.Service @Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt index b801ba1e..3ca63744 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt @@ -16,11 +16,8 @@ package dev.usbharu.hideout.application.service.init -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.exception.NotInitException -import dev.usbharu.hideout.core.domain.model.meta.Jwt -import dev.usbharu.hideout.core.domain.model.meta.Meta -import dev.usbharu.hideout.core.domain.model.meta.MetaRepository import org.springframework.stereotype.Service @Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImpl.kt deleted file mode 100644 index 323b78d7..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImpl.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.service.init - -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.model.meta.Jwt -import dev.usbharu.hideout.core.domain.model.meta.Meta -import dev.usbharu.hideout.core.domain.model.meta.MetaRepository -import dev.usbharu.hideout.util.ServerUtil -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.security.KeyPairGenerator -import java.util.* - -@Service -class ServerInitialiseServiceImpl( - private val metaRepository: MetaRepository, - private val transaction: Transaction -) : - ServerInitialiseService { - - val logger: Logger = LoggerFactory.getLogger(ServerInitialiseServiceImpl::class.java) - - override suspend fun init() { - transaction.transaction { - val savedMeta = metaRepository.get() - val implementationVersion = ServerUtil.getImplementationVersion() - if (wasInitialised(savedMeta).not()) { - logger.info("Start Initialise") - initialise(implementationVersion) - logger.info("Finish Initialise") - return@transaction - } - - if (isVersionChanged(requireNotNull(savedMeta))) { - logger.info("Version changed!! (${savedMeta.version} -> $implementationVersion)") - updateVersion(savedMeta, implementationVersion) - } - } - } - - private fun wasInitialised(meta: Meta?): Boolean { - logger.debug("Initialise checking...") - return meta != null - } - - private fun isVersionChanged(meta: Meta): Boolean = meta.version != ServerUtil.getImplementationVersion() - - private suspend fun initialise(implementationVersion: String) { - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val generateKeyPair = keyPairGenerator.generateKeyPair() - val jwt = Jwt( - UUID.randomUUID(), - Base64.getEncoder().encodeToString(generateKeyPair.private.encoded), - Base64.getEncoder().encodeToString(generateKeyPair.public.encoded) - ) - val meta = Meta(implementationVersion, jwt) - metaRepository.save(meta) - } - - private suspend fun updateVersion(meta: Meta, version: String) { - metaRepository.save(meta.copy(version = version)) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt similarity index 91% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt index be22d34d..cc6300c5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/DeleteLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt similarity index 94% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt index a4f38e1c..0ff97802 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActor.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActor.kt index 8031a87c..b54f2504 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor data class RegisterLocalActor( val name: String, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt similarity index 96% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index d93a7c0a..d0cc63a7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.service.id.IdGenerateService +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt similarity index 77% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt index 772385d8..fe69e97d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SetAlsoKnownAsLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt @@ -1,4 +1,4 @@ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt similarity index 91% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt index 08c78483..ab07d9ed 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/SuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt similarity index 90% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt index 962e9e2b..1948b0ea 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/actor/UnsuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.actor +package dev.usbharu.hideout.core.application.actor -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt similarity index 95% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt index 80fd74ca..46388563 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/DeleteLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.post +package dev.usbharu.hideout.core.application.post import dev.usbharu.hideout.core.domain.model.post.Post2Repository import dev.usbharu.hideout.core.domain.model.post.PostId diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt similarity index 94% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt index 025991d9..b5a6740b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPost.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.post +package dev.usbharu.hideout.core.application.post import dev.usbharu.hideout.core.domain.model.post.Visibility diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt similarity index 97% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index e08bffb2..99049b92 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.post +package dev.usbharu.hideout.core.application.post import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt index 318c010a..a8dcfb1a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNote.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.post +package dev.usbharu.hideout.core.application.post data class UpdateLocalNote( val postId: Long, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt index d7aaa905..5df3f5f9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/usecase/post/UpdateLocalNoteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.usecase.post +package dev.usbharu.hideout.core.application.post -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.Post2Repository import dev.usbharu.hideout.core.domain.model.post.PostId diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/Transaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/Transaction.kt similarity index 94% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/Transaction.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/Transaction.kt index f08e257f..5dc4df1b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/Transaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/Transaction.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.external +package dev.usbharu.hideout.core.application.shared import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt index b50b29df..34240b2e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt @@ -16,10 +16,13 @@ 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 java.net.URI import java.time.Instant sealed class Emoji { - abstract val domain: String + abstract val domain: Domain abstract val name: String @Suppress("FunctionMinLength") @@ -33,13 +36,13 @@ sealed class Emoji { } data class CustomEmoji( - val id: Long, + val id: EmojiId, override val name: String, - override val domain: String, - val instanceId: Long?, - val url: String, + override val domain: Domain, + val instanceId: InstanceId, + val url: URI, val category: String?, - val createdAt: Instant + val createdAt: Instant, ) : Emoji() { override fun id(): String = id.toString() } @@ -47,6 +50,6 @@ data class CustomEmoji( data class UnicodeEmoji( override val name: String ) : Emoji() { - override val domain: String = "unicode.org" + override val domain: Domain = Domain("unicode.org") override fun id(): String = name } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt deleted file mode 100644 index 10bffd91..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.filter - -data class Filter( - val id: Long, - val userId: Long, - val name: String, - val context: List, - val filterAction: FilterAction, -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt deleted file mode 100644 index 6eca79d8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.filter - -@Suppress("EnumEntryNameCase", "EnumNaming") -enum class FilterAction { - warn, - hide -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt deleted file mode 100644 index ef0ba01b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.filter - -interface FilterRepository { - - suspend fun generateId(): Long - suspend fun save(filter: Filter): Filter - suspend fun findById(id: Long): Filter? - - suspend fun findByUserIdAndId(userId: Long, id: Long): Filter? - suspend fun findByUserIdAndType(userId: Long, types: List): List - suspend fun deleteById(id: Long) - - suspend fun deleteByUserIdAndId(userId: Long, id: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterType.kt deleted file mode 100644 index be815999..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterType.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.filter - -@Suppress("EnumEntryNameCase", "EnumNaming") -enum class FilterType { - home, - notifications, - public, - thread, - account -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeyword.kt deleted file mode 100644 index d6457116..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeyword.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.filterkeyword - -import dev.usbharu.hideout.core.domain.model.filter.FilterMode - -data class FilterKeyword( - val id: Long, - val filterId: Long, - val keyword: String, - val mode: FilterMode -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeywordRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeywordRepository.kt deleted file mode 100644 index eae3bdde..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filterkeyword/FilterKeywordRepository.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.filterkeyword - -interface FilterKeywordRepository { - suspend fun generateId(): Long - suspend fun save(filterKeyword: FilterKeyword): FilterKeyword - suspend fun saveAll(filterKeywordList: List) - suspend fun findById(id: Long): FilterKeyword? - suspend fun deleteById(id: Long) - suspend fun deleteByFilterId(filterId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt deleted file mode 100644 index d1c60cd7..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.instance - -class Nodeinfo private constructor() { - - var links: List = emptyList() - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Nodeinfo - - return links == other.links - } - - override fun hashCode(): Int = links.hashCode() -} - -class Links private constructor() { - var rel: String? = null - var href: String? = null - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Links - - if (rel != other.rel) return false - if (href != other.href) return false - - return true - } - - override fun hashCode(): Int { - var result = rel?.hashCode() ?: 0 - result = 31 * result + (href?.hashCode() ?: 0) - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt deleted file mode 100644 index efa0da33..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("Filename") - -package dev.usbharu.hideout.core.domain.model.instance - -@Suppress("ClassNaming") -class Nodeinfo2_0 { - var metadata: Metadata? = null - var software: Software? = null - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Nodeinfo2_0 - - if (metadata != other.metadata) return false - if (software != other.software) return false - - return true - } - - override fun hashCode(): Int { - var result = metadata?.hashCode() ?: 0 - result = 31 * result + (software?.hashCode() ?: 0) - return result - } -} - -class Metadata { - var nodeName: String? = null - var nodeDescription: String? = null - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Metadata - - if (nodeName != other.nodeName) return false - if (nodeDescription != other.nodeDescription) return false - - return true - } - - override fun hashCode(): Int { - var result = nodeName?.hashCode() ?: 0 - result = 31 * result + (nodeDescription?.hashCode() ?: 0) - return result - } -} - -class Software { - var name: String? = null - var version: String? = null - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Software - - if (name != other.name) return false - if (version != other.version) return false - - return true - } - - override fun hashCode(): Int { - var result = name?.hashCode() ?: 0 - result = 31 * result + (version?.hashCode() ?: 0) - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/FileType.kt similarity index 92% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileType.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/FileType.kt index 111b85a7..9d2b8837 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileType.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/FileType.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.service.media +package dev.usbharu.hideout.core.domain.model.media enum class FileType { Image, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt index fe9f2883..06b5c92f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt @@ -16,34 +16,17 @@ package dev.usbharu.hideout.core.domain.model.media -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MimeType -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment +import java.net.URI data class Media( - val id: Long, - val name: String, - val url: String, - val remoteUrl: String?, - val thumbnailUrl: String?, + val id: MediaId, + val name: MediaName, + val url: URI, + val remoteUrl: URI?, + val thumbnailUrl: URI?, val type: FileType, val mimeType: MimeType, - val blurHash: String?, - val description: String? = null + val blurHash: MediaBlurHash?, + val description: MediaDescription? = null, ) -fun Media.toMediaAttachments(): MediaAttachment = MediaAttachment( - id = id.toString(), - type = when (type) { - FileType.Image -> MediaAttachment.Type.image - FileType.Video -> MediaAttachment.Type.video - FileType.Audio -> MediaAttachment.Type.audio - FileType.Unknown -> MediaAttachment.Type.unknown - }, - url = url, - previewUrl = thumbnailUrl, - remoteUrl = remoteUrl, - description = description, - blurhash = blurHash, - textUrl = url -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt similarity index 83% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt index 10a20ec5..ceee5d85 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt @@ -14,10 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.domain.model.filter +package dev.usbharu.hideout.core.domain.model.media -enum class FilterMode { - WHOLE_WORD, - REGEX, - NONE -} +@JvmInline +value class MediaBlurHash(val hash: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/FormattedPostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaDescription.kt similarity index 82% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/FormattedPostContent.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaDescription.kt index 4dd62908..c99345b0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/FormattedPostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaDescription.kt @@ -14,9 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.service.post +package dev.usbharu.hideout.core.domain.model.media -data class FormattedPostContent( - val html: String, - val content: String -) +@JvmInline +value class MediaDescription(val description: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Meta.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaName.kt similarity index 85% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Meta.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaName.kt index e26594ae..58c483ac 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Meta.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaName.kt @@ -14,6 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.domain.model.meta +package dev.usbharu.hideout.core.domain.model.media -data class Meta(val version: String, val jwt: Jwt) +@JvmInline +value class MediaName(val name: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MimeType.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MimeType.kt similarity index 92% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MimeType.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MimeType.kt index 0194f3b5..95cdcd99 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MimeType.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MimeType.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.service.media +package dev.usbharu.hideout.core.domain.model.media data class MimeType(val type: String, val subtype: String, val fileType: FileType) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Jwt.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Jwt.kt deleted file mode 100644 index 0893efe5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/Jwt.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.meta - -import java.util.* - -data class Jwt(val kid: UUID, val privateKey: String, val publicKey: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/MetaRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/MetaRepository.kt deleted file mode 100644 index 31807823..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/meta/MetaRepository.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.meta - -import org.springframework.stereotype.Repository - -@Repository -interface MetaRepository { - - suspend fun save(meta: Meta) - - suspend fun get(): Meta? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt deleted file mode 100644 index d97f9264..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/Notification.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.notification - -import java.time.Instant - -data class Notification( - val id: Long, - val type: String, - val userId: Long, - val sourceActorId: Long?, - val postId: Long?, - val text: String?, - val reactionId: Long?, - val createdAt: Instant -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/NotificationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/NotificationRepository.kt deleted file mode 100644 index 24d557db..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/notification/NotificationRepository.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.notification - -interface NotificationRepository { - suspend fun generateId(): Long - suspend fun save(notification: Notification): Notification - suspend fun findById(id: Long): Notification? - suspend fun deleteById(id: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt deleted file mode 100644 index e497788b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.reaction - -import dev.usbharu.hideout.core.domain.model.emoji.Emoji - -data class Reaction(val id: Long, val emoji: Emoji, val postId: Long, val actorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt deleted file mode 100644 index 493fcdac..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.reaction - -import dev.usbharu.hideout.core.domain.model.emoji.Emoji -import org.springframework.stereotype.Repository - -@Repository -@Suppress("FunctionMaxLength", "TooManyFunctions") -interface ReactionRepository { - suspend fun generateId(): Long - suspend fun save(reaction: Reaction): Reaction - suspend fun delete(reaction: Reaction): Reaction - suspend fun deleteByPostId(postId: Long): Int - suspend fun deleteByActorId(actorId: Long): Int - suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long) - suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji) - suspend fun findById(id: Long): Reaction? - suspend fun findByPostId(postId: Long): List - suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? - suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean - suspend fun existByPostIdAndActorIdAndUnicodeEmoji(postId: Long, actorId: Long, unicodeEmoji: String): Boolean - suspend fun existByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Boolean - suspend fun existByPostIdAndActor(postId: Long, actorId: Long): Boolean - suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt deleted file mode 100644 index cbdaa3ea..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.relationship - -/** - * ユーザーã¨ã®é–¢ä¿‚を表ã—ã¾ã™ - * - * @property actorId ユーザー - * @property targetActorId 相手ユーザー - * @property following フォローã—ã¦ã„ã‚‹ã‹ - * @property blocking ブロックã—ã¦ã„ã‚‹ã‹ - * @property muting ミュートã—ã¦ã„ã‚‹ã‹ - * @property followRequest フォローリクエストをé€ã£ã¦ã„ã‚‹ã‹ - * @property ignoreFollowRequestToTarget フォローリクエストを無視ã—ã¦ã„ã‚‹ã‹ - */ -data class Relationship( - val actorId: Long, - val targetActorId: Long, - val following: Boolean, - val blocking: Boolean, - val muting: Boolean, - val followRequest: Boolean, - val ignoreFollowRequestToTarget: Boolean -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt deleted file mode 100644 index 5e6b7886..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.relationship - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.withPagination -import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import org.jetbrains.exposed.dao.id.LongIdTable -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun save(relationship: Relationship): Relationship = query { - val singleOrNull = Relationships.selectAll().where { - (Relationships.actorId eq relationship.actorId).and( - Relationships.targetActorId eq relationship.targetActorId - ) - }.forUpdate().singleOrNull() - - if (singleOrNull == null) { - Relationships.insert { - it[actorId] = relationship.actorId - it[targetActorId] = relationship.targetActorId - it[following] = relationship.following - it[blocking] = relationship.blocking - it[muting] = relationship.muting - it[followRequest] = relationship.followRequest - it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget - } - } else { - Relationships.update({ - (Relationships.actorId eq relationship.actorId).and( - Relationships.targetActorId eq relationship.targetActorId - ) - }) { - it[following] = relationship.following - it[blocking] = relationship.blocking - it[muting] = relationship.muting - it[followRequest] = relationship.followRequest - it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget - } - } - return@query relationship - } - - override suspend fun delete(relationship: Relationship): Unit = query { - Relationships.deleteWhere { - (actorId eq relationship.actorId).and( - targetActorId eq relationship.targetActorId - ) - } - } - - override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? = query { - return@query Relationships.selectAll() - .where { (Relationships.actorId eq actorId).and(Relationships.targetActorId eq targetActorId) } - .singleOrNull()?.toRelationships() - } - - override suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long): Unit = query { - Relationships.deleteWhere { - Relationships.actorId.eq(actorId).or(Relationships.targetActorId.eq(targetActorId)) - } - } - - override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List = query { - return@query Relationships - .selectAll().where { Relationships.targetActorId eq targetId and (Relationships.following eq following) } - .map { it.toRelationships() } - } - - override suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int = query { - return@query Relationships - .selectAll() - .where { - Relationships.targetActorId eq targetId and (Relationships.following eq following) - } - .count() - .toInt() - } - - override suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int = query { - return@query Relationships - .selectAll() - .where { - Relationships.actorId eq userId and (Relationships.following eq following) - } - .count() - .toInt() - } - - override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean, - page: Page.PageByMaxId, - ): PaginationList = query { - val query = Relationships.selectAll().where { - Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest)) - .and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest)) - } - - val resultRowList = query.withPagination(page, Relationships.id) - - return@query PaginationList( - resultRowList.map { it.toRelationships() }, - resultRowList.next?.value, - resultRowList.prev?.value - ) - } - - override suspend fun findByActorIdAndMuting( - actorId: Long, - muting: Boolean, - page: Page.PageByMaxId, - ): PaginationList = query { - val query = - Relationships.selectAll().where { Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) } - - val resultRowList = query.withPagination(page, Relationships.id) - - return@query PaginationList( - resultRowList.map { it.toRelationships() }, - resultRowList.next?.value, - resultRowList.prev?.value - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(RelationshipRepositoryImpl::class.java) - } -} - -fun ResultRow.toRelationships(): Relationship = Relationship( - actorId = this[Relationships.actorId], - targetActorId = this[Relationships.targetActorId], - following = this[Relationships.following], - blocking = this[Relationships.blocking], - muting = this[Relationships.muting], - followRequest = this[Relationships.followRequest], - ignoreFollowRequestToTarget = this[Relationships.ignoreFollowRequestFromTarget] -) - -object Relationships : LongIdTable("relationships") { - val actorId = long("actor_id").references(Actors.id) - val targetActorId = long("target_actor_id").references(Actors.id) - val following = bool("following") - val blocking = bool("blocking") - val muting = bool("muting") - val followRequest = bool("follow_request") - val ignoreFollowRequestFromTarget = bool("ignore_follow_request") - - init { - uniqueIndex(actorId, targetActorId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt deleted file mode 100644 index 53c48d86..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEvent.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.shared.domainevent - -import java.time.Instant -import java.util.* - -data class DomainEvent( - private val id: String, - private val name: String, - private val occurredOn: Instant, - private val body: DomainEventBody, -) { - companion object { - fun create(name: String, body: DomainEventBody): DomainEvent { - return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body) - } - - fun reconstruct(id: String, name: String, occurredOn: Instant, body: DomainEventBody): DomainEvent { - return DomainEvent(id, name, occurredOn, body) - } - } -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt deleted file mode 100644 index cb7dd4d9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/domainevent/DomainEventBody.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.shared.domainevent - -abstract class DomainEventBody(val map: Map) { - fun toMap(): Map { - return map - } -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/Timeline.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/Timeline.kt deleted file mode 100644 index 46e4548a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/Timeline.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.timeline - -import dev.usbharu.hideout.core.domain.model.post.Visibility -import org.springframework.data.annotation.Id -import org.springframework.data.mongodb.core.index.CompoundIndex -import org.springframework.data.mongodb.core.mapping.Document - -@Document -@CompoundIndex(def = "{'userId':1,'timelineId':1,'postId':1}", unique = true) -data class Timeline( - @Id - val id: Long, - val userId: Long, - val timelineId: Long, - val postId: Long, - val postActorId: Long, - val createdAt: Long, - val replyId: Long?, - val repostId: Long?, - val visibility: Visibility, - val sensitive: Boolean, - val isLocal: Boolean, - val isPureRepost: Boolean = false, - val mediaIds: List = emptyList(), - val emojiIds: List = emptyList() -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/TimelineRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/TimelineRepository.kt deleted file mode 100644 index 21c8d2ee..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timeline/TimelineRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.timeline - -interface TimelineRepository { - suspend fun generateId(): Long - suspend fun save(timeline: Timeline): Timeline - suspend fun saveAll(timelines: List): List - suspend fun findByUserId(id: Long): List - suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt index eb17a1ef..79733294 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt @@ -18,8 +18,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository import dev.usbharu.hideout.application.service.id.IdGenerateService 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.TimelineRepository import org.jetbrains.exposed.sql.* import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt index 19e5cf8e..8882d480 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt @@ -17,10 +17,9 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository import dev.usbharu.hideout.application.service.id.IdGenerateService +import dev.usbharu.hideout.core.domain.model.media.FileType import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.mimeType -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MimeType +import dev.usbharu.hideout.core.domain.model.media.MimeType import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.slf4j.Logger diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt index c9d76578..c70ec0d8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt @@ -16,8 +16,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.core.domain.model.meta.Jwt -import dev.usbharu.hideout.core.domain.model.meta.MetaRepository import org.jetbrains.exposed.sql.* import org.springframework.stereotype.Repository import java.util.* diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt deleted file mode 100644 index e9be4342..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ReactionRepositoryImpl.kt +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji -import dev.usbharu.hideout.core.domain.model.emoji.Emoji -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import org.jetbrains.exposed.dao.id.LongIdTable -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 ReactionRepositoryImpl( - private val idGenerateService: IdGenerateService -) : ReactionRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(reaction: Reaction): Reaction = query { - if (Reactions.selectAll().where { Reactions.id eq reaction.id }.forUpdate().empty()) { - Reactions.insert { - it[id] = reaction.id - if (reaction.emoji is CustomEmoji) { - it[customEmojiId] = reaction.emoji.id - it[unicodeEmoji] = null - } else { - it[customEmojiId] = null - it[unicodeEmoji] = reaction.emoji.name - } - it[postId] = reaction.postId - it[actorId] = reaction.actorId - } - } else { - Reactions.update({ Reactions.id eq reaction.id }) { - if (reaction.emoji is CustomEmoji) { - it[customEmojiId] = reaction.emoji.id - it[unicodeEmoji] = null - } else { - it[customEmojiId] = null - it[unicodeEmoji] = reaction.emoji.name - } - it[postId] = reaction.postId - it[actorId] = reaction.actorId - } - } - return@query reaction - } - - override suspend fun delete(reaction: Reaction): Reaction = query { - if (reaction.emoji is CustomEmoji) { - Reactions.deleteWhere { - id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId)) - .and(customEmojiId.eq(reaction.emoji.id)) - } - } else { - Reactions.deleteWhere { - id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId)) - .and(unicodeEmoji.eq(reaction.emoji.name)) - } - } - return@query reaction - } - - override suspend fun deleteByPostId(postId: Long): Int = query { - return@query Reactions.deleteWhere { - Reactions.postId eq postId - } - } - - override suspend fun deleteByActorId(actorId: Long): Int = query { - return@query Reactions.deleteWhere { - Reactions.actorId eq actorId - } - } - - override suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long): Unit = query { - Reactions.deleteWhere { - Reactions.postId eq postId and (Reactions.actorId eq actorId) - } - } - - override suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Unit = query { - if (emoji is CustomEmoji) { - Reactions.deleteWhere { - Reactions.postId.eq(postId) - .and(Reactions.actorId.eq(actorId)) - .and(customEmojiId.eq(emoji.id)) - } - } else { - Reactions.deleteWhere { - Reactions.postId.eq(postId) - .and(Reactions.actorId.eq(actorId)) - .and(unicodeEmoji.eq(emoji.name)) - } - } - } - - override suspend fun findById(id: Long): Reaction? = query { - return@query Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.id eq id }.singleOrNull() - ?.toReaction() - } - - override suspend fun findByPostId(postId: Long): List = query { - return@query Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.postId eq postId } - .map { it.toReaction() } - } - - override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? = - query { - return@query Reactions.leftJoin(CustomEmojis).selectAll().where { - Reactions.postId eq postId and (Reactions.actorId eq actorId).and( - Reactions.customEmojiId.eq( - emojiId - ) - ) - }.singleOrNull()?.toReaction() - } - - override suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean = - query { - return@query Reactions.selectAll().where { - Reactions.postId - .eq(postId) - .and(Reactions.actorId.eq(actorId)) - .and(Reactions.customEmojiId.eq(emojiId)) - }.empty().not() - } - - override suspend fun existByPostIdAndActorIdAndUnicodeEmoji( - postId: Long, - actorId: Long, - unicodeEmoji: String - ): Boolean = query { - return@query Reactions.selectAll().where { - Reactions.postId - .eq(postId) - .and(Reactions.actorId.eq(actorId)) - .and(Reactions.unicodeEmoji.eq(unicodeEmoji)) - }.empty().not() - } - - override suspend fun existByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Boolean = query { - val query = Reactions.selectAll().where { - Reactions.postId - .eq(postId) - .and(Reactions.actorId.eq(actorId)) - } - - if (emoji is UnicodeEmoji) { - query.andWhere { Reactions.unicodeEmoji eq emoji.name } - } else { - emoji as CustomEmoji - query.andWhere { Reactions.customEmojiId eq emoji.id } - } - - return@query query.empty().not() - } - - override suspend fun existByPostIdAndActor(postId: Long, actorId: Long): Boolean = query { - Reactions.selectAll().where { Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)) }.empty().not() - } - - override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List = query { - return@query Reactions.leftJoin(CustomEmojis) - .selectAll().where { Reactions.postId eq postId and (Reactions.actorId eq actorId) } - .map { it.toReaction() } - } - - companion object { - private val logger = LoggerFactory.getLogger(ReactionRepositoryImpl::class.java) - } -} - -fun ResultRow.toReaction(): Reaction { - val emoji = if (this[Reactions.customEmojiId] != null) { - CustomEmoji( - id = this[Reactions.customEmojiId]!!, - name = this[CustomEmojis.name], - domain = this[CustomEmojis.domain], - instanceId = this[CustomEmojis.instanceId], - url = this[CustomEmojis.url], - category = this[CustomEmojis.category], - createdAt = this[CustomEmojis.createdAt] - ) - } else if (this[Reactions.unicodeEmoji] != null) { - UnicodeEmoji(this[Reactions.unicodeEmoji]!!) - } else { - throw IllegalStateException("customEmojiId and unicodeEmoji is null.") - } - - return Reaction( - this[Reactions.id].value, - emoji, - this[Reactions.postId], - this[Reactions.actorId] - ) -} - -object Reactions : LongIdTable("reactions") { - val customEmojiId = long("custom_emoji_id").references(CustomEmojis.id).nullable() - val unicodeEmoji = varchar("unicode_emoji", 255).nullable() - val postId: Column = - long("post_id").references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) - val actorId: Column = - long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) - - init { - uniqueIndex(customEmojiId, postId, actorId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepository.kt deleted file mode 100644 index 348d3abd..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepository.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.mongorepository - -import dev.usbharu.hideout.core.domain.model.timeline.Timeline -import org.springframework.data.domain.Pageable -import org.springframework.data.mongodb.repository.MongoRepository - -@Suppress("LongParameterList", "FunctionMaxLength") -interface MongoTimelineRepository : MongoRepository { - fun findByUserId(id: Long): List - fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List - fun findByUserIdAndTimelineIdAndPostIdBetweenAndIsLocal( - userId: Long?, - timelineId: Long?, - postIdMin: Long?, - postIdMax: Long?, - isLocal: Boolean?, - pageable: Pageable - ): List -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt deleted file mode 100644 index 58d36a36..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoTimelineRepositoryWrapper.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.mongorepository - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException -import dev.usbharu.hideout.core.domain.model.timeline.Timeline -import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.dao.DataAccessException -import org.springframework.dao.DuplicateKeyException -import org.springframework.stereotype.Repository - -@Repository -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false) -class MongoTimelineRepositoryWrapper( - private val mongoTimelineRepository: MongoTimelineRepository, - private val idGenerateService: IdGenerateService -) : - TimelineRepository { - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(timeline: Timeline): Timeline { - return withContext(Dispatchers.IO) { - mongoTimelineRepository.save(timeline) - } - } - - override suspend fun saveAll(timelines: List): List { - try { - return mongoTimelineRepository.saveAll(timelines) - } catch (e: DuplicateKeyException) { - throw DuplicateException("Timeline duplicate.", e) - } catch (e: DataAccessException) { - throw ResourceAccessException(e) - } - } - - override suspend fun findByUserId(id: Long): List { - return withContext(Dispatchers.IO) { - mongoTimelineRepository.findByUserId(id) - } - } - - override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List { - return withContext(Dispatchers.IO) { - mongoTimelineRepository.findByUserIdAndTimelineId(userId, timelineId) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index 578da4ad..36c61d4c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpMethod diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt index 4ad94f0e..7c5126e9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import kotlinx.coroutines.runBlocking import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt index 4cb43890..b6689829 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt @@ -19,7 +19,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import kotlinx.coroutines.runBlocking import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt index 6481007b..04b1f298 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import kotlinx.coroutines.runBlocking diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 22b6fd61..0c749ee5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -40,13 +40,7 @@ class AuthController( @PostMapping("/auth/sign_up") suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String { - val registerAccount = authApiService.registerAccount( - RegisterAccountDto( - signUpForm.username, - signUpForm.password, - signUpForm.recaptchaResponse - ) - ) + return "redirect:" + registerAccount.url } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/ExposedFilterQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/ExposedFilterQueryService.kt deleted file mode 100644 index 22defc4d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/ExposedFilterQueryService.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.query.model - -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.infrastructure.exposedrepository.FilterKeywords -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Filters -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toFilter -import dev.usbharu.hideout.core.infrastructure.exposedrepository.toFilterKeyword -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.selectAll -import org.springframework.stereotype.Repository - -@Repository -class ExposedFilterQueryService : FilterQueryService { - override suspend fun findByUserIdAndType(userId: Long, types: List): List { - return Filters - .rightJoin(FilterKeywords) - .selectAll() - .where { Filters.userId eq userId } - .toFilterQueryModel() - } - - override suspend fun findByUserId(userId: Long): List { - return Filters - .rightJoin(FilterKeywords) - .selectAll() - .where { Filters.userId eq userId } - .toFilterQueryModel() - } - - override suspend fun findByUserIdAndId(userId: Long, id: Long): FilterQueryModel? { - return Filters - .leftJoin(FilterKeywords) - .selectAll() - .where { Filters.userId eq userId and (Filters.id eq id) } - .toFilterQueryModel() - .firstOrNull() - } - - override suspend fun findByUserIdAndKeywordId(userId: Long, keywordId: Long): FilterQueryModel? { - return Filters - .leftJoin(FilterKeywords) - .selectAll() - .where { Filters.userId eq userId and (FilterKeywords.id eq keywordId) } - .toFilterQueryModel() - .firstOrNull() - } - - private fun Query.toFilterQueryModel(): List { - return this - .groupBy { it[Filters.id] } - .map { it.value } - .map { - FilterQueryModel.of( - it.first().toFilter(), - it.map { resultRow -> resultRow.toFilterKeyword() } - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryModel.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryModel.kt deleted file mode 100644 index 767649c3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryModel.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.query.model - -import dev.usbharu.hideout.core.domain.model.filter.Filter -import dev.usbharu.hideout.core.domain.model.filter.FilterAction -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword - -data class FilterQueryModel( - val id: Long, - val userId: Long, - val name: String, - val context: List, - val filterAction: FilterAction, - val keywords: List -) { - companion object { - @Suppress("FunctionMinLength") - fun of(filter: Filter, keywords: List): FilterQueryModel = FilterQueryModel( - id = filter.id, - userId = filter.userId, - name = filter.name, - context = filter.context, - filterAction = filter.filterAction, - keywords = keywords - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryService.kt deleted file mode 100644 index a0ad4f93..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/model/FilterQueryService.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.query.model - -import dev.usbharu.hideout.core.domain.model.filter.FilterType - -interface FilterQueryService { - suspend fun findByUserIdAndType(userId: Long, types: List): List - suspend fun findByUserId(userId: Long): List - suspend fun findByUserIdAndId(userId: Long, id: Long): FilterQueryModel? - suspend fun findByUserIdAndKeywordId(userId: Long, keywordId: Long): FilterQueryModel? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterKeyword.kt deleted file mode 100644 index 6c2ebc39..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterKeyword.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterMode - -data class FilterKeyword( - val keyword: String, - val mode: FilterMode -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterResult.kt deleted file mode 100644 index d87e6fea..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/FilterResult.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.query.model.FilterQueryModel - -data class FilterResult( - val filter: FilterQueryModel, - val keyword: String, -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteService.kt deleted file mode 100644 index 63e1ac83..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteService.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterAction -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.query.model.FilterQueryModel - -interface MuteService { - suspend fun createFilter( - title: String, - context: List, - action: FilterAction, - keywords: List, - loginUser: Long - ): FilterQueryModel - - suspend fun getFilters(userId: Long, types: List = emptyList()): List -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImpl.kt deleted file mode 100644 index 27872d7d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImpl.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.Filter -import dev.usbharu.hideout.core.domain.model.filter.FilterAction -import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import dev.usbharu.hideout.core.query.model.FilterQueryService -import org.springframework.stereotype.Service - -@Service -class MuteServiceImpl( - private val filterRepository: FilterRepository, - private val filterKeywordRepository: FilterKeywordRepository, - private val filterQueryService: FilterQueryService -) : MuteService { - override suspend fun createFilter( - title: String, - context: List, - action: FilterAction, - keywords: List, - loginUser: Long - ): FilterQueryModel { - val filter = Filter( - filterRepository.generateId(), - loginUser, - title, - context, - action - ) - - val filterKeywordList = keywords.map { - dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword( - filterKeywordRepository.generateId(), - filter.id, - it.keyword, - it.mode - ) - } - - val savedFilter = filterRepository.save(filter) - - filterKeywordRepository.saveAll(filterKeywordList) - return FilterQueryModel.of(savedFilter, filterKeywordList) - } - - override suspend fun getFilters(userId: Long, types: List): List = - filterQueryService.findByUserIdAndType(userId, types) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt deleted file mode 100644 index f4d04b30..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.instance - -data class InstanceCreateDto( - val name: String?, - val description: String?, - val url: String, - val iconUrl: String, - val sharedInbox: String?, - val software: String?, - val version: String?, -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt deleted file mode 100644 index a9a84f3d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.instance - -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.core.domain.model.instance.Instance -import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository -import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo -import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo2_0 -import dev.usbharu.hideout.core.service.resource.ResourceResolveService -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.net.URL -import java.time.Instant - -interface InstanceService { - suspend fun fetchInstance(url: String, sharedInbox: String? = null): Instance - suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance -} - -@Service -class InstanceServiceImpl( - private val instanceRepository: InstanceRepository, - private val resourceResolveService: ResourceResolveService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, -) : InstanceService { - override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance { - val u = URL(url) - val resolveInstanceUrl = u.protocol + "://" + u.host - - val instance = instanceRepository.findByUrl(resolveInstanceUrl) - - if (instance != null) { - return instance - } - - logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl) - @Suppress("TooGenericExceptionCaught") - try { - val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() - val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) - val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href } - - for ((key, value) in nodeinfoPathMap) { - when (key) { - "http://nodeinfo.diaspora.software/ns/schema/2.0", - "http://nodeinfo.diaspora.software/ns/schema/2.1", - -> { - val nodeinfo20 = objectMapper.readValue( - resourceResolveService.resolve(value!!).bodyAsText(), - Nodeinfo2_0::class.java - ) - - val instanceCreateDto = InstanceCreateDto( - name = nodeinfo20.metadata?.nodeName, - description = nodeinfo20.metadata?.nodeDescription, - url = resolveInstanceUrl, - iconUrl = "$resolveInstanceUrl/favicon.ico", - sharedInbox = sharedInbox, - software = nodeinfo20.software?.name, - version = nodeinfo20.software?.version - ) - return createNewInstance(instanceCreateDto) - } - - else -> { - throw IllegalStateException("Unknown nodeinfo versions: $key url: $value") - } - } - } - } catch (e: Exception) { - logger.warn("FAILED Fetch Instance", e) - } - return createNewInstance( - InstanceCreateDto( - name = null, - description = null, - url = resolveInstanceUrl, - iconUrl = "$resolveInstanceUrl/favicon.ico", - sharedInbox = null, - software = null, - version = null - ) - ) - } - - override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance { - val instance = Instance( - id = instanceRepository.generateId(), - name = instanceCreateDto.name ?: instanceCreateDto.url, - description = instanceCreateDto.description.orEmpty(), - url = instanceCreateDto.url, - iconUrl = instanceCreateDto.iconUrl, - sharedInbox = instanceCreateDto.sharedInbox, - software = instanceCreateDto.software ?: "unknown", - version = instanceCreateDto.version ?: "unknown", - isBlocked = false, - isMuted = false, - moderationNote = "", - createdAt = Instant.now() - ) - instanceRepository.save(instance) - return instance - } - - companion object { - private val logger = LoggerFactory.getLogger(InstanceServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationService.kt deleted file mode 100644 index b4aa4c57..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationService.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import org.apache.tika.Tika -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Component -import java.nio.file.Path - -@Component -class ApatcheTikaFileTypeDeterminationService : FileTypeDeterminationService { - override fun fileType( - byteArray: ByteArray, - filename: String, - contentType: String? - ): MimeType { - logger.info("START Detect file type name: {}", filename) - - val tika = Tika() - - val detect = try { - tika.detect(byteArray, filename) - } catch (e: IllegalStateException) { - logger.warn("FAILED Detect file type", e) - "application/octet-stream" - } - - val type = detect.substringBefore("/") - val fileType = when (type) { - "image" -> { - FileType.Image - } - - "video" -> { - FileType.Video - } - - "audio" -> { - FileType.Audio - } - - else -> { - FileType.Unknown - } - } - val mimeType = MimeType(type, detect.substringAfter("/"), fileType) - - logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType) - return mimeType - } - - override fun fileType(path: Path, filename: String): MimeType { - logger.info("START Detect file type name: {}", filename) - - val tika = Tika() - - val detect = try { - tika.detect(path) - } catch (e: IllegalStateException) { - logger.warn("FAILED Detect file type", e) - "application/octet-stream" - } - - val type = detect.substringBefore("/") - val fileType = when (type) { - "image" -> { - FileType.Image - } - - "video" -> { - FileType.Video - } - - "audio" -> { - FileType.Audio - } - - else -> { - FileType.Unknown - } - } - val mimeType = MimeType(type, detect.substringAfter("/"), fileType) - - logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType) - return mimeType - } - - companion object { - private val logger = LoggerFactory.getLogger(ApatcheTikaFileTypeDeterminationService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileTypeDeterminationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileTypeDeterminationService.kt deleted file mode 100644 index e09a263b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/FileTypeDeterminationService.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import java.nio.file.Path - -interface FileTypeDeterminationService { - fun fileType(byteArray: ByteArray, filename: String, contentType: String?): MimeType - fun fileType(path: Path, filename: String): MimeType -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt deleted file mode 100644 index 7ca382f6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStore.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.LocalStorageConfig -import org.slf4j.LoggerFactory -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.stereotype.Service -import java.nio.file.Path -import java.nio.file.StandardOpenOption -import kotlin.io.path.copyTo -import kotlin.io.path.createDirectories -import kotlin.io.path.deleteIfExists -import kotlin.io.path.outputStream - -/** - * ローカルファイルシステムã«ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’ä¿å­˜ã—ã¾ã™ - * - * @constructor - * ApplicationConfigã¨LocalStorageConfigã‚’ã‚‚ã¨ã«ä½œæˆ - * - * @param applicationConfig ApplicationConfig - * @param localStorageConfig LocalStorageConfig - */ -@Service -@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) -class LocalFileSystemMediaDataStore( - applicationConfig: ApplicationConfig, - localStorageConfig: LocalStorageConfig -) : MediaDataStore { - - private val savePath: Path = Path.of(localStorageConfig.path).toAbsolutePath() - - private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/" - - init { - savePath.createDirectories() - } - - @Suppress("NestedBlockDepth") - override suspend fun save(dataMediaSave: MediaSave): SavedMedia { - val fileSavePath = buildSavePath(savePath, dataMediaSave.name) - val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name) - - dataMediaSave.thumbnailInputStream?.inputStream()?.use { - it.buffered().use { bufferedInputStream -> - thumbnailSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) - .use { outputStream -> - outputStream.buffered().use { - bufferedInputStream.transferTo(it) - } - } - } - } - - dataMediaSave.fileInputStream.inputStream().use { - it.buffered().use { bufferedInputStream -> - fileSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) - .use { outputStream -> outputStream.buffered().use { bufferedInputStream.transferTo(it) } } - } - } - - return SuccessSavedMedia( - dataMediaSave.name, - publicUrl + dataMediaSave.name, - publicUrl + "thumbnail-" + dataMediaSave.name - ) - } - - override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia { - logger.info("START Media upload. {}", dataSaveRequest.name) - val fileSavePath = buildSavePath(savePath, dataSaveRequest.name) - val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataSaveRequest.name) - - val fileSavePathString = fileSavePath.toAbsolutePath().toString() - logger.info("MEDIA save. path: {}", fileSavePathString) - - @Suppress("TooGenericExceptionCaught") - try { - dataSaveRequest.filePath.copyTo(fileSavePath) - dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath) - } catch (e: Exception) { - logger.warn("FAILED to Save the media.", e) - return FaildSavedMedia("FAILED to Save the media.", "Failed copy to path: $fileSavePathString", e) - } - - logger.info("SUCCESS Media upload. {}", dataSaveRequest.name) - return SuccessSavedMedia( - dataSaveRequest.name, - publicUrl + dataSaveRequest.name, - publicUrl + "thumbnail-" + dataSaveRequest.name - ) - } - - /** - * メディアを削除ã—ã¾ã™ã€‚サムãƒã‚¤ãƒ«ã‚‚削除ã•ã‚Œã¾ã™ã€‚ - * - * @param id 削除ã™ã‚‹ãƒ¡ãƒ‡ã‚£ã‚¢ã®id [SuccessSavedMedia.name]を指定ã—ã¾ã™ã€‚ - */ - override suspend fun delete(id: String) { - logger.info("START Media delete. id: {}", id) - @Suppress("TooGenericExceptionCaught") - try { - buildSavePath(savePath, id).deleteIfExists() - buildSavePath(savePath, "thumbnail-$id").deleteIfExists() - } catch (e: Exception) { - logger.warn("FAILED Media delete. id: {}", id, e) - } - } - - private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name) - - companion object { - private val logger = LoggerFactory.getLogger(LocalFileSystemMediaDataStore::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashService.kt deleted file mode 100644 index 13fd1eee..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import java.awt.image.BufferedImage - -interface MediaBlurhashService { - fun generateBlurhash(bufferedImage: BufferedImage): String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashServiceImpl.kt deleted file mode 100644 index 963554a3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaBlurhashServiceImpl.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import io.trbl.blurhash.BlurHash -import org.springframework.stereotype.Service -import java.awt.image.BufferedImage - -@Service -class MediaBlurhashServiceImpl : MediaBlurhashService { - override fun generateBlurhash(bufferedImage: BufferedImage): String = BlurHash.encode(bufferedImage) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt deleted file mode 100644 index 79a30159..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaDataStore.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -/** - * メディアをä¿å­˜ã™ã‚‹ã‚¤ãƒ³ã‚¿ãƒ•ã‚§ãƒ¼ã‚¹ - * - */ -interface MediaDataStore { - /** - * InputStreamを使用ã—ã¦ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’ä¿å­˜ã—ã¾ã™ - * - * @param dataMediaSave Fileã¨Thumbnailã®inputStream - * @return ä¿å­˜ã•ã‚ŒãŸãƒ¡ãƒ‡ã‚£ã‚¢ - */ - suspend fun save(dataMediaSave: MediaSave): SavedMedia - - /** - * 一時ファイルã®ãƒ‘スを使用ã—ã¦ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’ä¿å­˜ã—ã¾ã™ - * - * @param dataSaveRequest Fileã¨Thumbnailã®ãƒ‘ス - * @return ä¿å­˜ã•ã‚ŒãŸãƒ¡ãƒ‡ã‚£ã‚¢ - */ - suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia - - /** - * メディアを削除ã—ã¾ã™ - * 実装ã¯ã‚µãƒ ãƒã‚¤ãƒ«ã€ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ãªã©ã‚‚削除ã™ã‚‹ã¹ãã§ã™ã€‚ - * - * @param id 削除ã™ã‚‹ãƒ¡ãƒ‡ã‚£ã‚¢ã®id 通常ã¯[SuccessSavedMedia.name]を指定ã—ã¾ã™ã€‚ - */ - suspend fun delete(id: String) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaFileRenameService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaFileRenameService.kt deleted file mode 100644 index b130cb75..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaFileRenameService.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -interface MediaFileRenameService { - /** - * メディアをリãƒãƒ¼ãƒ ã—ã¾ã™ - * - * @param uploadName アップロードã•ã‚ŒãŸæ™‚点ã§ã®ãƒ•ã‚¡ã‚¤ãƒ«å - * @param uploadMimeType アップロードã•ã‚ŒãŸæ™‚点ã§ã®MimeType - * @param processedName 処ç†å¾Œã®ãƒ•ã‚¡ã‚¤ãƒ«å - * @param processedMimeType 処ç†å¾Œã®MimeType - * @return リãƒãƒ¼ãƒ å¾Œã®ãƒ•ã‚¡ã‚¤ãƒ«å - */ - fun rename(uploadName: String, uploadMimeType: MimeType, processedName: String, processedMimeType: MimeType): String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt deleted file mode 100644 index f8754288..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSave.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -data class MediaSave( - val name: String, - val prefix: String, - val fileInputStream: ByteArray, - val thumbnailInputStream: ByteArray? -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MediaSave - - if (name != other.name) return false - if (prefix != other.prefix) return false - if (!fileInputStream.contentEquals(other.fileInputStream)) return false - if (thumbnailInputStream != null) { - if (other.thumbnailInputStream == null) return false - if (!thumbnailInputStream.contentEquals(other.thumbnailInputStream)) return false - } else if (other.thumbnailInputStream != null) return false - - return true - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + prefix.hashCode() - result = 31 * result + fileInputStream.contentHashCode() - result = 31 * result + (thumbnailInputStream?.contentHashCode() ?: 0) - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSaveRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSaveRequest.kt deleted file mode 100644 index 7e04f027..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaSaveRequest.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import java.nio.file.Path - -data class MediaSaveRequest( - val name: String, - val preffix: String, - val filePath: Path, - val thumbnailPath: Path? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaService.kt deleted file mode 100644 index 386daa78..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaService.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import dev.usbharu.hideout.core.domain.model.media.Media -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest - -interface MediaService { - suspend fun uploadLocalMedia(mediaRequest: MediaRequest): Media - suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt deleted file mode 100644 index d938d5df..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException -import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException -import dev.usbharu.hideout.core.domain.model.media.Media -import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.service.media.converter.MediaProcessService -import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest -import dev.usbharu.hideout.util.withDelete -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.nio.file.Files -import javax.imageio.ImageIO -import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia - -@Service -@Suppress("TooGenericExceptionCaught") -class MediaServiceImpl( - private val mediaDataStore: MediaDataStore, - private val fileTypeDeterminationService: FileTypeDeterminationService, - private val mediaBlurhashService: MediaBlurhashService, - private val mediaRepository: MediaRepository, - private val mediaProcessServices: List, - private val remoteMediaDownloadService: RemoteMediaDownloadService, - private val renameService: MediaFileRenameService -) : MediaService { - @Suppress("LongMethod", "NestedBlockDepth") - override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia { - val fileName = mediaRequest.file.name - logger.info( - "Media upload. filename:$fileName " + - "contentType:${mediaRequest.file.contentType}" - ) - - val tempFile = Files.createTempFile("hideout-tmp-file", ".tmp") - - tempFile.withDelete().use { - Files.newOutputStream(tempFile).use { outputStream -> - mediaRequest.file.inputStream.use { - it.transferTo(outputStream) - } - } - val mimeType = fileTypeDeterminationService.fileType(tempFile, fileName) - - val process = findMediaProcessor(mimeType).process( - mimeType, - fileName, - tempFile, - null - ) - - val dataMediaSave = MediaSaveRequest( - renameService.rename( - mediaRequest.file.name, - mimeType, - process.filePath.fileName.toString(), - process.fileMimeType - ), - "", - process.filePath, - process.thumbnailPath - ) - dataMediaSave.filePath.withDelete().use { - dataMediaSave.thumbnailPath.withDelete().use { - val save = try { - mediaDataStore.save(dataMediaSave) - } catch (e: Exception) { - logger.warn("Failed to save the media", e) - throw MediaSaveException("Failed to save the media.", e) - } - if (save.success.not()) { - save as FaildSavedMedia - logger.warn("Failed to save the media. reason: ${save.reason}") - logger.warn(save.description, save.trace) - throw MediaSaveException("Failed to save the media.") - } - save as SuccessSavedMedia - val blurHash = generateBlurhash(process) - return mediaRepository.save( - EntityMedia( - id = mediaRepository.generateId(), - name = fileName, - url = save.url, - remoteUrl = null, - thumbnailUrl = save.thumbnailUrl, - type = process.fileMimeType.fileType, - mimeType = process.fileMimeType, - blurHash = blurHash, - description = mediaRequest.description - ) - ) - } - } - } - } - - // TODO: ä»®ã®å‡¦ç†ã¨ã—ã¦ä¿å­˜ã—ãŸã‚ˆã†ã«å‹•ã‹ã™ - @Suppress("LongMethod", "NestedBlockDepth") - override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media { - logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}") - - val findByRemoteUrl = mediaRepository.findByRemoteUrl(remoteMedia.url) - if (findByRemoteUrl != null) { - logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url) - return findByRemoteUrl - } - - remoteMediaDownloadService.download(remoteMedia.url).withDelete().use { - val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name) - - val process = findMediaProcessor(mimeType).process(mimeType, remoteMedia.name, it.path, null) - - val mediaSaveRequest = MediaSaveRequest( - renameService.rename( - remoteMedia.name, - mimeType, - process.filePath.fileName.toString(), - process.fileMimeType - ), - "", - process.filePath, - process.thumbnailPath - ) - - mediaSaveRequest.filePath.withDelete().use { - mediaSaveRequest.filePath.withDelete().use { - val save = try { - mediaDataStore.save(mediaSaveRequest) - } catch (e: Exception) { - logger.warn("Failed to save the media", e) - throw MediaSaveException("Failed to save the media.", e) - } - - if (save is FaildSavedMedia) { - logger.warn("Failed to save the media. reason: ${save.reason}") - logger.warn(save.description, save.trace) - throw MediaSaveException("Failed to save the media.") - } - save as SuccessSavedMedia - val blurhash = generateBlurhash(process) - return mediaRepository.save( - EntityMedia( - id = mediaRepository.generateId(), - name = remoteMedia.name, - url = save.url, - remoteUrl = remoteMedia.url, - thumbnailUrl = save.thumbnailUrl, - type = process.fileMimeType.fileType, - mimeType = process.fileMimeType, - blurHash = blurhash - ) - ) - } - } - } - } - - private fun findMediaProcessor(mimeType: MimeType): MediaProcessService { - try { - return mediaProcessServices.first { - try { - it.isSupport(mimeType) - } catch (_: Exception) { - false - } - } - } catch (_: NoSuchElementException) { - throw UnsupportedMediaException("MediaType: $mimeType isn't supported.") - } - } - - private fun generateBlurhash(process: ProcessedMediaPath): String { - val path = if (process.thumbnailPath != null && process.thumbnailMimeType != null) { - process.thumbnailPath - } else { - process.filePath - } - val mimeType = if (process.thumbnailPath != null && process.thumbnailMimeType != null) { - process.thumbnailMimeType - } else { - process.fileMimeType - } - - val imageReadersByMIMEType = ImageIO.getImageReadersByMIMEType(mimeType.type + "/" + mimeType.subtype) - for (imageReader in imageReadersByMIMEType) { - try { - val bufferedImage = ImageIO.createImageInputStream(path.toFile()).use { - imageReader.input = it - imageReader.read(0) - } - return mediaBlurhashService.generateBlurhash(bufferedImage) - } catch (e: Exception) { - logger.warn("Failed to read thumbnail", e) - } - } - return "" - } - - companion object { - private val logger = LoggerFactory.getLogger(MediaServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt deleted file mode 100644 index d02f3be1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedFile.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -data class ProcessedFile( - val byteArray: ByteArray, - val extension: String -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ProcessedFile - - if (!byteArray.contentEquals(other.byteArray)) return false - if (extension != other.extension) return false - - return true - } - - override fun hashCode(): Int { - var result = byteArray.contentHashCode() - result = 31 * result + extension.hashCode() - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMedia.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMedia.kt deleted file mode 100644 index 8b1a1aff..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMedia.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -data class ProcessedMedia( - val file: ProcessedFile, - val thumbnail: ProcessedFile? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMediaPath.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMediaPath.kt deleted file mode 100644 index 61023d3e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ProcessedMediaPath.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import java.nio.file.Path - -data class ProcessedMediaPath( - val filePath: Path, - val thumbnailPath: Path?, - val fileMimeType: MimeType, - val thumbnailMimeType: MimeType? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMedia.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMedia.kt deleted file mode 100644 index 0a2c21d9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMedia.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -data class RemoteMedia( - val name: String, - val url: String, - val mediaType: String, - val description: String? = null -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadService.kt deleted file mode 100644 index a23f0b10..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import java.nio.file.Path - -interface RemoteMediaDownloadService { - suspend fun download(url: String): Path -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt deleted file mode 100644 index 26843782..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/RemoteMediaDownloadServiceImpl.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import dev.usbharu.hideout.application.config.MediaConfig -import dev.usbharu.hideout.core.domain.exception.media.RemoteMediaFileSizeException -import dev.usbharu.hideout.core.service.resource.KtorResourceResolveService -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.nio.file.Files -import java.nio.file.Path -import kotlin.io.path.outputStream - -@Service -class RemoteMediaDownloadServiceImpl( - private val resourceResolveService: KtorResourceResolveService, - private val mediaConfig: MediaConfig -) : - RemoteMediaDownloadService { - override suspend fun download(url: String): Path { - logger.info("START Download remote file. url: {}", url) - val httpResponse = resourceResolveService.resolve(url).body() - val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp") - - logger.debug("Save to {} url: {} ", createTempFile, url) - - httpResponse.use { inputStream -> - createTempFile.outputStream().use { - inputStream.transferTo(it) - } - } - - val contentLength = createTempFile.toFile().length() - if (contentLength >= mediaConfig.remoteMediaFileSizeLimit) { - throw RemoteMediaFileSizeException( - "File size is too large. $contentLength >= ${mediaConfig.remoteMediaFileSizeLimit}" - ) - } - - logger.info("SUCCESS Download remote file. url: {}", url) - return createTempFile - } - - companion object { - private val logger = LoggerFactory.getLogger(RemoteMediaDownloadServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt deleted file mode 100644 index 8fb8ee8f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/S3MediaDataStore.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import dev.usbharu.hideout.application.config.S3StorageConfig -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.withContext -import org.slf4j.LoggerFactory -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.stereotype.Service -import software.amazon.awssdk.core.sync.RequestBody -import software.amazon.awssdk.services.s3.S3Client -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest -import software.amazon.awssdk.services.s3.model.GetUrlRequest -import software.amazon.awssdk.services.s3.model.PutObjectRequest - -@Service -@ConditionalOnProperty("hideout.storage.type", havingValue = "s3") -class S3MediaDataStore(private val s3Client: S3Client, private val s3StorageConfig: S3StorageConfig) : MediaDataStore { - override suspend fun save(dataMediaSave: MediaSave): SavedMedia { - val fileUploadRequest = PutObjectRequest.builder() - .bucket(s3StorageConfig.bucket) - .key(dataMediaSave.name) - .build() - - val thumbnailKey = "thumbnail-${dataMediaSave.name}" - val thumbnailUploadRequest = PutObjectRequest.builder() - .bucket(s3StorageConfig.bucket) - .key(thumbnailKey) - .build() - - withContext(Dispatchers.IO) { - awaitAll( - async { - if (dataMediaSave.thumbnailInputStream != null) { - s3Client.putObject( - thumbnailUploadRequest, - RequestBody.fromBytes(dataMediaSave.thumbnailInputStream) - ) - s3Client.utilities() - .getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(thumbnailKey).build()) - } else { - null - } - }, - async { - s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream)) - s3Client.utilities() - .getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(dataMediaSave.name).build()) - } - ) - } - return SuccessSavedMedia( - name = dataMediaSave.name, - url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataMediaSave.name}", - thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey" - ) - } - - override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia { - logger.info("MEDIA upload. {}", dataSaveRequest.name) - - val fileUploadRequest = PutObjectRequest.builder() - .bucket(s3StorageConfig.bucket) - .key(dataSaveRequest.name) - .build() - - logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, dataSaveRequest.name) - - val thumbnailKey = "thumbnail-${dataSaveRequest.name}" - val thumbnailUploadRequest = PutObjectRequest.builder() - .bucket(s3StorageConfig.bucket) - .key(thumbnailKey) - .build() - - logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, thumbnailKey) - - withContext(Dispatchers.IO) { - awaitAll( - async { - if (dataSaveRequest.thumbnailPath != null) { - s3Client.putObject( - thumbnailUploadRequest, - RequestBody.fromFile(dataSaveRequest.thumbnailPath) - ) - } else { - null - } - }, - async { - s3Client.putObject(fileUploadRequest, RequestBody.fromFile(dataSaveRequest.filePath)) - } - ) - } - val successSavedMedia = SuccessSavedMedia( - name = dataSaveRequest.name, - url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataSaveRequest.name}", - thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey" - ) - - logger.info("SUCCESS Media upload. {}", dataSaveRequest.name) - logger.debug( - "name: {} url: {} thumbnail url: {}", - successSavedMedia.name, - successSavedMedia.url, - successSavedMedia.thumbnailUrl - ) - - return successSavedMedia - } - - override suspend fun delete(id: String) { - val fileDeleteRequest = DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key(id).build() - val thumbnailDeleteRequest = - DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key("thumbnail-$id").build() - s3Client.deleteObject(fileDeleteRequest) - s3Client.deleteObject(thumbnailDeleteRequest) - } - - companion object { - private val logger = LoggerFactory.getLogger(S3MediaDataStore::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/SavedMedia.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/SavedMedia.kt deleted file mode 100644 index 14413c25..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/SavedMedia.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -sealed class SavedMedia(val success: Boolean) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as SavedMedia - - return success == other.success - } - - override fun hashCode(): Int = success.hashCode() -} - -class SuccessSavedMedia( - val name: String, - val url: String, - val thumbnailUrl: String, -) : - SavedMedia(true) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as SuccessSavedMedia - - if (name != other.name) return false - if (url != other.url) return false - if (thumbnailUrl != other.thumbnailUrl) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + url.hashCode() - result = 31 * result + thumbnailUrl.hashCode() - return result - } -} - -class FaildSavedMedia( - val reason: String, - val description: String, - val trace: Throwable? = null -) : SavedMedia(false) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as FaildSavedMedia - - if (reason != other.reason) return false - if (description != other.description) return false - if (trace != other.trace) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + reason.hashCode() - result = 31 * result + description.hashCode() - result = 31 * result + (trace?.hashCode() ?: 0) - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateService.kt deleted file mode 100644 index 71258673..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateService.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import java.io.InputStream - -interface ThumbnailGenerateService { - fun generate(bufferedImage: InputStream, width: Int, height: Int): ProcessedFile? - fun generate(outputStream: ByteArray, width: Int, height: Int): ProcessedFile? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateServiceImpl.kt deleted file mode 100644 index c23fb2a6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/ThumbnailGenerateServiceImpl.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import org.springframework.stereotype.Service -import java.awt.image.BufferedImage -import java.io.ByteArrayOutputStream -import java.io.InputStream -import javax.imageio.ImageIO - -@Service -class ThumbnailGenerateServiceImpl : ThumbnailGenerateService { - override fun generate(bufferedImage: InputStream, width: Int, height: Int): ProcessedFile? { - val image = ImageIO.read(bufferedImage) - return internalGenerate(image) - } - - override fun generate(outputStream: ByteArray, width: Int, height: Int): ProcessedFile? { - val image = ImageIO.read(outputStream.inputStream()) - return internalGenerate(image) - } - - private fun internalGenerate(image: BufferedImage): ProcessedFile { - val byteArrayOutputStream = ByteArrayOutputStream() - ImageIO.write(image, "jpeg", byteArrayOutputStream) - return ProcessedFile(byteArrayOutputStream.toByteArray(), "jpg") - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/UUIDMediaFileRenameService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/UUIDMediaFileRenameService.kt deleted file mode 100644 index 3a9e748f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/UUIDMediaFileRenameService.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.util.* - -@Qualifier("uuid") -@Service -class UUIDMediaFileRenameService : MediaFileRenameService { - override fun rename( - uploadName: String, - uploadMimeType: MimeType, - processedName: String, - processedMimeType: MimeType - ): String = "${UUID.randomUUID()}.${uploadMimeType.subtype}.${processedMimeType.subtype}" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverter.kt deleted file mode 100644 index e7767896..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverter.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter - -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.ProcessedFile -import java.io.InputStream - -interface MediaConverter { - fun isSupport(fileType: FileType): Boolean - fun convert(inputStream: InputStream): ProcessedFile -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRoot.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRoot.kt deleted file mode 100644 index 5b837f7c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRoot.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter - -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.ProcessedFile -import java.io.InputStream - -interface MediaConverterRoot { - suspend fun convert( - fileType: FileType, - contentType: String, - filename: String, - inputStream: InputStream - ): ProcessedFile -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRootImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRootImpl.kt deleted file mode 100644 index 79c44ec3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaConverterRootImpl.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter - -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.ProcessedFile -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.springframework.stereotype.Service -import java.io.InputStream - -@Service -class MediaConverterRootImpl(private val converters: List) : MediaConverterRoot { - override suspend fun convert( - fileType: FileType, - contentType: String, - filename: String, - inputStream: InputStream - ): ProcessedFile { - val convert = converters.find { - it.isSupport(fileType) - }?.convert(inputStream) - if (convert != null) { - return convert - } - return withContext(Dispatchers.IO) { - if (filename.contains('.')) { - ProcessedFile(inputStream.readAllBytes(), filename.substringAfterLast(".")) - } else { - ProcessedFile(inputStream.readAllBytes(), contentType.substringAfterLast("/")) - } - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessService.kt deleted file mode 100644 index 53a11fc7..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessService.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter - -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MimeType -import dev.usbharu.hideout.core.service.media.ProcessedMedia -import dev.usbharu.hideout.core.service.media.ProcessedMediaPath -import java.nio.file.Path - -interface MediaProcessService { - fun isSupport(mimeType: MimeType): Boolean - - suspend fun process( - fileType: FileType, - contentType: String, - fileName: String, - file: ByteArray, - thumbnail: ByteArray? - ): ProcessedMedia - - suspend fun process( - mimeType: MimeType, - fileName: String, - filePath: Path, - thumbnails: Path? - ): ProcessedMediaPath -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessServiceImpl.kt deleted file mode 100644 index bd5f5f0a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/MediaProcessServiceImpl.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter - -import dev.usbharu.hideout.core.domain.exception.media.MediaConvertException -import dev.usbharu.hideout.core.service.media.* -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.nio.file.Path - -@Service -@Suppress("TooGenericExceptionCaught") -class MediaProcessServiceImpl( - private val mediaConverterRoot: MediaConverterRoot, - private val thumbnailGenerateService: ThumbnailGenerateService -) : MediaProcessService { - override fun isSupport(mimeType: MimeType): Boolean = false - - override suspend fun process( - fileType: FileType, - contentType: String, - filename: String, - file: ByteArray, - thumbnail: ByteArray? - ): ProcessedMedia { - val fileInputStream = try { - mediaConverterRoot.convert(fileType, contentType, filename, file.inputStream().buffered()) - } catch (e: Exception) { - logger.warn("Failed convert media.", e) - throw MediaConvertException("Failed convert media.", e) - } - val thumbnailInputStream = try { - thumbnail?.let { mediaConverterRoot.convert(fileType, contentType, filename, it.inputStream().buffered()) } - } catch (e: Exception) { - logger.warn("Failed convert thumbnail media.", e) - null - } - return ProcessedMedia( - fileInputStream, - thumbnailGenerateService.generate( - thumbnailInputStream?.byteArray ?: file, - 2048, - 2048 - ) - ) - } - - override suspend fun process( - mimeType: MimeType, - fileName: String, - filePath: Path, - thumbnails: Path? - ): ProcessedMediaPath { - TODO("Not yet implemented") - } - - companion object { - private val logger = LoggerFactory.getLogger(MediaProcessServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt deleted file mode 100644 index c41b7da8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessService.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter.image - -import dev.usbharu.hideout.core.domain.exception.media.MediaProcessException -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MimeType -import dev.usbharu.hideout.core.service.media.ProcessedMedia -import dev.usbharu.hideout.core.service.media.ProcessedMediaPath -import dev.usbharu.hideout.core.service.media.converter.MediaProcessService -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.slf4j.MDCContext -import kotlinx.coroutines.withContext -import net.coobird.thumbnailator.Thumbnails -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.awt.Color -import java.awt.image.BufferedImage -import java.nio.file.Files -import java.nio.file.Path -import java.util.* -import javax.imageio.ImageIO -import kotlin.io.path.inputStream -import kotlin.io.path.outputStream - -@Service -@Qualifier("image") -class ImageMediaProcessService(private val imageMediaProcessorConfiguration: ImageMediaProcessorConfiguration?) : - MediaProcessService { - - private val convertType = imageMediaProcessorConfiguration?.convert ?: "jpeg" - - private val supportedTypes = imageMediaProcessorConfiguration?.supportedType ?: listOf("webp", "jpeg", "png") - - private val genThumbnail = imageMediaProcessorConfiguration?.thubnail?.generate ?: true - - private val width = imageMediaProcessorConfiguration?.thubnail?.width ?: 1000 - private val height = imageMediaProcessorConfiguration?.thubnail?.height ?: 1000 - - override fun isSupport(mimeType: MimeType): Boolean { - if (mimeType.type != "image") { - return false - } - return supportedTypes.contains(mimeType.subtype) - } - - override suspend fun process( - fileType: FileType, - contentType: String, - fileName: String, - file: ByteArray, - thumbnail: ByteArray? - ): ProcessedMedia { - TODO("Not yet implemented") - } - - override suspend fun process( - mimeType: MimeType, - fileName: String, - filePath: Path, - thumbnails: Path? - ): ProcessedMediaPath = withContext(Dispatchers.IO + MDCContext()) { - val read = ImageIO.read(filePath.inputStream()) - - val bufferedImage = BufferedImage(read.width, read.height, BufferedImage.TYPE_INT_RGB) - - val graphics = bufferedImage.createGraphics() - - graphics.drawImage(read, 0, 0, Color.BLACK, null) - - val tempFileName = UUID.randomUUID().toString() - val tempFile = Files.createTempFile(tempFileName, "tmp") - - val thumbnailPath = if (genThumbnail) { - val tempThumbnailFile = Files.createTempFile("thumbnail-$tempFileName", ".tmp") - - tempThumbnailFile.outputStream().use { - val write = ImageIO.write( - if (thumbnails != null) { - Thumbnails.of(thumbnails.toFile()) - .size(width, height) - .imageType(BufferedImage.TYPE_INT_RGB) - .asBufferedImage() - } else { - Thumbnails.of(bufferedImage) - .size(width, height) - .imageType(BufferedImage.TYPE_INT_RGB) - .asBufferedImage() - }, - convertType, - it - ) - tempThumbnailFile.takeIf { write } - } - } else { - null - } - - tempFile.outputStream().use { - if (ImageIO.write(bufferedImage, convertType, it).not()) { - logger.warn("Failed to save a temporary file. type: {} ,path: {}", convertType, tempFile) - throw MediaProcessException("Failed to save a temporary file.") - } - } - ProcessedMediaPath( - tempFile, - thumbnailPath, - MimeType("image", convertType, FileType.Image), - MimeType("image", convertType, FileType.Image).takeIf { genThumbnail } - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(ImageMediaProcessService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt deleted file mode 100644 index cd24a7d3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter.image - -import org.springframework.boot.context.properties.ConfigurationProperties - -@ConfigurationProperties("hideout.media.image") -data class ImageMediaProcessorConfiguration( - val convert: String?, - val thubnail: ImageMediaProcessorThumbnailConfiguration?, - val supportedType: List?, - -) - -data class ImageMediaProcessorThumbnailConfiguration( - val generate: Boolean, - val width: Int, - val height: Int -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt deleted file mode 100644 index bf8465e1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/movie/MovieMediaProcessService.kt +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media.converter.movie - -import dev.usbharu.hideout.core.service.media.FileType -import dev.usbharu.hideout.core.service.media.MimeType -import dev.usbharu.hideout.core.service.media.ProcessedMedia -import dev.usbharu.hideout.core.service.media.ProcessedMediaPath -import dev.usbharu.hideout.core.service.media.converter.MediaProcessService -import org.bytedeco.ffmpeg.global.avcodec -import org.bytedeco.javacv.FFmpegFrameFilter -import org.bytedeco.javacv.FFmpegFrameGrabber -import org.bytedeco.javacv.FFmpegFrameRecorder -import org.bytedeco.javacv.Java2DFrameConverter -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.stereotype.Service -import java.awt.image.BufferedImage -import java.nio.file.Files -import java.nio.file.Path -import javax.imageio.ImageIO -import kotlin.math.min - -@Service -@Qualifier("video") -class MovieMediaProcessService : MediaProcessService { - override fun isSupport(mimeType: MimeType): Boolean = mimeType.type == "video" - - override suspend fun process( - fileType: FileType, - contentType: String, - fileName: String, - file: ByteArray, - thumbnail: ByteArray? - ): ProcessedMedia { - TODO("Not yet implemented") - } - - @Suppress("LongMethod", "NestedBlockDepth", "CognitiveComplexMethod") - override suspend fun process( - mimeType: MimeType, - fileName: String, - filePath: Path, - thumbnails: Path? - ): ProcessedMediaPath { - val tempFile = Files.createTempFile("hideout-movie-processor-", ".tmp") - val thumbnailFile = Files.createTempFile("hideout-movie-thumbnail-generate-", ".tmp") - logger.info("START Convert Movie Media {}", fileName) - FFmpegFrameGrabber(filePath.toFile()).use { grabber -> - grabber.start() - val width = grabber.imageWidth - val height = grabber.imageHeight - val frameRate = 60.0 - - logger.debug("Movie Media Width {}, Height {}", width, height) - - FFmpegFrameFilter( - "fps=fps=${frameRate.toInt()}", - "anull", - width, - height, - grabber.audioChannels - ).use { filter -> - - filter.sampleFormat = grabber.sampleFormat - filter.sampleRate = grabber.sampleRate - filter.pixelFormat = grabber.pixelFormat - filter.frameRate = grabber.frameRate - filter.start() - - val videoBitRate = min(1300000, (width * height * frameRate * 1 * 0.07).toInt()) - - logger.debug("Movie Media BitRate {}", videoBitRate) - - FFmpegFrameRecorder(tempFile.toFile(), width, height, grabber.audioChannels).use { - it.sampleRate = grabber.sampleRate - it.format = "mp4" - it.videoCodec = avcodec.AV_CODEC_ID_H264 - it.audioCodec = avcodec.AV_CODEC_ID_AAC - it.audioChannels = grabber.audioChannels - it.videoQuality = 1.0 - it.frameRate = frameRate - it.setVideoOption("preset", "ultrafast") - it.timestamp = 0 - it.gopSize = frameRate.toInt() - it.videoBitrate = videoBitRate - it.start() - - var bufferedImage: BufferedImage? = null - - val frameConverter = Java2DFrameConverter() - - while (true) { - val grab = grabber.grab() ?: break - - if (bufferedImage == null) { - bufferedImage = frameConverter.convert(grab) - } - - if (grab.image != null || grab.samples != null) { - filter.push(grab) - } - while (true) { - val frame = filter.pull() ?: break - it.record(frame) - } - } - - if (bufferedImage != null) { - ImageIO.write(bufferedImage, "jpeg", thumbnailFile.toFile()) - } - } - } - } - - logger.info("SUCCESS Convert Movie Media {}", fileName) - - return ProcessedMediaPath( - tempFile, - thumbnailFile, - MimeType("video", "mp4", FileType.Video), - MimeType("image", "jpeg", FileType.Image) - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(MovieMediaProcessService::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt deleted file mode 100644 index 8f4c382f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationRequest.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import java.time.Instant - -sealed class NotificationRequest(open val userId: Long, open val sourceActorId: Long?, val type: String) { - abstract fun buildNotification(id: Long, createdAt: Instant): Notification -} - -interface PostId { - val postId: Long -} - -data class MentionNotificationRequest( - override val userId: Long, - override val sourceActorId: Long, - override val postId: Long -) : NotificationRequest( - userId, - sourceActorId, - "mention" -), - PostId { - override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( - id = id, - type = type, - userId = userId, - sourceActorId = sourceActorId, - postId = postId, - text = null, - reactionId = null, - createdAt = createdAt - ) -} - -data class PostNotificationRequest( - override val userId: Long, - override val sourceActorId: Long, - override val postId: Long - -) : NotificationRequest(userId, sourceActorId, "post"), PostId { - override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( - id = id, - type = type, - userId = userId, - sourceActorId = sourceActorId, - postId = postId, - text = null, - reactionId = null, - createdAt = createdAt - ) -} - -data class RepostNotificationRequest( - override val userId: Long, - override val sourceActorId: Long, - override val postId: Long -) : NotificationRequest(userId, sourceActorId, "repost"), PostId { - override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( - id = id, - type = type, - userId = userId, - sourceActorId = sourceActorId, - postId = postId, - text = null, - reactionId = null, - createdAt = createdAt - ) -} - -data class FollowNotificationRequest( - override val userId: Long, - override val sourceActorId: Long -) : NotificationRequest(userId, sourceActorId, "follow") { - override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( - id = id, - type = type, - userId = userId, - sourceActorId = sourceActorId, - postId = null, - text = null, - reactionId = null, - createdAt = createdAt - ) -} - -data class FollowRequestNotificationRequest( - override val userId: Long, - override val sourceActorId: Long -) : NotificationRequest(userId, sourceActorId, "follow-request") { - override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( - id = id, - type = type, - userId = userId, - sourceActorId = sourceActorId, - postId = null, - text = null, - reactionId = null, - createdAt = createdAt - ) -} - -data class ReactionNotificationRequest( - override val userId: Long, - override val sourceActorId: Long, - override val postId: Long, - val reactionId: Long - -) : NotificationRequest(userId, sourceActorId, "reaction"), PostId { - override fun buildNotification(id: Long, createdAt: Instant): Notification = Notification( - id = id, - type = type, - userId = userId, - sourceActorId = sourceActorId, - postId = postId, - text = null, - reactionId = reactionId, - createdAt = createdAt - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt deleted file mode 100644 index 14354b26..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationService.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification - -interface NotificationService { - suspend fun publishNotify(notificationRequest: NotificationRequest): Notification? - suspend fun unpublishNotify(notificationId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt deleted file mode 100644 index 189ac6fc..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImpl.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.notification.Notification -import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class NotificationServiceImpl( - private val relationshipNotificationManagementService: RelationshipNotificationManagementService, - private val relationshipRepository: RelationshipRepository, - private val notificationStoreList: List, - private val notificationRepository: NotificationRepository, - private val actorRepository: ActorRepository, - private val postRepository: PostRepository, - private val reactionRepository: ReactionRepository, - private val applicationConfig: ApplicationConfig -) : NotificationService { - override suspend fun publishNotify(notificationRequest: NotificationRequest): Notification? { - logger.debug("NOTIFICATION REQUEST user: {} type: {}", notificationRequest.userId, notificationRequest.type) - logger.trace("NotificationRequest: {}", notificationRequest) - - val user = actorRepository.findById(notificationRequest.userId) - if (user == null || user.domain != applicationConfig.url.host) { - logger.debug("NOTIFICATION REQUEST is rejected. (Remote Actor or user not found.)") - return null - } - - // ã¨ã‚Šã‚ãˆãšå€‹äººé–“ã®Relationshipã«åŸºã¥ã„ã¦ãã‚る。今後増や㙠- if (!relationship(notificationRequest)) { - logger.debug("NOTIFICATION REQUEST is rejected. (relationship)") - return null - } - - val id = notificationRepository.generateId() - val createdAt = Instant.now() - - val notification = notificationRequest.buildNotification(id, createdAt) - - val savedNotification = notificationRepository.save(notification) - - val sourceActor = savedNotification.sourceActorId?.let { actorRepository.findById(it) } - - val post = savedNotification.postId?.let { postRepository.findById(it) } - val reaction = savedNotification.reactionId?.let { reactionRepository.findById(it) } - - logger.info( - "NOTIFICATION id: {} user: {} type: {}", - savedNotification.id, - savedNotification.userId, - savedNotification.type - ) - - logger.debug("push to {} notification store.", notificationStoreList.size) - for (it in notificationStoreList) { - @Suppress("TooGenericExceptionCaught") - try { - it.publishNotification(savedNotification, user, sourceActor, post, reaction) - } catch (e: Exception) { - logger.warn("FAILED Publish to notification.", e) - } - } - logger.debug("SUCCESS Notification id: {}", savedNotification.id) - - return savedNotification - } - - override suspend fun unpublishNotify(notificationId: Long) { - notificationRepository.deleteById(notificationId) - for (notificationStore in notificationStoreList) { - notificationStore.unpulishNotification(notificationId) - } - } - - /** - * 個人間ã®Relationshipã«åŸºã¥ã„ã¦é€šçŸ¥ã‚’é€ä¿¡ã™ã‚‹ã‹åˆ¤æ–­ã—ã¾ã™ - * - * @param notificationRequest - * @return trueã®å ´åˆé€ä¿¡ã™ã‚‹ - */ - private suspend fun relationship(notificationRequest: NotificationRequest): Boolean { - val targetActorId = notificationRequest.sourceActorId ?: return true - val relationship = - relationshipRepository.findByUserIdAndTargetUserId(notificationRequest.userId, targetActorId) ?: return true - return relationshipNotificationManagementService.sendNotification(relationship, notificationRequest) - } - - companion object { - private val logger = LoggerFactory.getLogger(NotificationServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt deleted file mode 100644 index d7c89265..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementService.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.relationship.Relationship - -interface RelationshipNotificationManagementService { - fun sendNotification(relationship: Relationship, notificationRequest: NotificationRequest): Boolean -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt deleted file mode 100644 index 0f1bd478..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImpl.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.relationship.Relationship -import org.springframework.stereotype.Service - -@Service -class RelationshipNotificationManagementServiceImpl : RelationshipNotificationManagementService { - override fun sendNotification(relationship: Relationship, notificationRequest: NotificationRequest): Boolean = - relationship.muting.not() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatter.kt deleted file mode 100644 index bc203296..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatter.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import org.jsoup.nodes.TextNode -import org.jsoup.select.Elements -import org.owasp.html.PolicyFactory -import org.springframework.stereotype.Service - -@Service -class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : PostContentFormatter { - override fun format(content: String): FormattedPostContent { - // ã¾ãšä¸æ­£ãªHTMLã‚’æ•´å½¢ã™ã‚‹ - val document = Jsoup.parseBodyFragment(content) - val outputSettings = Document.OutputSettings() - outputSettings.prettyPrint(false) - - document.outputSettings(outputSettings) - - val unsafeElement = document.getElementsByTag("body").first() ?: return FormattedPostContent( - "", - "" - ) - - // 文字ã ã‘ã®HTMLãªã©ã¯ã“ã“ã§pã‚¿ã‚°ã§å›²ã‚€ - val flattenHtml = unsafeElement.childNodes().mapNotNull { - if (it is Element) { - it - } else if (it is TextNode) { - Element("p").appendText(it.text()) - } else { - null - } - }.filter { it.text().isNotBlank() } - - // HTMLã®ã‚µãƒ‹ã‚¿ã‚¤ã‚ºã‚’ã™ã‚‹ - val unsafeHtml = Elements(flattenHtml).outerHtml() - - val safeHtml = policyFactory.sanitize(unsafeHtml) - - val safeDocument = - Jsoup.parseBodyFragment(safeHtml).getElementsByTag("body").first() ?: return FormattedPostContent("", "") - - val formattedHtml = mutableListOf() - - // 連続ã™ã‚‹brタグを段è½ã«å¤‰æ›ã™ã‚‹ - for (element in safeDocument.children()) { - var brCount = 0 - var prevIndex = 0 - val childNodes = element.childNodes() - for ((index, childNode) in childNodes.withIndex()) { - if (childNode is Element && childNode.tagName() == "br") { - brCount++ - } else if (brCount >= 2) { - formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, index - brCount))) - prevIndex = index - } - } - formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, childNodes.size))) - } - - val elements = Elements(formattedHtml) - - return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements)) - } - - private fun printHtml(element: Elements): String { - return element.joinToString("\n\n") { - it.childNodes().joinToString("") { node -> - if (node is Element && node.tagName() == "br") { - "\n" - } else if (node is Element) { - node.text() - } else if (node is TextNode) { - node.text() - } else { - "" - } - } - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt deleted file mode 100644 index 7a0269d5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -interface PostContentFormatter { - fun format(content: String): FormattedPostContent -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostCreateDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostCreateDto.kt deleted file mode 100644 index f2450dd4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostCreateDto.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.core.domain.model.post.Visibility - -data class PostCreateDto( - val text: String, - val overview: String? = null, - val visibility: Visibility = Visibility.PUBLIC, - val repostId: Long? = null, - val repolyId: Long? = null, - val userId: Long, - val mediaIds: List = emptyList() -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionService.kt deleted file mode 100644 index 5b3421cc..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionService.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.reaction - -import dev.usbharu.hideout.core.domain.model.emoji.Emoji -import org.springframework.stereotype.Service - -@Service -interface ReactionService { - suspend fun receiveReaction(emoji: Emoji, actorId: Long, postId: Long) - suspend fun receiveRemoveReaction(actorId: Long, postId: Long) - suspend fun sendReaction(emoji: Emoji, actorId: Long, postId: Long) - suspend fun removeReaction(actorId: Long, postId: Long) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt deleted file mode 100644 index 26fe8d61..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImpl.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.reaction - -import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.model.emoji.Emoji -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.service.notification.NotificationService -import dev.usbharu.hideout.core.service.notification.ReactionNotificationRequest -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service - -@Service -class ReactionServiceImpl( - private val reactionRepository: ReactionRepository, - private val apReactionService: APReactionService, - private val notificationService: NotificationService, - private val postRepository: PostRepository -) : ReactionService { - override suspend fun receiveReaction( - emoji: Emoji, - actorId: Long, - postId: Long - ) { - if (reactionRepository.existByPostIdAndActor(postId, actorId)) { - reactionRepository.deleteByPostIdAndActorId(postId, actorId) - } - try { - val reaction = reactionRepository.save(Reaction(reactionRepository.generateId(), emoji, postId, actorId)) - - notificationService.publishNotify( - ReactionNotificationRequest( - postRepository.findById(postId)!!.actorId, - actorId, - postId, - reaction.id - ) - ) - } catch (_: DuplicateException) { - } - } - - override suspend fun receiveRemoveReaction(actorId: Long, postId: Long) { - val reaction = reactionRepository.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) - if (reaction == null) { - LOGGER.warn("FAILED receive Remove Reaction. $actorId $postId") - return - } - reactionRepository.delete(reaction) - } - - override suspend fun sendReaction(emoji: Emoji, actorId: Long, postId: Long) { - val findByPostIdAndUserIdAndEmojiId = - reactionRepository.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) - - if (findByPostIdAndUserIdAndEmojiId != null) { - apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) - reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) - } - - val reaction = Reaction(reactionRepository.generateId(), emoji, postId, actorId) - reactionRepository.save(reaction) - apReactionService.reaction(reaction) - - val id = postRepository.findById(postId)!!.actorId - - notificationService.publishNotify(ReactionNotificationRequest(id, actorId, postId, reaction.id)) - } - - override suspend fun removeReaction(actorId: Long, postId: Long) { - val findByPostIdAndUserIdAndEmojiId = - reactionRepository.findByPostIdAndActorIdAndEmojiId(postId, actorId, 0) - if (findByPostIdAndUserIdAndEmojiId == null) { - LOGGER.warn("FAILED Remove reaction. actorId: $actorId postId: $postId") - return - } - reactionRepository.delete(findByPostIdAndUserIdAndEmojiId) - apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId) - } - - companion object { - val LOGGER: Logger = LoggerFactory.getLogger(ReactionServiceImpl::class.java) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt deleted file mode 100644 index 3e6408fe..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -interface CacheManager { - suspend fun putCache(key: String, block: suspend () -> ResolveResponse) - suspend fun getOrWait(key: String): ResolveResponse -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt deleted file mode 100644 index 39c1239f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -import dev.usbharu.hideout.util.LruCache -import kotlinx.coroutines.delay -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class InMemoryCacheManager : CacheManager { - private val cacheKey = LruCache(15) - private val valueStore = mutableMapOf() - private val keyMutex = Mutex() - - override suspend fun putCache(key: String, block: suspend () -> ResolveResponse) { - val needRunBlock: Boolean - keyMutex.withLock { - cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() } - - val cached = cacheKey[key] - if (cached == null) { - needRunBlock = true - cacheKey[key] = Instant.now().toEpochMilli() - - valueStore.remove(key) - } else { - needRunBlock = false - } - } - if (needRunBlock) { - @Suppress("TooGenericExceptionCaught") - val processed = try { - block() - } catch (e: Exception) { - cacheKey.remove(key) - throw e - } - - if (cacheKey.containsKey(key)) { - valueStore[key] = processed - } - } - } - - override suspend fun getOrWait(key: String): ResolveResponse { - while (valueStore.contains(key).not()) { - if (cacheKey.containsKey(key).not()) { - throw IllegalStateException("Invalid cache key. $key") - } - delay(1) - } - return valueStore.getValue(key) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt deleted file mode 100644 index c2453e7a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -import io.ktor.client.statement.* -import io.ktor.util.* -import io.ktor.utils.io.jvm.javaio.* -import java.io.InputStream - -class KtorResolveResponse(val ktorHttpResponse: HttpResponse) : ResolveResponse { - - private lateinit var _bodyAsText: String - private lateinit var _bodyAsBytes: ByteArray - - override suspend fun body(): InputStream = ktorHttpResponse.bodyAsChannel().toInputStream() - override suspend fun bodyAsText(): String { - if (!this::_bodyAsText.isInitialized) { - _bodyAsText = ktorHttpResponse.bodyAsText() - } - return _bodyAsText - } - - override suspend fun bodyAsBytes(): ByteArray { - if (!this::_bodyAsBytes.isInitialized) { - _bodyAsBytes = ktorHttpResponse.readBytes() - } - return _bodyAsBytes - } - - override suspend fun header(): Map> = ktorHttpResponse.headers.toMap() - override suspend fun status(): Int = ktorHttpResponse.status.value - override suspend fun statusMessage(): String = ktorHttpResponse.status.description - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as KtorResolveResponse - - if (ktorHttpResponse != other.ktorHttpResponse) return false - if (_bodyAsText != other._bodyAsText) return false - if (!_bodyAsBytes.contentEquals(other._bodyAsBytes)) return false - - return true - } - - override fun hashCode(): Int { - var result = ktorHttpResponse.hashCode() - result = 31 * result + _bodyAsText.hashCode() - result = 31 * result + _bodyAsBytes.contentHashCode() - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt deleted file mode 100644 index f8407b23..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -import dev.usbharu.hideout.application.config.MediaConfig -import dev.usbharu.hideout.core.domain.exception.media.RemoteMediaFileSizeException -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.http.* -import org.springframework.stereotype.Service - -@Service -class KtorResourceResolveService( - private val httpClient: HttpClient, - private val cacheManager: CacheManager, - private val mediaConfig: MediaConfig -) : - ResourceResolveService { - - var sizeLimit = mediaConfig.remoteMediaFileSizeLimit - - override suspend fun resolve(url: String): ResolveResponse { - cacheManager.putCache(getCacheKey(url)) { - runResolve(url) - } - return cacheManager.getOrWait(getCacheKey(url)) - } - - protected suspend fun runResolve(url: String): ResolveResponse { - val httpResponse = httpClient.get(url) - val contentLength = httpResponse.contentLength() - if ((contentLength ?: 0) >= sizeLimit) { - throw RemoteMediaFileSizeException("File size is too large. $contentLength >= $sizeLimit") - } - return KtorResolveResponse(httpResponse) - } - - protected suspend fun getCacheKey(url: String) = url -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt deleted file mode 100644 index 8da3c7cb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -import java.io.InputStream - -interface ResolveResponse { - suspend fun body(): InputStream - suspend fun bodyAsText(): String - suspend fun bodyAsBytes(): ByteArray - suspend fun header(): Map> - suspend fun status(): Int - suspend fun statusMessage(): String -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt deleted file mode 100644 index bcb1c97f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -interface ResourceResolveService { - suspend fun resolve(url: String): ResolveResponse -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt deleted file mode 100644 index 98447037..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.timeline - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.withPagination -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import org.jetbrains.exposed.sql.andWhere -import org.jetbrains.exposed.sql.selectAll -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.stereotype.Service - -@Service -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true) -class ExposedGenerateTimelineService(private val statusQueryService: StatusQueryService) : GenerateTimelineService { - - override suspend fun getTimeline( - forUserId: Long?, - localOnly: Boolean, - mediaOnly: Boolean, - page: Page - ): PaginationList { - val query = Timelines.selectAll() - - if (forUserId != null) { - query.andWhere { Timelines.userId eq forUserId } - } - if (localOnly) { - query.andWhere { Timelines.isLocal eq true } - } - val result = query.withPagination(page, Timelines.id) - - val statusQueries = result.map { - StatusQuery( - it[Timelines.postId], - it[Timelines.replyId], - it[Timelines.repostId], - it[Timelines.mediaIds].split(",").mapNotNull { s -> s.toLongOrNull() }, - it[Timelines.emojiIds].split(",").mapNotNull { s -> s.toLongOrNull() } - ) - } - - val findByPostIdsWithMediaIds = statusQueryService.findByPostIdsWithMediaIds(statusQueries) - return PaginationList( - findByPostIdsWithMediaIds, - findByPostIdsWithMediaIds.lastOrNull()?.id?.toLongOrNull(), - findByPostIdsWithMediaIds.firstOrNull()?.id?.toLongOrNull() - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt deleted file mode 100644 index a065a58d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.timeline - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import org.springframework.stereotype.Service - -@Service -@Suppress("LongParameterList") -interface GenerateTimelineService { - - suspend fun getTimeline( - forUserId: Long? = null, - localOnly: Boolean = false, - mediaOnly: Boolean = false, - page: Page - ): PaginationList -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt deleted file mode 100644 index 39ce7e52..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.timeline - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.domain.model.timeline.Timeline -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.data.domain.Sort -import org.springframework.data.mongodb.core.MongoTemplate -import org.springframework.data.mongodb.core.query.Criteria -import org.springframework.data.mongodb.core.query.Query -import org.springframework.stereotype.Service - -@Service -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false) -class MongoGenerateTimelineService( - private val statusQueryService: StatusQueryService, - private val mongoTemplate: MongoTemplate -) : - GenerateTimelineService { - - override suspend fun getTimeline( - forUserId: Long?, - localOnly: Boolean, - mediaOnly: Boolean, - page: Page - ): PaginationList { - val query = Query() - - if (forUserId != null) { - val criteria = Criteria.where("userId").`is`(forUserId) - query.addCriteria(criteria) - } - if (localOnly) { - val criteria = Criteria.where("isLocal").`is`(true) - query.addCriteria(criteria) - } - - if (page.minId != null) { - 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, "createdAt")) - 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) } - - query.with(Sort.by(Sort.Direction.DESC, "createdAt")) - - val timelines = mongoTemplate.find(query, Timeline::class.java) - - val statuses = statusQueryService.findByPostIdsWithMediaIds( - timelines.map { - StatusQuery( - it.postId, - it.replyId, - it.repostId, - it.mediaIds, - it.emojiIds - ) - } - ) - return PaginationList( - statuses, - statuses.lastOrNull()?.id?.toLongOrNull(), - statuses.firstOrNull()?.id?.toLongOrNull() - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserCreateDto.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserCreateDto.kt deleted file mode 100644 index be7b5e0d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserCreateDto.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.user - -data class UserCreateDto( - val name: String, - val screenName: String, - val description: String, - val password: String -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/CollectionUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/CollectionUtil.kt deleted file mode 100644 index 03249388..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/CollectionUtil.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -class CollectionUtil - -fun Iterable.singleOr(block: (e: RuntimeException) -> Throwable): T { - return try { - this.single() - } catch (e: NoSuchElementException) { - throw block(e) - } catch (e: IllegalArgumentException) { - throw block(e) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt deleted file mode 100644 index 01fd3842..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/HttpUtil.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import io.ktor.http.* - -object HttpUtil { - val Activity: ContentType - get() = ContentType("application", "activity+json") - - val JsonLd: ContentType - get() { - return ContentType( - contentType = "application", - contentSubtype = "ld+json", - parameters = listOf(HeaderValueParam("profile", "https://www.w3.org/ns/activitystreams")) - ) - } - - fun isContentTypeOfActivityPub( - contentType: String, - subType: String - ): Boolean { - if (contentType != "application") { - return false - } - if (subType == "activity+json") { - return true - } - return subType == "ld+json" - } - - fun isContentTypeOfActivityPub(contentType: ContentType): Boolean { - return isContentTypeOfActivityPub( - contentType.contentType, - contentType.contentSubtype - ) - } -// fun -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/InstantParseUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/InstantParseUtil.kt deleted file mode 100644 index fd6c3669..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/InstantParseUtil.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import java.time.Instant -import java.time.format.DateTimeParseException - -object InstantParseUtil { - fun parse(str: String?): Instant? { - return try { - Instant.ofEpochMilli(str?.toLong() ?: return null) - } catch (e: NumberFormatException) { - try { - Instant.parse(str) - } catch (e: DateTimeParseException) { - null - } - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt deleted file mode 100644 index c5ffdea2..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import java.io.Serial - -class LruCache(private val maxSize: Int) : LinkedHashMap(15, 0.75f, true) { - - override fun removeEldestEntry(eldest: MutableMap.MutableEntry?): Boolean = size > maxSize - override fun toString(): String { - return "LruCache(" + - "maxSize=$maxSize" + - ")" + - " ${super.toString()}" - } - - companion object { - @Serial - private const val serialVersionUID: Long = -6446947260925053191L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt index 8827b61a..8efbd8b0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt @@ -45,9 +45,4 @@ object RsaUtil { fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded)) - fun decodeRsaPrivateKeyPem(pem: String): RSAPrivateKey { - val replace = pem.replace(replaceHeaderAndFooterRegex, "") - .replace("\n", "") - return decodeRsaPrivateKey(replace) - } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/ServerUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/ServerUtil.kt deleted file mode 100644 index 137d449a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/ServerUtil.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -object ServerUtil { - fun getImplementationVersion(): String = - ServerUtil.javaClass.`package`.implementationVersion ?: "DEVELOPMENT-VERSION" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/TempFileUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/TempFileUtil.kt deleted file mode 100644 index 39fcda52..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/TempFileUtil.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import java.nio.file.Files -import java.nio.file.Path - -fun T.withDelete(): TempFile = TempFile(this) - -class TempFile(val path: T) : AutoCloseable { - override fun close() { - path?.let { Files.deleteIfExists(it) } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TempFile<*> - - return path == other.path - } - - override fun hashCode(): Int = path?.hashCode() ?: 0 -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt index bba96791..532f6403 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt @@ -19,9 +19,6 @@ package dev.usbharu.hideout.application.service.init import dev.usbharu.hideout.core.domain.exception.NotInitException -import dev.usbharu.hideout.core.domain.model.meta.Jwt -import dev.usbharu.hideout.core.domain.model.meta.Meta -import dev.usbharu.hideout.core.domain.model.meta.MetaRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImplTest.kt deleted file mode 100644 index d24bc17c..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseServiceImplTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) - -package dev.usbharu.hideout.application.service.init - -import dev.usbharu.hideout.core.domain.model.meta.Jwt -import dev.usbharu.hideout.core.domain.model.meta.Meta -import dev.usbharu.hideout.core.domain.model.meta.MetaRepository -import dev.usbharu.hideout.util.ServerUtil -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.mockito.kotlin.* -import utils.TestTransaction -import java.util.* -import kotlin.test.assertEquals - -class ServerInitialiseServiceImplTest { - @Test - fun `init メタデータãŒç„¡ã„ã¨ãã«åˆæœŸåŒ–を実行ã™ã‚‹`() = runTest { - val metaRepository = mock { - onBlocking { get() } doReturn null - onBlocking { save(any()) } doReturn Unit - } - val serverInitialiseServiceImpl = ServerInitialiseServiceImpl(metaRepository, TestTransaction) - - serverInitialiseServiceImpl.init() - verify(metaRepository, times(1)).save(any()) - } - - @Test - fun `init メタデータãŒå­˜åœ¨ã—ã¦åŒã˜ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã¨ãã¯ä½•ã‚‚ã—ãªã„`() = runTest { - val meta = Meta(ServerUtil.getImplementationVersion(), Jwt(UUID.randomUUID(), "aaafafd", "afafasdf")) - val metaRepository = mock { - onBlocking { get() } doReturn meta - } - val serverInitialiseServiceImpl = ServerInitialiseServiceImpl(metaRepository, TestTransaction) - serverInitialiseServiceImpl.init() - verify(metaRepository, times(0)).save(any()) - } - - @Test - fun `init メタデータãŒå­˜åœ¨ã—ã¦é•ã†ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã¨ãã¯ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’変更ã™ã‚‹`() = runTest { - val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "aaafafd", "afafasdf")) - val metaRepository = mock { - onBlocking { get() } doReturn meta - onBlocking { save(any()) } doReturn Unit - } - - val serverInitialiseServiceImpl = ServerInitialiseServiceImpl(metaRepository, TestTransaction) - serverInitialiseServiceImpl.init() - verify(metaRepository, times(1)).save(any()) - argumentCaptor { - verify(metaRepository, times(1)).save(capture()) - assertEquals(ServerUtil.getImplementationVersion(), firstValue.version) - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt index 43167eef..30508ea1 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.service.notification -import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship import org.junit.jupiter.api.Test import kotlin.test.assertTrue diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt index 0d82e8b5..acff5e20 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt @@ -21,8 +21,8 @@ import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowServi import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.service.notification.NotificationService +import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt index 29db21c2..71c05e4a 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt @@ -18,8 +18,6 @@ package dev.usbharu.hideout.core.service.timeline import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService 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.TimelineRepository import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt index bfd88778..feb0cc4f 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt @@ -25,7 +25,6 @@ import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import dev.usbharu.hideout.core.service.instance.InstanceService import dev.usbharu.owl.producer.api.OwlProducer import jakarta.validation.Validation import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index e1676e7a..0640f8ba 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -16,9 +16,9 @@ package dev.usbharu.hideout.mastodon.service.account -import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship diff --git a/hideout-core/src/test/kotlin/utils/TestTransaction.kt b/hideout-core/src/test/kotlin/utils/TestTransaction.kt index 4fc832fd..a32bf303 100644 --- a/hideout-core/src/test/kotlin/utils/TestTransaction.kt +++ b/hideout-core/src/test/kotlin/utils/TestTransaction.kt @@ -16,7 +16,7 @@ package utils -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction object TestTransaction : Transaction { override suspend fun transaction(block: suspend () -> T): T = block() diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt index 7d939be0..71817b59 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.external.job.DeliverAcceptTask import dev.usbharu.hideout.core.external.job.DeliverAcceptTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt index 9d880986..0f71f98c 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.external.job.DeliverCreateTask import dev.usbharu.hideout.core.external.job.DeliverCreateTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt index 73179f07..b84a5d7a 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.external.job.DeliverRejectTask import dev.usbharu.hideout.core.external.job.DeliverRejectTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt index 949435ba..4c08786f 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.external.job.DeliverUndoTask import dev.usbharu.hideout.core.external.job.DeliverUndoTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt index 1a5be9a4..ae4af395 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt @@ -18,11 +18,10 @@ package dev.usbharu.hideout.worker import com.fasterxml.jackson.core.JsonParseException import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessor import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.external.job.InboxTask import dev.usbharu.hideout.core.external.job.InboxTaskDef import dev.usbharu.hideout.util.RsaUtil diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt index 7839bc01..9a73010f 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException import dev.usbharu.hideout.core.external.job.ReceiveFollowTask import dev.usbharu.hideout.core.external.job.ReceiveFollowTaskDef diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt index 9faec4c6..e7de84b0 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.external.job.UpdateActorTask import dev.usbharu.hideout.core.external.job.UpdateActorTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner From ccd089fa8ec81da9fd5c83201c99a25015dda20f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:49:59 +0900 Subject: [PATCH 13/54] wip --- .../DeleteLocalActorApplicationService.kt | 11 +- .../MigrationLocalActorApplicationService.kt | 14 +- .../RegisterLocalActorApplicationService.kt | 19 +-- ...AlsoKnownAsLocalActorApplicationService.kt | 2 +- .../SuspendLocalActorApplicationService.kt | 11 +- .../UnsuspendLocalActorApplicationService.kt | 9 +- .../post/DeleteLocalPostApplicationService.kt | 6 +- .../RegisterLocalPostApplicationService.kt | 13 +- .../post/UpdateLocalNoteApplicationService.kt | 6 +- .../core/domain/event/actor/ActorEvent.kt | 8 +- .../ActorInstanceRelationshipEvent.kt | 2 +- .../domain/event/instance/InstanceEvent.kt | 2 +- .../core/domain/event/post/PostEvent.kt | 8 +- .../event/relationship/RelationshipEvent.kt | 10 +- .../model/actor/{Actor2.kt => Actor.kt} | 78 +++------ .../domain/model/actor/ActorDescription.kt | 13 +- .../core/domain/model/actor/ActorId.kt | 2 +- .../core/domain/model/actor/ActorKeyId.kt | 2 +- .../core/domain/model/actor/ActorName.kt | 5 +- .../domain/model/actor/ActorPostsCount.kt | 6 +- .../domain/model/actor/ActorPrivateKey.kt | 2 +- .../core/domain/model/actor/ActorPublicKey.kt | 2 +- .../model/actor/ActorRelationshipCount.kt | 2 +- ...Actor2Repository.kt => ActorRepository.kt} | 10 +- .../domain/model/actor/ActorScreenName.kt | 12 +- .../ActorInstanceRelationship.kt | 2 +- .../ActorInstanceRelationshipRepository.kt | 2 +- .../domain/model/deletedActor/DeletedActor.kt | 32 ---- .../model/deletedActor/DeletedActorId.kt | 20 --- .../deletedActor/DeletedActorRepository.kt | 24 --- .../model/emoji/CustomEmojiRepository.kt | 2 - .../core/domain/model/emoji/EmojiId.kt | 2 +- .../core/domain/model/instance/Instance.kt | 3 - .../core/domain/model/instance/InstanceId.kt | 2 +- .../model/instance/InstanceRepository.kt | 1 - .../hideout/core/domain/model/media/Media.kt | 1 - .../core/domain/model/media/MediaBlurHash.kt | 2 +- .../domain/model/media/MediaRepository.kt | 6 +- .../domain/model/post/{Post2.kt => Post.kt} | 12 +- .../core/domain/model/post/PostContent.kt | 4 +- .../{Post2Repository.kt => PostRepository.kt} | 14 +- .../{Relationship2.kt => Relationship.kt} | 5 +- ...epository.kt => RelationshipRepository.kt} | 8 +- .../core/domain/model/shared/Domain.kt | 2 +- .../actor/RemoteActorCheckDomainService.kt | 6 +- .../actor/local/LocalActorDomainService.kt | 2 +- .../LocalActorMigrationCheckDomainService.kt | 6 +- .../ActorInstanceRelationshipDomainService.kt | 2 +- .../service/userdetail/PasswordEncoder.kt | 2 +- .../userdetail/UserDetailDomainService.kt | 2 +- .../domain/shared/domainevent/DomainEvent.kt | 2 +- .../shared/domainevent/DomainEventBody.kt | 2 +- .../domainevent/DomainEventPublisher.kt | 2 +- .../shared/domainevent/DomainEventStorable.kt | 2 +- .../domainevent/DomainEventSubscriber.kt | 2 +- .../DomainEventPublishableRepository.kt | 2 +- .../exposed/ActorQueryMapper.kt | 53 +++++++ .../exposed/ActorResultRowMapper.kt | 65 ++++++++ .../CustomEmojiRepositoryImpl.kt | 59 +++---- .../DeletedActorRepositoryImpl.kt | 106 ------------- ...epository.kt => ExposedActorRepository.kt} | 53 ++++--- .../ExposedFilterKeywordRepository.kt | 98 ------------ .../ExposedFilterRepository.kt | 104 ------------ ...Repository.kt => ExposedPostRepository.kt} | 93 +++++++---- .../ExposedTimelineRepository.kt | 150 ------------------ .../InstanceRepositoryImpl.kt | 74 +++++---- .../exposedrepository/MediaRepositoryImpl.kt | 111 +++++-------- .../exposedrepository/MetaRepositoryImpl.kt | 62 -------- .../UserDetailRepositoryImpl.kt | 41 +++-- .../factory/ActorDescriptionFactoryImpl.kt | 41 ----- ...tor2FactoryImpl.kt => ActorFactoryImpl.kt} | 23 +-- .../factory/ActorScreenNameFactoryImpl.kt | 45 ------ .../factory/PostContentFactoryImpl.kt | 2 +- .../infrastructure/factory/PostFactoryImpl.kt | 8 +- .../SpringFrameworkDomainEventPublisher.kt | 2 +- .../interfaces/api/auth/AuthController.kt | 22 +-- .../api/media/LocalFileController.kt | 26 +-- .../dev/usbharu/hideout/util/RsaUtil.kt | 1 - .../actor/{Actors2Test.kt => ActorsTest.kt} | 2 +- .../domain/model/actor/TestActor2Factory.kt | 6 +- 80 files changed, 523 insertions(+), 1155 deletions(-) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/{Actor2.kt => Actor.kt} (61%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/{Actor2Repository.kt => ActorRepository.kt} (80%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/{Post2.kt => Post.kt} (98%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/{Post2Repository.kt => PostRepository.kt} (72%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/{Relationship2.kt => Relationship.kt} (99%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/{Relationship2Repository.kt => RelationshipRepository.kt} (80%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/{ExposedActor2Repository.kt => ExposedActorRepository.kt} (70%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterKeywordRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/{ExposedPost2Repository.kt => ExposedPostRepository.kt} (74%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/{Actor2FactoryImpl.kt => ActorFactoryImpl.kt} (84%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt rename hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/{Actors2Test.kt => ActorsTest.kt} (96%) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt index cc6300c5..73ef9151 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt @@ -17,22 +17,21 @@ package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.stereotype.Service @Service class DeleteLocalActorApplicationService( private val transaction: Transaction, - private val actor2Repository: Actor2Repository, + private val actorRepository: ActorRepository, ) { suspend fun delete(actorId: Long, executor: ActorId) { transaction.transaction { val id = ActorId(actorId) - val findById = actor2Repository.findById(id)!! + val findById = actorRepository.findById(id)!! findById.delete() - actor2Repository.delete(findById) + actorRepository.delete(findById) } - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt index 0ff97802..6d147f4a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt @@ -17,8 +17,8 @@ package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService import org.springframework.stereotype.Service @@ -26,24 +26,23 @@ import org.springframework.stereotype.Service @Service class MigrationLocalActorApplicationService( private val transaction: Transaction, - private val actor2Repository: Actor2Repository, + private val actorRepository: ActorRepository, private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, ) { suspend fun migration(from: Long, to: Long, executor: ActorId) { transaction.transaction { - val fromActorId = ActorId(from) val toActorId = ActorId(to) - val fromActor = actor2Repository.findById(fromActorId)!! - val toActor = actor2Repository.findById(toActorId)!! + val fromActor = actorRepository.findById(fromActorId)!! + val toActor = actorRepository.findById(toActorId)!! val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor) when (canAccountMigration) { is AlreadyMoved -> TODO() is CanAccountMigration -> { fromActor.moveTo = toActorId - actor2Repository.save(fromActor) + actorRepository.save(fromActor) } is CircularReferences -> TODO() @@ -51,6 +50,5 @@ class MigrationLocalActorApplicationService( is AlsoKnownAsNotFound -> TODO() } } - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index d0cc63a7..f924330e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -19,22 +19,22 @@ package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository 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.actor.local.LocalActorDomainService import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService -import dev.usbharu.hideout.core.infrastructure.factory.Actor2FactoryImpl +import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl import org.springframework.stereotype.Service @Service class RegisterLocalActorApplicationService( private val transaction: Transaction, private val actorDomainService: LocalActorDomainService, - private val actor2Repository: Actor2Repository, - private val actor2FactoryImpl: Actor2FactoryImpl, + private val actorRepository: ActorRepository, + private val actorFactoryImpl: ActorFactoryImpl, private val instanceRepository: InstanceRepository, private val applicationConfig: ApplicationConfig, private val userDetailDomainService: UserDetailDomainService, @@ -44,26 +44,23 @@ class RegisterLocalActorApplicationService( suspend fun register(registerLocalActor: RegisterLocalActor) { transaction.transaction { if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) { - //todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ + // todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ throw Exception("Username already exists") } val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! - - val actor = actor2FactoryImpl.createLocal( + val actor = actorFactoryImpl.createLocal( registerLocalActor.name, actorDomainService.generateKeyPair(), instance.id ) - actor2Repository.save(actor) + actorRepository.save(actor) val userDetail = UserDetail.create( id = UserDetailId(idGenerateService.generateId()), actorId = actor.id, password = userDetailDomainService.hashPassword(registerLocalActor.password), ) userDetailRepository.save(userDetail) - } - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt index fe69e97d..3f676614 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SetAlsoKnownAsLocalActorApplicationService.kt @@ -5,4 +5,4 @@ import org.springframework.stereotype.Service @Service interface SetAlsoKnownAsLocalActorApplicationService { suspend fun setAlsoKnownAs(actorId: Long, alsoKnownAs: List) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt index ab07d9ed..082208b3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt @@ -17,24 +17,21 @@ package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.stereotype.Service @Service class SuspendLocalActorApplicationService( private val transaction: Transaction, - private val actor2Repository: Actor2Repository, + private val actorRepository: ActorRepository, ) { suspend fun suspend(actorId: Long, executor: ActorId) { transaction.transaction { - val id = ActorId(actorId) - val findById = actor2Repository.findById(id)!! + val findById = actorRepository.findById(id)!! findById.suspend = true } - - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt index 1948b0ea..baf0ab4b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UnsuspendLocalActorApplicationService.kt @@ -17,21 +17,20 @@ package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import org.springframework.stereotype.Service @Service class UnsuspendLocalActorApplicationService( private val transaction: Transaction, - private val actor2Repository: Actor2Repository, + private val actorRepository: ActorRepository, ) { suspend fun unsuspend(actorId: Long, executor: Long) { transaction.transaction { - val findById = actor2Repository.findById(ActorId(actorId))!! + val findById = actorRepository.findById(ActorId(actorId))!! findById.suspend = false } - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt index 46388563..b470ce4d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt @@ -16,15 +16,15 @@ package dev.usbharu.hideout.core.application.post -import dev.usbharu.hideout.core.domain.model.post.Post2Repository import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostRepository import org.springframework.stereotype.Service @Service -class DeleteLocalPostApplicationService(private val postRepository: Post2Repository) { +class DeleteLocalPostApplicationService(private val postRepository: PostRepository) { suspend fun delete(postId: Long) { val findById = postRepository.findById(PostId(postId))!! findById.delete() postRepository.save(findById) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index 99049b92..8aac1a77 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -16,27 +16,26 @@ package dev.usbharu.hideout.core.application.post -import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository 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.media.MediaId -import dev.usbharu.hideout.core.domain.model.post.Post2Repository import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import org.springframework.stereotype.Service @Service class RegisterLocalPostApplicationService( private val postFactory: PostFactoryImpl, - private val actor2Repository: Actor2Repository, - private val postRepository: Post2Repository, + private val actorRepository: ActorRepository, + private val postRepository: PostRepository, ) { suspend fun register(registerLocalPost: RegisterLocalPost) { - val actorId = ActorId(registerLocalPost.actorId) val post = postFactory.createLocal( actorId, - actor2Repository.findById(actorId)!!.name, + actorRepository.findById(actorId)!!.name, PostOverview(registerLocalPost.overview), registerLocalPost.content, registerLocalPost.visibility, @@ -48,4 +47,4 @@ class RegisterLocalPostApplicationService( postRepository.save(post) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt index 5df3f5f9..2a8c231e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt @@ -18,16 +18,16 @@ package dev.usbharu.hideout.core.application.post import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.media.MediaId -import dev.usbharu.hideout.core.domain.model.post.Post2Repository import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostOverview +import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl import org.springframework.stereotype.Service @Service class UpdateLocalNoteApplicationService( private val transaction: Transaction, - private val postRepository: Post2Repository, + private val postRepository: PostRepository, private val postContentFactoryImpl: PostContentFactoryImpl, ) { suspend fun update(updateLocalNote: UpdateLocalNote) { @@ -42,4 +42,4 @@ class UpdateLocalNoteApplicationService( postRepository.save(post) } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt index 49128421..a03f92e6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -16,11 +16,11 @@ package dev.usbharu.hideout.core.domain.event.actor -import dev.usbharu.hideout.core.domain.model.actor.Actor2 +import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody -class ActorDomainEventFactory(private val actor: Actor2) { +class ActorDomainEventFactory(private val actor: Actor) { fun createEvent(actorEvent: ActorEvent): DomainEvent { return DomainEvent.create( actorEvent.eventName, @@ -30,7 +30,7 @@ class ActorDomainEventFactory(private val actor: Actor2) { } } -class ActorEventBody(actor: Actor2) : DomainEventBody( +class ActorEventBody(actor: Actor) : DomainEventBody( mapOf( "actor" to actor ) @@ -43,4 +43,4 @@ enum class ActorEvent(val eventName: String, val collectable: Boolean = true) { move("ActorMove"), actorSuspend("ActorSuspend"), actorUnsuspend("ActorUnsuspend"), -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt index f2e0dd47..c5c6daf7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt @@ -44,4 +44,4 @@ enum class ActorInstanceRelationshipEvent(val eventName: String) { block("ActorInstanceBlock"), mute("ActorInstanceMute"), unmute("ActorInstanceUnmute"), -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt index 5a3b2f33..276d2f75 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt @@ -33,4 +33,4 @@ class InstanceEventBody(instance: Instance) : DomainEventBody(mapOf("instance" t enum class InstanceEvent(val eventName: String) { update("InstanceUpdate") -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt index 50eb3a50..1ae9ddb9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt @@ -16,11 +16,11 @@ package dev.usbharu.hideout.core.domain.event.post -import dev.usbharu.hideout.core.domain.model.post.Post2 +import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody -class PostDomainEventFactory(private val post: Post2) { +class PostDomainEventFactory(private val post: Post) { fun createEvent(postEvent: PostEvent): DomainEvent { return DomainEvent.create( postEvent.eventName, @@ -29,11 +29,11 @@ class PostDomainEventFactory(private val post: Post2) { } } -class PostEventBody(post: Post2) : DomainEventBody(mapOf("post" to post)) +class PostEventBody(post: Post) : DomainEventBody(mapOf("post" to post)) enum class PostEvent(val eventName: String) { delete("PostDelete"), update("PostUpdate"), create("PostCreate"), checkUpdate("PostCheckUpdate"), -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt index 4854475d..38183454 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt @@ -16,17 +16,17 @@ package dev.usbharu.hideout.core.domain.event.relationship -import dev.usbharu.hideout.core.domain.model.relationship.Relationship2 +import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody -class RelationshipEventFactory(private val relationship2: Relationship2) { +class RelationshipEventFactory(private val relationship: Relationship) { fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent { - return DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship2)) + return DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship)) } } -class RelationshipEventBody(relationship: Relationship2) : DomainEventBody(mapOf("relationship" to relationship)) +class RelationshipEventBody(relationship: Relationship) : DomainEventBody(mapOf("relationship" to relationship)) enum class RelationshipEvent(val eventName: String) { follow("RelationshipFollow"), @@ -37,4 +37,4 @@ enum class RelationshipEvent(val eventName: String) { unmute("RelationshipUnmute"), followRequest("RelationshipFollowRequest"), unfollowRequest("RelationshipUnfollowRequest"), -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt similarity index 61% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 78cc0864..702f3246 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -18,13 +18,14 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory 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.shared.Domain import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant -class Actor2 private constructor( +class Actor( val id: ActorId, val name: ActorName, val domain: Domain, @@ -49,6 +50,8 @@ class Actor2 private constructor( var lastUpdateAt: Instant = createdAt, alsoKnownAs: Set = emptySet(), moveTo: ActorId? = null, + emojiIds: Set, + deleted: Boolean, ) : DomainEventStorable() { var suspend = suspend @@ -74,9 +77,8 @@ class Actor2 private constructor( field = value } - - val emojis - get() = screenName.emojis + description.emojis + var emojis = emojiIds + private set var description = description set(value) { @@ -89,62 +91,28 @@ class Actor2 private constructor( field = value } + var deleted = deleted + private set fun delete() { - addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) + if (deleted.not()) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) + screenName = ActorScreenName.empty + description = ActorDescription.empty + emojis = emptySet() + lastPostAt = null + postsCount = ActorPostsCount.ZERO + followersCount = null + followingCount = null + } + } + + fun restore() { + deleted = false + checkUpdate() } fun checkUpdate() { addDomainEvent(ActorDomainEventFactory(this).createEvent(checkUpdate)) } - - abstract class Actor2Factory { - protected suspend fun internalCreate( - id: ActorId, - name: ActorName, - domain: Domain, - screenName: ActorScreenName, - description: ActorDescription, - inbox: URI, - outbox: URI, - url: URI, - publicKey: ActorPublicKey, - privateKey: ActorPrivateKey? = null, - createdAt: Instant, - keyId: ActorKeyId, - followersEndpoint: URI, - followingEndpoint: URI, - instance: InstanceId, - locked: Boolean, - followersCount: ActorRelationshipCount, - followingCount: ActorRelationshipCount, - postsCount: ActorPostsCount, - lastPostDate: Instant? = null, - suspend: Boolean, - ): Actor2 { - return Actor2( - id = id, - name = name, - domain = domain, - screenName = screenName, - description = description, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = privateKey, - createdAt = createdAt, - keyId = keyId, - followersEndpoint = followersEndpoint, - followingEndpoint = followingEndpoint, - instance = instance, - locked = locked, - followersCount = followersCount, - followingCount = followingCount, - postsCount = postsCount, - lastPostAt = lastPostDate, - suspend = suspend - ) - } - } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt index 8711377e..3d734d97 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -16,15 +16,10 @@ package dev.usbharu.hideout.core.domain.model.actor -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId - - -class ActorDescription private constructor(val description: String, val emojis: List) { +@JvmInline +value class ActorDescription(val description: String) { companion object { val length = 10000 + val empty = ActorDescription("") } - abstract class ActorDescriptionFactory { - protected suspend fun create(description: String, emojis: List): ActorDescription = - ActorDescription(description, emojis) - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt index 871df769..19e295f4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorId.kt @@ -24,4 +24,4 @@ value class ActorId(val id: Long) { companion object { val ghost = ActorId(0L) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt index 5b2fdb84..ccf98962 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorKeyId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorKeyId(val keyId: String) \ No newline at end of file +value class ActorKeyId(val keyId: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt index 14e4b850..d65cd986 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt @@ -18,11 +18,8 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline value class ActorName(val name: String) { - init { - - } companion object { val length = 300 } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt index d76b2f95..e24819e4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt @@ -24,4 +24,8 @@ value class ActorPostsCount(val postsCount: Int) { operator fun inc() = ActorPostsCount(postsCount + 1) operator fun dec() = ActorPostsCount(postsCount - 1) -} \ No newline at end of file + + companion object { + val ZERO = ActorPostsCount(0) + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt index 8dbb7fad..2777c3e5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorPrivateKey(val privateKey: String) \ No newline at end of file +value class ActorPrivateKey(val privateKey: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt index f94cb421..ac8cc06f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline -value class ActorPublicKey(val publicKey: String) \ No newline at end of file +value class ActorPublicKey(val publicKey: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt index 78bdfe09..7543996d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt @@ -24,4 +24,4 @@ value class ActorRelationshipCount(val relationshipCount: Int) { operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount + 1) operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount - 1) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt similarity index 80% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt index ddfccccd..0438660b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt @@ -16,8 +16,8 @@ package dev.usbharu.hideout.core.domain.model.actor -interface Actor2Repository { - suspend fun save(actor: Actor2): Actor2 - suspend fun delete(actor: Actor2) - suspend fun findById(id: ActorId): Actor2? -} \ No newline at end of file +interface ActorRepository { + suspend fun save(actor: Actor): Actor + suspend fun delete(actor: Actor) + suspend fun findById(id: ActorId): Actor? +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt index b8b80cfc..343e8f8a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt @@ -16,16 +16,10 @@ package dev.usbharu.hideout.core.domain.model.actor -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId - - -class ActorScreenName private constructor(val screenName: String, val emojis: List) { +@JvmInline +value class ActorScreenName(val screenName: String) { companion object { val length = 300 - } - - abstract class ActorScreenNameFactory { - protected suspend fun create(screenName: String, emojis: List): ActorScreenName = - ActorScreenName(screenName, emojis) + val empty = ActorScreenName("") } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt index eaf51768..bd1c0c1b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -85,4 +85,4 @@ data class ActorInstanceRelationship( result = 31 * result + instanceId.hashCode() return result } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt index 74c3cc02..5bc7abd5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt @@ -19,4 +19,4 @@ package dev.usbharu.hideout.core.domain.model.actorinstancerelationship interface ActorInstanceRelationshipRepository { suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship suspend fun delete(actorInstanceRelationship: ActorInstanceRelationship) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt deleted file mode 100644 index 61e15775..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActor.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.deletedActor - -import dev.usbharu.hideout.core.domain.model.actor.ActorName -import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey -import dev.usbharu.hideout.core.domain.model.shared.Domain -import java.net.URI -import java.time.Instant - -data class DeletedActor( - val id: DeletedActorId, - val name: ActorName, - val domain: Domain, - val apId: URI, - val publicKey: ActorPublicKey, - val deletedAt: Instant, -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt deleted file mode 100644 index b8cfbc98..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorId.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.deletedActor - -@JvmInline -value class DeletedActorId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt deleted file mode 100644 index 530e4738..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/deletedActor/DeletedActorRepository.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.model.deletedActor - -interface DeletedActorRepository { - suspend fun save(deletedActor: DeletedActor): DeletedActor - suspend fun delete(deletedActor: DeletedActor) - suspend fun findById(id: DeletedActorId): DeletedActor? - suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt index de339d34..dcda92d0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt @@ -17,10 +17,8 @@ package dev.usbharu.hideout.core.domain.model.emoji interface CustomEmojiRepository { - suspend fun generateId(): Long suspend fun save(customEmoji: CustomEmoji): CustomEmoji suspend fun findById(id: Long): CustomEmoji? suspend fun delete(customEmoji: CustomEmoji) - suspend fun findByNameAndDomain(name: String, domain: String): CustomEmoji? suspend fun findByNamesAndDomain(names: List, domain: String): List } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt index 4e7e16d6..0ba25e1a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/EmojiId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.emoji @JvmInline -value class EmojiId(val emojiId: Long) \ No newline at end of file +value class EmojiId(val emojiId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index f9d00f17..28e1f6d4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -37,7 +37,6 @@ class Instance( val createdAt: Instant, ) : DomainEventStorable() { - var iconUrl = iconUrl set(value) { addDomainEvent(InstanceEventFactory(this).createEvent(InstanceEvent.update)) @@ -56,6 +55,4 @@ class Instance( override fun hashCode(): Int { return id.hashCode() } - - } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt index 1ad754d9..66ed056b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceId.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.instance @JvmInline -value class InstanceId(val instanceId: Long) \ No newline at end of file +value class InstanceId(val instanceId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt index 7ab21f8f..5446d3bd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.domain.model.instance import java.net.URI interface InstanceRepository { - suspend fun generateId(): InstanceId suspend fun save(instance: Instance): Instance suspend fun findById(id: InstanceId): Instance? suspend fun delete(instance: Instance) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt index 06b5c92f..529eb3af 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt @@ -29,4 +29,3 @@ data class Media( val blurHash: MediaBlurHash?, val description: MediaDescription? = null, ) - diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt index ceee5d85..1b1dd054 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaBlurHash.kt @@ -17,4 +17,4 @@ package dev.usbharu.hideout.core.domain.model.media @JvmInline -value class MediaBlurHash(val hash: String) \ No newline at end of file +value class MediaBlurHash(val hash: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt index a3bd8e37..cc5321b8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/MediaRepository.kt @@ -17,9 +17,7 @@ package dev.usbharu.hideout.core.domain.model.media interface MediaRepository { - suspend fun generateId(): Long suspend fun save(media: Media): Media - suspend fun findById(id: Long): Media? - suspend fun delete(id: Long) - suspend fun findByRemoteUrl(remoteUrl: String): Media? + suspend fun findById(id: MediaId): Media? + suspend fun delete(media: Media) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt similarity index 98% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index 137820c7..d992a78e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -24,7 +24,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant -class Post2 private constructor( +class Post private constructor( val id: PostId, actorId: ActorId, overview: PostOverview? = null, @@ -143,7 +143,6 @@ class Post2 private constructor( content = PostContent.empty overview = null mediaIds = emptyList() - } deleted = true } @@ -157,6 +156,7 @@ class Post2 private constructor( this.content = content this.overview = overview this.mediaIds = mediaIds + checkUpdate() } var hide = hide @@ -182,7 +182,7 @@ class Post2 private constructor( if (this === other) return true if (javaClass != other?.javaClass) return false - other as Post2 + other as Post return id == other.id } @@ -207,8 +207,8 @@ class Post2 private constructor( deleted: Boolean, mediaIds: List, hide: Boolean, - ): Post2 { - return Post2( + ): Post { + return Post( id = id, actorId = actorId, overview = overview, @@ -226,4 +226,4 @@ class Post2 private constructor( ).apply { addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.create)) } } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index ee9e4e08..2471ba2a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -29,7 +29,9 @@ class PostContent private constructor(val text: String, val content: String, val abstract class PostContentFactory { protected suspend fun create(text: String, content: String, emojiIds: List): PostContent { return PostContent( - text, content, emojiIds + text, + content, + emojiIds ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt similarity index 72% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt index 26bdb9f0..ddd781a7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostRepository.kt @@ -18,10 +18,10 @@ package dev.usbharu.hideout.core.domain.model.post import dev.usbharu.hideout.core.domain.model.actor.ActorId -interface Post2Repository { - suspend fun save(post: Post2): Post2 - suspend fun saveAll(posts: List): List - suspend fun findById(id: PostId): Post2? - suspend fun findByActorId(id: ActorId): List - suspend fun delete(post: Post2) -} \ No newline at end of file +interface PostRepository { + suspend fun save(post: Post): Post + suspend fun saveAll(posts: List): List + suspend fun findById(id: PostId): Post? + suspend fun findByActorId(id: ActorId): List + suspend fun delete(post: Post) +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt similarity index 99% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt index 5b735964..dd06c900 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt @@ -21,7 +21,7 @@ import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventFacto import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable -class Relationship2( +class Relationship( val actorId: ActorId, val targetActorId: ActorId, following: Boolean, @@ -43,7 +43,6 @@ class Relationship2( var mutingFollowRequest: Boolean = mutingFollowRequest private set - fun follow() { require(blocking.not()) following = true @@ -103,4 +102,4 @@ class Relationship2( fun rejectFollowRequest() { followRequesting = false } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt similarity index 80% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index c04e6106..6b84b91c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.domain.model.relationship -interface Relationship2Repository { - suspend fun save(relationship2: Relationship2): Relationship2 - suspend fun delete(relationship2: Relationship2) -} \ No newline at end of file +interface RelationshipRepository { + suspend fun save(relationship: Relationship): Relationship + suspend fun delete(relationship: Relationship) +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt index 95cbff75..7ac333af 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt @@ -21,4 +21,4 @@ value class Domain(val domain: String) { companion object { val length = 1000 } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt index 24c5b325..c4ef154b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt @@ -17,14 +17,14 @@ package dev.usbharu.hideout.core.domain.service.actor import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.Actor2 +import dev.usbharu.hideout.core.domain.model.actor.Actor import org.springframework.stereotype.Service interface IRemoteActorCheckDomainService { - fun isRemoteActor(actor: Actor2): Boolean + fun isRemoteActor(actor: Actor): Boolean } @Service class RemoteActorCheckDomainService(private val applicationConfig: ApplicationConfig) : IRemoteActorCheckDomainService { - override fun isRemoteActor(actor: Actor2): Boolean = actor.domain.domain == applicationConfig.url.host + override fun isRemoteActor(actor: Actor): Boolean = actor.domain.domain == applicationConfig.url.host } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt index 677654f7..1dafb03d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainService.kt @@ -22,4 +22,4 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey interface LocalActorDomainService { suspend fun usernameAlreadyUse(name: String): Boolean suspend fun generateKeyPair(): Pair -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt index dfa8c9c5..3c1adf00 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt @@ -16,10 +16,10 @@ package dev.usbharu.hideout.core.domain.service.actor.local -import dev.usbharu.hideout.core.domain.model.actor.Actor2 +import dev.usbharu.hideout.core.domain.model.actor.Actor interface LocalActorMigrationCheckDomainService { - suspend fun canAccountMigration(from: Actor2, to: Actor2): AccountMigrationCheck + suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck } sealed class AccountMigrationCheck( @@ -34,4 +34,4 @@ sealed class AccountMigrationCheck( class AlreadyMoved(val message: String) : AccountMigrationCheck(false) class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt index ac573d8c..d47ef39a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actorinstancerelationship/ActorInstanceRelationshipDomainService.kt @@ -18,4 +18,4 @@ package dev.usbharu.hideout.core.domain.service.actorinstancerelationship interface ActorInstanceRelationshipDomainService { suspend fun block() -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt index 9ff2ee3d..979bc1e0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/PasswordEncoder.kt @@ -18,4 +18,4 @@ package dev.usbharu.hideout.core.domain.service.userdetail interface PasswordEncoder { suspend fun encode(input: String): String -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt index 33db1331..cb3fb074 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/userdetail/UserDetailDomainService.kt @@ -22,4 +22,4 @@ import org.springframework.stereotype.Service @Service class UserDetailDomainService(private val passwordEncoder: PasswordEncoder) { suspend fun hashPassword(password: String) = UserDetailHashedPassword(passwordEncoder.encode(password)) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt index cb5c711b..a30cb94c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt @@ -50,4 +50,4 @@ data class DomainEvent( return DomainEvent(id, name, occurredOn, body, collectable) } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt index fed5479d..8a46bb19 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt @@ -20,4 +20,4 @@ abstract class DomainEventBody(val map: Map) { fun toMap(): Map { return map } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt index 59d19150..7f7cd592 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventPublisher.kt @@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.shared.domainevent interface DomainEventPublisher { suspend fun publishEvent(domainEvent: DomainEvent) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt index dd0c6e60..3fedb624 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt @@ -26,4 +26,4 @@ abstract class DomainEventStorable { fun clearDomainEvents() = domainEvents.clear() fun getDomainEvents(): List = domainEvents.toList() -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt index 8d529739..5d24a1b7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventSubscriber.kt @@ -4,4 +4,4 @@ interface DomainEventSubscriber { fun subscribe(eventName: String, domainEventConsumer: DomainEventConsumer) } -typealias DomainEventConsumer = (DomainEvent) -> Unit \ No newline at end of file +typealias DomainEventConsumer = (DomainEvent) -> Unit diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt index d542a825..3b385984 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/repository/DomainEventPublishableRepository.kt @@ -19,4 +19,4 @@ interface DomainEventPublishableRepository { } entity.clearDomainEvents() } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt new file mode 100644 index 00000000..f1c99847 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposed + +import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper +import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper +import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors +import dev.usbharu.hideout.core.infrastructure.exposedrepository.ActorsAlsoKnownAs +import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component + +@Component +class ActorQueryMapper(private val actorResultRowMapper: ResultRowMapper) : QueryMapper { + override fun map(query: Query): List { + return query + .groupBy { it[Actors.id] } + .map { it.value } + .map { + it + .first() + .let(actorResultRowMapper::map) + .apply { + alsoKnownAs = it.mapNotNull { resultRow: ResultRow -> + resultRow.getOrNull( + ActorsAlsoKnownAs.alsoKnownAs + )?.let { actorId -> + ActorId( + actorId + ) + } + }.toSet() + clearDomainEvents() + } + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt new file mode 100644 index 00000000..a75c148d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposed + +import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper +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.shared.Domain +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component +import java.net.URI + +@Component +class ActorResultRowMapper : ResultRowMapper { + override fun map(resultRow: ResultRow): Actor { + return Actor( + id = ActorId(resultRow[Actors.id]), + name = ActorName(resultRow[Actors.name]), + domain = Domain(resultRow[Actors.domain]), + screenName = ActorScreenName(resultRow[Actors.screenName]), + description = ActorDescription(resultRow[Actors.description]), + inbox = URI.create(resultRow[Actors.inbox]), + outbox = URI.create(resultRow[Actors.outbox]), + url = URI.create(resultRow[Actors.url]), + publicKey = ActorPublicKey(resultRow[Actors.publicKey]), + privateKey = resultRow[Actors.privateKey]?.let { ActorPrivateKey(it) }, + createdAt = resultRow[Actors.createdAt], + keyId = ActorKeyId(resultRow[Actors.keyId]), + followersEndpoint = resultRow[Actors.followers]?.let { URI.create(it) }, + followingEndpoint = resultRow[Actors.following]?.let { URI.create(it) }, + instance = InstanceId(resultRow[Actors.instance]), + locked = resultRow[Actors.locked], + followersCount = resultRow[Actors.followersCount]?.let { ActorRelationshipCount(it) }, + followingCount = resultRow[Actors.followingCount]?.let { ActorRelationshipCount(it) }, + postsCount = ActorPostsCount(resultRow[Actors.postsCount]), + lastPostAt = resultRow[Actors.lastPostAt], + suspend = resultRow[Actors.suspend], + lastUpdateAt = resultRow[Actors.lastUpdateAt], + alsoKnownAs = emptySet(), + moveTo = resultRow[Actors.moveTo]?.let { ActorId(it) }, + emojiIds = resultRow[Actors.emojis] + .split(",") + .filter { it.isNotEmpty() } + .map { EmojiId(it.toLong()) } + .toSet(), + deleted = resultRow[Actors.deleted] + ) + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt index a73378ad..cd50d5c4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt @@ -16,9 +16,11 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.application.service.id.IdGenerateService 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 org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.javatime.CurrentTimestamp @@ -26,34 +28,33 @@ import org.jetbrains.exposed.sql.javatime.timestamp import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository +import java.net.URI @Repository -class CustomEmojiRepositoryImpl(private val idGenerateService: IdGenerateService) : CustomEmojiRepository, +class CustomEmojiRepositoryImpl : CustomEmojiRepository, AbstractRepository() { override val logger: Logger get() = Companion.logger - override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(customEmoji: CustomEmoji): CustomEmoji = query { val singleOrNull = - CustomEmojis.selectAll().where { CustomEmojis.id eq customEmoji.id }.forUpdate().singleOrNull() + CustomEmojis.selectAll().where { CustomEmojis.id eq customEmoji.id.emojiId }.forUpdate().singleOrNull() if (singleOrNull == null) { CustomEmojis.insert { - it[id] = customEmoji.id + it[id] = customEmoji.id.emojiId it[name] = customEmoji.name - it[domain] = customEmoji.domain - it[instanceId] = customEmoji.instanceId - it[url] = customEmoji.url + it[domain] = customEmoji.domain.domain + it[instanceId] = customEmoji.instanceId.instanceId + it[url] = customEmoji.url.toString() it[category] = customEmoji.category it[createdAt] = customEmoji.createdAt } } else { - CustomEmojis.update({ CustomEmojis.id eq customEmoji.id }) { + CustomEmojis.update({ CustomEmojis.id eq customEmoji.id.emojiId }) { it[name] = customEmoji.name - it[domain] = customEmoji.domain - it[instanceId] = customEmoji.instanceId - it[url] = customEmoji.url + it[domain] = customEmoji.domain.domain + it[instanceId] = customEmoji.instanceId.instanceId + it[url] = customEmoji.url.toString() it[category] = customEmoji.category it[createdAt] = customEmoji.createdAt } @@ -66,14 +67,16 @@ class CustomEmojiRepositoryImpl(private val idGenerateService: IdGenerateService } override suspend fun delete(customEmoji: CustomEmoji): Unit = query { - CustomEmojis.deleteWhere { id eq customEmoji.id } + CustomEmojis.deleteWhere { id eq customEmoji.id.emojiId } } - override suspend fun findByNameAndDomain(name: String, domain: String): CustomEmoji? = query { - return@query CustomEmojis - .selectAll().where { CustomEmojis.name eq name and (CustomEmojis.domain eq domain) } - .singleOrNull() - ?.toCustomEmoji() + override suspend fun findByNamesAndDomain(names: List, domain: String): List { + return CustomEmojis + .selectAll() + .where { + CustomEmojis.name inList names and (CustomEmojis.domain eq domain) + } + .map { it.toCustomEmoji() } } companion object { @@ -82,22 +85,22 @@ class CustomEmojiRepositoryImpl(private val idGenerateService: IdGenerateService } fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji( - id = this[CustomEmojis.id], + id = EmojiId(this[CustomEmojis.id]), name = this[CustomEmojis.name], - domain = this[CustomEmojis.domain], - instanceId = this[CustomEmojis.instanceId], - url = this[CustomEmojis.url], + domain = Domain(this[CustomEmojis.domain]), + instanceId = InstanceId(this[CustomEmojis.instanceId]), + url = URI.create(this[CustomEmojis.url]), category = this[CustomEmojis.category], createdAt = this[CustomEmojis.createdAt] ) fun ResultRow.toCustomEmojiOrNull(): CustomEmoji? { return CustomEmoji( - id = this.getOrNull(CustomEmojis.id) ?: return null, + id = EmojiId(this.getOrNull(CustomEmojis.id) ?: return null), name = this.getOrNull(CustomEmojis.name) ?: return null, - domain = this.getOrNull(CustomEmojis.domain) ?: return null, - instanceId = this[CustomEmojis.instanceId], - url = this.getOrNull(CustomEmojis.url) ?: return null, + domain = Domain(this.getOrNull(CustomEmojis.domain) ?: return null), + instanceId = InstanceId(this.getOrNull(CustomEmojis.instanceId) ?: return null), + url = URI.create(this.getOrNull(CustomEmojis.url) ?: return null), category = this[CustomEmojis.category], createdAt = this.getOrNull(CustomEmojis.createdAt) ?: return null ) @@ -107,7 +110,7 @@ object CustomEmojis : Table("emojis") { val id = long("id") val name = varchar("name", 1000) val domain = varchar("domain", 1000) - val instanceId = long("instance_id").references(Instance.id).nullable() + val instanceId = long("instance_id").references(Instance.id) val url = varchar("url", 255).uniqueIndex() val category = varchar("category", 255).nullable() val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt deleted file mode 100644 index a6900563..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/DeletedActorRepositoryImpl.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Repository - -@Repository -class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun save(deletedActor: DeletedActor): DeletedActor = query { - val singleOrNull = - DeletedActors.selectAll().where { DeletedActors.id eq deletedActor.id }.forUpdate().singleOrNull() - - if (singleOrNull == null) { - DeletedActors.insert { - it[id] = deletedActor.id - it[name] = deletedActor.name - it[domain] = deletedActor.domain - it[apId] = deletedActor.apId - it[publicKey] = deletedActor.publicKey - it[deletedAt] = deletedActor.deletedAt - } - } else { - DeletedActors.update({ DeletedActors.id eq deletedActor.id }) { - it[name] = deletedActor.name - it[domain] = deletedActor.domain - it[apId] = deletedActor.apId - it[publicKey] = deletedActor.publicKey - it[deletedAt] = deletedActor.deletedAt - } - } - return@query deletedActor - } - - override suspend fun delete(deletedActor: DeletedActor): Unit = query { - DeletedActors.deleteWhere { id eq deletedActor.id } - } - - override suspend fun findById(id: Long): DeletedActor? = query { - return@query DeletedActors - .selectAll().where { DeletedActors.id eq id } - .singleOrNull() - ?.toDeletedActor() - } - - override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? = query { - return@query DeletedActors - .selectAll().where { DeletedActors.name eq name and (DeletedActors.domain eq domain) } - .singleOrNull() - ?.toDeletedActor() - } - - companion object { - private val logger = LoggerFactory.getLogger(DeletedActorRepositoryImpl::class.java) - } -} - -fun ResultRow.toDeletedActor(): DeletedActor = deletedActor(this) - -private fun deletedActor(singleOr: ResultRow): DeletedActor { - return DeletedActor( - id = singleOr[DeletedActors.id], - name = singleOr[DeletedActors.name], - domain = singleOr[DeletedActors.domain], - apId = singleOr[DeletedActors.publicKey], - publicKey = singleOr[DeletedActors.apId], - deletedAt = singleOr[DeletedActors.deletedAt] - ) -} - -object DeletedActors : Table("deleted_actors") { - val id = long("id") - val name = varchar("name", 300) - val domain = varchar("domain", 255) - val apId = varchar("ap_id", 255).uniqueIndex() - val publicKey = varchar("public_key", 10000).uniqueIndex() - val deletedAt = timestamp("deleted_at") - override val primaryKey: PrimaryKey = PrimaryKey(id) - - init { - uniqueIndex(name, domain) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt similarity index 70% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index 1645d927..466cf138 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActor2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository +import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper import dev.usbharu.hideout.core.domain.model.actor.* import dev.usbharu.hideout.core.domain.model.shared.Domain import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher @@ -12,18 +13,22 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedActor2Repository(override val domainEventPublisher: DomainEventPublisher) : AbstractRepository(), - DomainEventPublishableRepository, Actor2Repository { +class ExposedActorRepository( + private val actorQueryMapper: QueryMapper, + override val domainEventPublisher: DomainEventPublisher, +) : AbstractRepository(), + DomainEventPublishableRepository, + ActorRepository { override val logger: Logger get() = Companion.logger companion object { - private val logger = LoggerFactory.getLogger(ExposedActor2Repository::class.java) + private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java) } - override suspend fun save(actor: Actor2): Actor2 { + override suspend fun save(actor: Actor): Actor { query { - Actors2.upsert { + Actors.upsert { it[id] = actor.id.id it[name] = actor.name.name it[domain] = actor.domain.domain @@ -49,32 +54,41 @@ class ExposedActor2Repository(override val domainEventPublisher: DomainEventPubl it[moveTo] = actor.moveTo?.id it[emojis] = actor.emojis.joinToString(",") } - Actors2AlsoKnownAs.deleteWhere { + ActorsAlsoKnownAs.deleteWhere { actorId eq actor.id.id } - Actors2AlsoKnownAs.batchInsert(actor.alsoKnownAs) { - this[Actors2AlsoKnownAs.actorId] = actor.id.id - this[Actors2AlsoKnownAs.alsoKnownAs] = it.id + ActorsAlsoKnownAs.batchInsert(actor.alsoKnownAs) { + this[ActorsAlsoKnownAs.actorId] = actor.id.id + this[ActorsAlsoKnownAs.alsoKnownAs] = it.id } } update(actor) return actor } - override suspend fun delete(actor: Actor2) { + override suspend fun delete(actor: Actor) { query { - Actors2.deleteWhere { id eq actor.id.id } - Actors2AlsoKnownAs.deleteWhere { actorId eq actor.id.id } + Actors.deleteWhere { id eq actor.id.id } + ActorsAlsoKnownAs.deleteWhere { actorId eq actor.id.id } } update(actor) } - override suspend fun findById(id: ActorId): Actor2? { - TODO() + override suspend fun findById(id: ActorId): Actor? { + return query { + Actors + .leftJoin(ActorsAlsoKnownAs, onColumn = { Actors.id }, otherColumn = { actorId }) + .selectAll() + .where { + Actors.id eq id.id + } + .let(actorQueryMapper::map) + .first() + } } } -object Actors2 : Table("actors") { +object Actors : Table("actors") { val id = long("id") val name = varchar("name", ActorName.length) val domain = varchar("domain", Domain.length) @@ -99,6 +113,7 @@ object Actors2 : Table("actors") { val suspend = bool("suspend") val moveTo = long("move_to").references(id).nullable() val emojis = varchar("emojis", 3000) + val deleted = bool("deleted") override val primaryKey = PrimaryKey(id) @@ -107,10 +122,10 @@ object Actors2 : Table("actors") { } } -object Actors2AlsoKnownAs : Table("actor_alsoknwonas") { +object ActorsAlsoKnownAs : Table("actor_alsoknwonas") { val actorId = - long("actor_id").references(Actors2.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) - val alsoKnownAs = long("alsoKnownAs").references(Actors2.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) + val alsoKnownAs = long("alsoKnownAs").references(Actors.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) override val primaryKey: PrimaryKey = PrimaryKey(actorId, alsoKnownAs) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterKeywordRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterKeywordRepository.kt deleted file mode 100644 index 23d6f0c8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterKeywordRepository.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.filter.FilterMode -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository -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 ExposedFilterKeywordRepository(private val idGenerateService: IdGenerateService) : FilterKeywordRepository, - AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(filterKeyword: FilterKeyword): FilterKeyword = query { - val empty = FilterKeywords.selectAll().where { FilterKeywords.id eq filterKeyword.id }.empty() - if (empty) { - FilterKeywords.insert { - it[id] = filterKeyword.id - it[filterId] = filterKeyword.filterId - it[keyword] = filterKeyword.keyword - it[mode] = filterKeyword.mode.name - } - } else { - FilterKeywords.update({ FilterKeywords.id eq filterKeyword.id }) { - it[filterId] = filterKeyword.filterId - it[keyword] = filterKeyword.keyword - it[mode] = filterKeyword.mode.name - } - } - filterKeyword - } - - override suspend fun saveAll(filterKeywordList: List): Unit = query { - FilterKeywords.batchInsert(filterKeywordList, ignore = true) { - this[FilterKeywords.id] = it.id - this[FilterKeywords.filterId] = it.filterId - this[FilterKeywords.keyword] = it.keyword - this[FilterKeywords.mode] = it.mode.name - } - } - - override suspend fun findById(id: Long): FilterKeyword? = query { - return@query FilterKeywords.selectAll().where { FilterKeywords.id eq id }.singleOrNull()?.toFilterKeyword() - } - - override suspend fun deleteById(id: Long): Unit = query { - FilterKeywords.deleteWhere { FilterKeywords.id eq id } - } - - override suspend fun deleteByFilterId(filterId: Long): Unit = query { - FilterKeywords.deleteWhere { FilterKeywords.filterId eq filterId } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedFilterKeywordRepository::class.java) - } -} - -fun ResultRow.toFilterKeyword(): FilterKeyword { - return FilterKeyword( - this[FilterKeywords.id], - this[FilterKeywords.filterId], - this[FilterKeywords.keyword], - this[FilterKeywords.mode].let { FilterMode.valueOf(it) } - ) -} - -object FilterKeywords : Table("filter_keywords") { - val id = long("id") - val filterId = long("filter_id").references(Filters.id) - val keyword = varchar("keyword", 1000) - val mode = varchar("mode", 100) - - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt deleted file mode 100644 index 69aec93b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.filter.Filter -import dev.usbharu.hideout.core.domain.model.filter.FilterAction -import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.filter.FilterType -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 ExposedFilterRepository(private val idGenerateService: IdGenerateService) : FilterRepository, - AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(filter: Filter): Filter = query { - val empty = Filters.selectAll().where { - Filters.id eq filter.id - }.forUpdate().empty() - if (empty) { - Filters.insert { - it[id] = filter.id - it[userId] = filter.userId - it[name] = filter.name - it[context] = filter.context.joinToString(",") { filterType -> filterType.name } - it[filterAction] = filter.filterAction.name - } - } else { - Filters.update({ Filters.id eq filter.id }) { - it[userId] = filter.userId - it[name] = filter.name - it[context] = filter.context.joinToString(",") { filterType -> filterType.name } - it[filterAction] = filter.filterAction.name - } - } - filter - } - - override suspend fun findById(id: Long): Filter? = query { - return@query Filters.selectAll().where { Filters.id eq id }.singleOrNull()?.toFilter() - } - - override suspend fun findByUserIdAndId(userId: Long, id: Long): Filter? = query { - return@query Filters.selectAll().where { Filters.userId eq userId and (Filters.id eq id) }.singleOrNull() - ?.toFilter() - } - - override suspend fun findByUserIdAndType(userId: Long, types: List): List = query { - return@query Filters.selectAll().where { Filters.userId eq userId }.map { it.toFilter() } - .filter { it.context.containsAll(types) } - } - - override suspend fun deleteById(id: Long): Unit = query { - Filters.deleteWhere { Filters.id eq id } - } - - override suspend fun deleteByUserIdAndId(userId: Long, id: Long) { - Filters.deleteWhere { Filters.userId eq userId and (Filters.id eq id) } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedFilterRepository::class.java) - } -} - -fun ResultRow.toFilter(): Filter = Filter( - this[Filters.id], - this[Filters.userId], - this[Filters.name], - this[Filters.context].split(",").filterNot(String::isEmpty).map { FilterType.valueOf(it) }, - this[Filters.filterAction].let { FilterAction.valueOf(it) } -) - -object Filters : Table() { - val id = long("id") - val userId = long("user_id").references(Actors.id) - val name = varchar("name", 255) - val context = varchar("context", 500) - val filterAction = varchar("action", 255) - - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt similarity index 74% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 9abb2bbe..8115fc25 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPost2Repository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -20,34 +20,37 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.post.* import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.actorId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.apId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.content -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.createdAt -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.deleted -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.hide -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.id -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.moveTo -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.overview -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.replyId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.repostId -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.sensitive -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.text -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.url -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.visibility +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.actorId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.apId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.content +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.createdAt +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.deleted +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.hide +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.id +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.moveTo +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.overview +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.replyId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.repostId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.sensitive +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.text +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.url +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.visibility import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList import org.jetbrains.exposed.sql.javatime.timestamp import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedPost2Repository(override val domainEventPublisher: DomainEventPublisher) : Post2Repository, - AbstractRepository(), DomainEventPublishableRepository { - override suspend fun save(post: Post2): Post2 { +class ExposedPostRepository(override val domainEventPublisher: DomainEventPublisher) : + PostRepository, + AbstractRepository(), + DomainEventPublishableRepository { + override suspend fun save(post: Post): Post { query { - Posts2.upsert { + Posts.upsert { it[id] = post.id.id it[actorId] = post.actorId.id it[overview] = post.overview?.overview @@ -70,6 +73,9 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli PostsEmojis.deleteWhere { postId eq post.id.id } + PostsVisibleActors.deleteWhere { + postId eq post.id.id + } PostsMedia.batchInsert(post.mediaIds) { this[PostsMedia.postId] = post.id.id this[PostsMedia.mediaId] = it.id @@ -78,14 +84,18 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli this[PostsEmojis.postId] = post.id.id this[PostsEmojis.emojiId] = it.emojiId } + PostsVisibleActors.batchInsert(post.visibleActors) { + this[PostsVisibleActors.postId] = post.id.id + this[PostsVisibleActors.actorId] = it.id + } } update(post) return post } - override suspend fun saveAll(posts: List): List { + override suspend fun saveAll(posts: List): List { query { - Posts2.batchUpsert(posts, id) { + Posts.batchUpsert(posts, id) { this[id] = it.id.id this[actorId] = it.actorId.id this[overview] = it.overview?.overview @@ -103,15 +113,30 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli this[moveTo] = it.moveTo?.id } val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id.id to it.id } } - PostsMedia.batchUpsert(mediaIds, PostsMedia.postId) { + val postsIds = posts.map { it.id.id } + PostsMedia.deleteWhere { + postId inList postsIds + } + PostsMedia.batchInsert(mediaIds) { this[PostsMedia.postId] = it.first this[PostsMedia.mediaId] = it.second } val emojiIds = posts.flatMap { post -> post.emojiIds.map { post.id.id to it.emojiId } } - PostsEmojis.batchUpsert(emojiIds, PostsEmojis.postId) { + PostsEmojis.deleteWhere { + postId inList postsIds + } + PostsEmojis.batchInsert(emojiIds) { this[PostsEmojis.postId] = it.first this[PostsEmojis.emojiId] = it.second } + val visibleActors = posts.flatMap { post -> post.visibleActors.map { post.id.id to it.id } } + PostsVisibleActors.deleteWhere { + postId inList postsIds + } + PostsVisibleActors.batchInsert(visibleActors) { + this[PostsVisibleActors.postId] = it.first + this[PostsVisibleActors.actorId] = it.second + } } posts.forEach { update(it) @@ -119,17 +144,17 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli return posts } - override suspend fun findById(id: PostId): Post2? { + override suspend fun findById(id: PostId): Post? { TODO("Not yet implemented") } - override suspend fun findByActorId(id: ActorId): List { + override suspend fun findByActorId(id: ActorId): List { TODO("Not yet implemented") } - override suspend fun delete(post: Post2) { + override suspend fun delete(post: Post) { query { - Posts2.deleteWhere { + Posts.deleteWhere { id eq post.id.id } } @@ -139,13 +164,13 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli override val logger: Logger = Companion.logger companion object { - private val logger = LoggerFactory.getLogger(ExposedPost2Repository::class.java) + private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java) } } -object Posts2 : Table("posts") { +object Posts : Table("posts") { val id = long("id") - val actorId = long("actor_id").references(Actors2.id) + val actorId = long("actor_id").references(Actors.id) val overview = varchar("overview", PostOverview.length).nullable() val content = varchar("content", PostContent.contentLength) val text = varchar("text", PostContent.textLength) @@ -159,7 +184,6 @@ object Posts2 : Table("posts") { val deleted = bool("deleted") val hide = bool("hide") val moveTo = long("move_to").references(id).nullable() - } object PostsMedia : Table("posts_media") { @@ -172,4 +196,9 @@ object PostsEmojis : Table("posts_emojis") { val postId = long("post_id").references(id) val emojiId = long("emoji_id").references(CustomEmojis.id) override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId) -} \ No newline at end of file +} + +object PostsVisibleActors : Table("posts_visible_actors") { + val postId = long("post_id").references(id) + val actorId = long("actor_id").references(Actors.id) +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt deleted file mode 100644 index 79733294..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedTimelineRepository.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Visibility -import org.jetbrains.exposed.sql.* -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.stereotype.Repository - -@Repository -@Qualifier("jdbc") -@ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true) -class ExposedTimelineRepository(private val idGenerateService: IdGenerateService) : TimelineRepository, - AbstractRepository() { - override val logger: Logger - get() = Companion.logger - - override suspend fun generateId(): Long = idGenerateService.generateId() - - override suspend fun save(timeline: Timeline): Timeline = query { - if (Timelines.selectAll().where { Timelines.id eq timeline.id }.forUpdate().singleOrNull() == null) { - Timelines.insert { - it[id] = timeline.id - it[userId] = timeline.userId - it[timelineId] = timeline.timelineId - it[postId] = timeline.postId - it[postActorId] = timeline.postActorId - it[createdAt] = timeline.createdAt - it[replyId] = timeline.replyId - it[repostId] = timeline.repostId - it[visibility] = timeline.visibility.ordinal - it[sensitive] = timeline.sensitive - it[isLocal] = timeline.isLocal - it[isPureRepost] = timeline.isPureRepost - it[mediaIds] = timeline.mediaIds.joinToString(",") - it[emojiIds] = timeline.emojiIds.joinToString(",") - } - } else { - Timelines.update({ Timelines.id eq timeline.id }) { - it[userId] = timeline.userId - it[timelineId] = timeline.timelineId - it[postId] = timeline.postId - it[postActorId] = timeline.postActorId - it[createdAt] = timeline.createdAt - it[replyId] = timeline.replyId - it[repostId] = timeline.repostId - it[visibility] = timeline.visibility.ordinal - it[sensitive] = timeline.sensitive - it[isLocal] = timeline.isLocal - it[isPureRepost] = timeline.isPureRepost - it[mediaIds] = timeline.mediaIds.joinToString(",") - it[emojiIds] = timeline.emojiIds.joinToString(",") - } - } - return@query timeline - } - - override suspend fun saveAll(timelines: List): List = query { - Timelines.batchInsert(timelines, true, false) { - this[Timelines.id] = it.id - this[Timelines.userId] = it.userId - this[Timelines.timelineId] = it.timelineId - this[Timelines.postId] = it.postId - this[Timelines.postActorId] = it.postActorId - this[Timelines.createdAt] = it.createdAt - this[Timelines.replyId] = it.replyId - this[Timelines.repostId] = it.repostId - this[Timelines.visibility] = it.visibility.ordinal - this[Timelines.sensitive] = it.sensitive - this[Timelines.isLocal] = it.isLocal - this[Timelines.isPureRepost] = it.isPureRepost - this[Timelines.mediaIds] = it.mediaIds.joinToString(",") - this[Timelines.emojiIds] = it.emojiIds.joinToString(",") - } - return@query timelines - } - - override suspend fun findByUserId(id: Long): List = query { - return@query Timelines.selectAll().where { Timelines.userId eq id }.map { it.toTimeline() } - } - - override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List = query { - return@query Timelines.selectAll().where { Timelines.userId eq userId and (Timelines.timelineId eq timelineId) } - .map { it.toTimeline() } - } - - companion object { - private val logger = LoggerFactory.getLogger(ExposedTimelineRepository::class.java) - } -} - -fun ResultRow.toTimeline(): Timeline { - return Timeline( - id = this[Timelines.id], - userId = this[Timelines.userId], - timelineId = this[Timelines.timelineId], - postId = this[Timelines.postId], - postActorId = this[Timelines.postActorId], - createdAt = this[Timelines.createdAt], - replyId = this[Timelines.replyId], - repostId = this[Timelines.repostId], - visibility = Visibility.values().first { it.ordinal == this[Timelines.visibility] }, - sensitive = this[Timelines.sensitive], - isLocal = this[Timelines.isLocal], - isPureRepost = this[Timelines.isPureRepost], - mediaIds = this[Timelines.mediaIds].split(",").mapNotNull { it.toLongOrNull() }, - emojiIds = this[Timelines.emojiIds].split(",").mapNotNull { it.toLongOrNull() } - ) -} - -object Timelines : Table("timelines") { - val id = long("id") - val userId = long("user_id") - val timelineId = long("timeline_id") - val postId = long("post_id") - val postActorId = long("post_actor_id") - val createdAt = long("created_at") - val replyId = long("reply_id").nullable() - val repostId = long("repost_id").nullable() - val visibility = integer("visibility") - val sensitive = bool("sensitive") - val isLocal = bool("is_local") - val isPureRepost = bool("is_pure_repost") - val mediaIds = varchar("media_ids", 255) - val emojiIds = varchar("emoji_ids", 255) - - override val primaryKey: PrimaryKey = PrimaryKey(id) - - init { - uniqueIndex(userId, timelineId, postId) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index 2084b4fa..76844bed 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -16,69 +16,67 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import dev.usbharu.hideout.core.domain.model.instance.* import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.javatime.timestamp import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository +import java.net.URI import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity @Repository -class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : InstanceRepository, +class InstanceRepositoryImpl : InstanceRepository, AbstractRepository() { override val logger: Logger get() = Companion.logger - override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(instance: InstanceEntity): InstanceEntity = query { - if (Instance.selectAll().where { Instance.id.eq(instance.id) }.forUpdate().empty()) { + if (Instance.selectAll().where { Instance.id.eq(instance.id.instanceId) }.forUpdate().empty()) { Instance.insert { - it[id] = instance.id - it[name] = instance.name - it[description] = instance.description - it[url] = instance.url - it[iconUrl] = instance.iconUrl - it[sharedInbox] = instance.sharedInbox - it[software] = instance.software - it[version] = instance.version + it[id] = instance.id.instanceId + it[name] = instance.name.name + it[description] = instance.description.description + it[url] = instance.url.toString() + it[iconUrl] = instance.iconUrl.toString() + it[sharedInbox] = instance.sharedInbox?.toString() + it[software] = instance.software.software + it[version] = instance.version.version it[isBlocked] = instance.isBlocked it[isMuted] = instance.isMuted - it[moderationNote] = instance.moderationNote + it[moderationNote] = instance.moderationNote.note it[createdAt] = instance.createdAt } } else { - Instance.update({ Instance.id eq instance.id }) { - it[name] = instance.name - it[description] = instance.description - it[url] = instance.url - it[iconUrl] = instance.iconUrl - it[sharedInbox] = instance.sharedInbox - it[software] = instance.software - it[version] = instance.version + Instance.update({ Instance.id eq instance.id.instanceId }) { + it[name] = instance.name.name + it[description] = instance.description.description + it[url] = instance.url.toString() + it[iconUrl] = instance.iconUrl.toString() + it[sharedInbox] = instance.sharedInbox?.toString() + it[software] = instance.software.software + it[version] = instance.version.version it[isBlocked] = instance.isBlocked it[isMuted] = instance.isMuted - it[moderationNote] = instance.moderationNote + it[moderationNote] = instance.moderationNote.note it[createdAt] = instance.createdAt } } return@query instance } - override suspend fun findById(id: Long): InstanceEntity? = query { - return@query Instance.selectAll().where { Instance.id eq id } + override suspend fun findById(id: InstanceId): InstanceEntity? = query { + return@query Instance.selectAll().where { Instance.id eq id.instanceId } .singleOrNull()?.toInstance() } override suspend fun delete(instance: InstanceEntity): Unit = query { - Instance.deleteWhere { id eq instance.id } + Instance.deleteWhere { id eq instance.id.instanceId } } - override suspend fun findByUrl(url: String): dev.usbharu.hideout.core.domain.model.instance.Instance? = query { - return@query Instance.selectAll().where { Instance.url eq url }.singleOrNull()?.toInstance() + override suspend fun findByUrl(url: URI): dev.usbharu.hideout.core.domain.model.instance.Instance? = query { + return@query Instance.selectAll().where { Instance.url eq url.toString() }.singleOrNull()?.toInstance() } companion object { @@ -88,17 +86,17 @@ class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : fun ResultRow.toInstance(): InstanceEntity { return InstanceEntity( - id = this[Instance.id], - name = this[Instance.name], - description = this[Instance.description], - url = this[Instance.url], - iconUrl = this[Instance.iconUrl], - sharedInbox = this[Instance.sharedInbox], - software = this[Instance.software], - version = this[Instance.version], + id = InstanceId(this[Instance.id]), + name = InstanceName(this[Instance.name]), + description = InstanceDescription(this[Instance.description]), + url = URI.create(this[Instance.url]), + iconUrl = URI.create(this[Instance.iconUrl]), + sharedInbox = this[Instance.sharedInbox]?.let { URI.create(it) }, + software = InstanceSoftware(this[Instance.software]), + version = InstanceVersion(this[Instance.version]), isBlocked = this[Instance.isBlocked], isMuted = this[Instance.isMuted], - moderationNote = this[Instance.moderationNote], + moderationNote = InstanceModerationNote(this[Instance.moderationNote]), createdAt = this[Instance.createdAt] ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt index 8882d480..92306fbf 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt @@ -16,105 +16,82 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.application.service.id.IdGenerateService -import dev.usbharu.hideout.core.domain.model.media.FileType -import dev.usbharu.hideout.core.domain.model.media.MediaRepository -import dev.usbharu.hideout.core.domain.model.media.MimeType +import dev.usbharu.hideout.core.domain.model.media.* 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 +import java.net.URI import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia @Repository -class MediaRepositoryImpl(private val idGenerateService: IdGenerateService) : MediaRepository, AbstractRepository() { +class MediaRepositoryImpl : MediaRepository, AbstractRepository() { + override suspend fun findById(id: MediaId): dev.usbharu.hideout.core.domain.model.media.Media? { + return query { + return@query Media + .selectAll().where { Media.id eq id.id } + .singleOrNull() + ?.toMedia() + } + } + + override suspend fun delete(media: dev.usbharu.hideout.core.domain.model.media.Media): Unit = query { + Media.deleteWhere { + id eq id + } + } + override val logger: Logger get() = Companion.logger - override suspend fun generateId(): Long = idGenerateService.generateId() - override suspend fun save(media: EntityMedia): EntityMedia = query { - if (Media.selectAll().where { Media.id eq media.id }.forUpdate().singleOrNull() != null + if (Media.selectAll().where { Media.id eq media.id.id }.forUpdate().singleOrNull() != null ) { - Media.update({ Media.id eq media.id }) { - it[name] = media.name - it[url] = media.url - it[remoteUrl] = media.remoteUrl - it[thumbnailUrl] = media.thumbnailUrl - it[type] = media.type.ordinal - it[blurhash] = media.blurHash + Media.update({ Media.id eq media.id.id }) { + it[name] = media.name.name + it[url] = media.url.toString() + it[remoteUrl] = media.remoteUrl?.toString() + it[thumbnailUrl] = media.thumbnailUrl?.toString() + it[type] = media.type.name + it[blurhash] = media.blurHash?.hash it[mimeType] = media.mimeType.type + "/" + media.mimeType.subtype - it[description] = media.description + it[description] = media.description?.description } } else { Media.insert { - it[id] = media.id - it[name] = media.name - it[url] = media.url - it[remoteUrl] = media.remoteUrl - it[thumbnailUrl] = media.thumbnailUrl - it[type] = media.type.ordinal - it[blurhash] = media.blurHash + it[id] = media.id.id + it[name] = media.name.name + it[url] = media.url.toString() + it[remoteUrl] = media.remoteUrl?.toString() + it[thumbnailUrl] = media.thumbnailUrl?.toString() + it[type] = media.type.name + it[blurhash] = media.blurHash?.hash it[mimeType] = media.mimeType.type + "/" + media.mimeType.subtype - it[description] = media.description + it[description] = media.description?.description } } return@query media } - override suspend fun findById(id: Long): EntityMedia? = query { - return@query Media - .selectAll().where { Media.id eq id } - .singleOrNull() - ?.toMedia() - } - - override suspend fun delete(id: Long): Unit = query { - Media.deleteWhere { - Media.id eq id - } - } - - override suspend fun findByRemoteUrl(remoteUrl: String): dev.usbharu.hideout.core.domain.model.media.Media? = - query { - return@query Media.selectAll().where { Media.remoteUrl eq remoteUrl }.singleOrNull()?.toMedia() - } - companion object { private val logger = LoggerFactory.getLogger(MediaRepositoryImpl::class.java) } } fun ResultRow.toMedia(): EntityMedia { - val fileType = FileType.values().first { it.ordinal == this[Media.type] } + val fileType = FileType.valueOf(this[Media.type]) val mimeType = this[Media.mimeType] return EntityMedia( - id = this[Media.id], - name = this[Media.name], - url = this[Media.url], - remoteUrl = this[Media.remoteUrl], - thumbnailUrl = this[Media.thumbnailUrl], + id = MediaId(this[Media.id]), + name = MediaName(this[Media.name]), + url = URI.create(this[Media.url]), + remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) }, + thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) }, type = fileType, - blurHash = this[Media.blurhash], + blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) }, mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType), - description = this[Media.description] - ) -} - -fun ResultRow.toMediaOrNull(): EntityMedia? { - val fileType = FileType.values().first { it.ordinal == (this.getOrNull(Media.type) ?: return null) } - val mimeType = this.getOrNull(Media.mimeType) ?: return null - return EntityMedia( - id = this.getOrNull(Media.id) ?: return null, - name = this.getOrNull(Media.name) ?: return null, - url = this.getOrNull(Media.url) ?: return null, - remoteUrl = this[Media.remoteUrl], - thumbnailUrl = this[Media.thumbnailUrl], - type = FileType.values().first { it.ordinal == this.getOrNull(Media.type) }, - blurHash = this[Media.blurhash], - mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType), - description = this[Media.description] + description = this[Media.description]?.let { MediaDescription(it) } ) } @@ -124,7 +101,7 @@ object Media : Table("media") { val url = varchar("url", 255).uniqueIndex() val remoteUrl = varchar("remote_url", 255).uniqueIndex().nullable() val thumbnailUrl = varchar("thumbnail_url", 255).uniqueIndex().nullable() - val type = integer("type") + val type = varchar("type", 100) val blurhash = varchar("blurhash", 255).nullable() val mimeType = varchar("mime_type", 255) val description = varchar("description", 4000).nullable() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt deleted file mode 100644 index c70ec0d8..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MetaRepositoryImpl.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.exposedrepository - -import org.jetbrains.exposed.sql.* -import org.springframework.stereotype.Repository -import java.util.* - -@Repository -class MetaRepositoryImpl : MetaRepository { - - override suspend fun save(meta: dev.usbharu.hideout.core.domain.model.meta.Meta) { - if (Meta.selectAll().where { Meta.id eq 1 }.empty()) { - Meta.insert { - it[id] = 1 - it[version] = meta.version - it[kid] = UUID.randomUUID().toString() - it[jwtPrivateKey] = meta.jwt.privateKey - it[jwtPublicKey] = meta.jwt.publicKey - } - } else { - Meta.update({ Meta.id eq 1 }) { - it[version] = meta.version - it[kid] = UUID.randomUUID().toString() - it[jwtPrivateKey] = meta.jwt.privateKey - it[jwtPublicKey] = meta.jwt.publicKey - } - } - } - - override suspend fun get(): dev.usbharu.hideout.core.domain.model.meta.Meta? { - return Meta.selectAll().where { Meta.id eq 1 }.singleOrNull()?.let { - dev.usbharu.hideout.core.domain.model.meta.Meta( - it[Meta.version], - Jwt(UUID.fromString(it[Meta.kid]), it[Meta.jwtPrivateKey], it[Meta.jwtPublicKey]) - ) - } - } -} - -object Meta : Table("meta_info") { - val id: Column = long("id") - val version: Column = varchar("version", 1000) - val kid: Column = varchar("kid", 1000) - val jwtPrivateKey: Column = varchar("jwt_private_key", 100000) - val jwtPublicKey: Column = varchar("jwt_public_key", 100000) - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt index ef003ab7..a10357a9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt @@ -16,14 +16,14 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository +import dev.usbharu.hideout.core.domain.model.actor.ActorId 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 import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import org.jetbrains.exposed.dao.id.LongIdTable +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.update +import org.jetbrains.exposed.sql.javatime.timestamp import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @@ -35,24 +35,28 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() { override suspend fun save(userDetail: UserDetail): UserDetail = query { val singleOrNull = - UserDetails.selectAll().where { UserDetails.actorId eq userDetail.actorId }.forUpdate().singleOrNull() + UserDetails.selectAll().where { UserDetails.id eq userDetail.id.id }.forUpdate().singleOrNull() if (singleOrNull == null) { UserDetails.insert { - it[actorId] = userDetail.actorId - it[password] = userDetail.password + it[id] = userDetail.id.id + it[actorId] = userDetail.actorId.id + it[password] = userDetail.password.password it[autoAcceptFolloweeFollowRequest] = userDetail.autoAcceptFolloweeFollowRequest + it[lastMigration] = userDetail.lastMigration } } else { - UserDetails.update({ UserDetails.actorId eq userDetail.actorId }) { - it[password] = userDetail.password + UserDetails.update({ UserDetails.id eq userDetail.id.id }) { + it[actorId] = userDetail.actorId.id + it[password] = userDetail.password.password it[autoAcceptFolloweeFollowRequest] = userDetail.autoAcceptFolloweeFollowRequest + it[lastMigration] = userDetail.lastMigration } } return@query userDetail } override suspend fun delete(userDetail: UserDetail): Unit = query { - UserDetails.deleteWhere { actorId eq userDetail.actorId } + UserDetails.deleteWhere { id eq userDetail.id.id } } override suspend fun findByActorId(actorId: Long): UserDetail? = query { @@ -60,10 +64,12 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() { .selectAll().where { UserDetails.actorId eq actorId } .singleOrNull() ?.let { - UserDetail( - it[UserDetails.actorId], - it[UserDetails.password], - it[UserDetails.autoAcceptFolloweeFollowRequest] + UserDetail.create( + UserDetailId(it[UserDetails.id]), + ActorId(it[UserDetails.actorId]), + UserDetailHashedPassword(it[UserDetails.password]), + it[UserDetails.autoAcceptFolloweeFollowRequest], + it[UserDetails.lastMigration] ) } } @@ -73,8 +79,11 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() { } } -object UserDetails : LongIdTable("user_details") { +object UserDetails : Table("user_details") { + val id = long("id") val actorId = long("actor_id").references(Actors.id) val password = varchar("password", 255) val autoAcceptFolloweeFollowRequest = bool("auto_accept_followee_follow_request") + val lastMigration = timestamp("last_migration").nullable() + override val primaryKey: PrimaryKey = PrimaryKey(id) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt deleted file mode 100644 index a5d107e4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorDescriptionFactoryImpl.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.factory - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorDescription -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId -import org.springframework.stereotype.Component - -@Component -class ActorDescriptionFactoryImpl( - private val applicationConfig: ApplicationConfig, - private val emojiRepository: CustomEmojiRepository, -) : ActorDescription.ActorDescriptionFactory() { - val regex = Regex(":(w+):") - suspend fun create(description: String): ActorDescription { - val findAll = regex.findAll(description) - - val emojis = - emojiRepository.findByNamesAndDomain( - findAll.map { it.groupValues[1] }.toList(), - applicationConfig.url.host - ) - return create(description, emojis.map { EmojiId(it.id) }) - } -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt similarity index 84% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index e25f6157..871b67b6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/Actor2FactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -26,25 +26,23 @@ import java.net.URI import java.time.Instant @Component -class Actor2FactoryImpl( +class ActorFactoryImpl( private val idGenerateService: IdGenerateService, - private val actorScreenNameFactory: ActorScreenNameFactoryImpl, - private val actorDescriptionFactory: ActorDescriptionFactoryImpl, private val applicationConfig: ApplicationConfig, -) : Actor2.Actor2Factory() { +) { suspend fun createLocal( name: String, keyPair: Pair, instanceId: InstanceId, - ): Actor2 { + ): Actor { val actorName = ActorName(name) val userUrl = "${applicationConfig.url}/users/${actorName.name}" - return super.internalCreate( + return Actor( id = ActorId(idGenerateService.generateId()), name = actorName, domain = Domain(applicationConfig.url.host), - screenName = actorScreenNameFactory.create(name), - description = actorDescriptionFactory.create(""), + screenName = ActorScreenName(name), + description = ActorDescription(""), inbox = URI.create("$userUrl/inbox"), outbox = URI.create("$userUrl/outbox"), url = applicationConfig.url.toURI(), @@ -59,8 +57,11 @@ class Actor2FactoryImpl( followersCount = ActorRelationshipCount(0), followingCount = ActorRelationshipCount(0), postsCount = ActorPostsCount(0), - lastPostDate = null, - suspend = false + lastPostAt = null, + suspend = false, + emojiIds = emptySet() ) } -} \ No newline at end of file +} + +// todo ãªã‚“ã‹è‰²ã€…ãŠã‹ã—ã„ã®ã§ç›´ã™ diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt deleted file mode 100644 index b82773e4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorScreenNameFactoryImpl.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.factory - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorScreenName -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository -import dev.usbharu.hideout.core.domain.model.emoji.EmojiId -import org.springframework.stereotype.Component - -@Component -class ActorScreenNameFactoryImpl( - private val applicationConfig: ApplicationConfig, - private val emojiRepository: CustomEmojiRepository, -) : ActorScreenName.ActorScreenNameFactory() { - val regex = Regex(":(w+):") - - suspend fun create(content: String): ActorScreenName { - - val findAll = regex.findAll(content) - - val emojis = - emojiRepository.findByNamesAndDomain( - findAll.map { it.groupValues[1] }.toList(), - applicationConfig.url.host - ) - return create(content, emojis.map { EmojiId(it.id) }) - - } - -} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt index b8536126..b901f6a6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt @@ -32,4 +32,4 @@ class PostContentFactoryImpl( emptyList() ) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index 4a3ae815..9e240543 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -21,7 +21,7 @@ import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorName import dev.usbharu.hideout.core.domain.model.media.MediaId -import dev.usbharu.hideout.core.domain.model.post.Post2 +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.PostOverview import dev.usbharu.hideout.core.domain.model.post.Visibility @@ -34,7 +34,7 @@ class PostFactoryImpl( private val idGenerateService: IdGenerateService, private val postContentFactoryImpl: PostContentFactoryImpl, private val applicationConfig: ApplicationConfig, -) : Post2.PostFactory() { +) : Post.PostFactory() { suspend fun createLocal( actorId: ActorId, actorName: ActorName, @@ -45,7 +45,7 @@ class PostFactoryImpl( replyId: PostId?, sensitive: Boolean, mediaIds: List, - ): Post2 { + ): Post { val id = idGenerateService.generateId() val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName + "/posts/" + id) return super.create( @@ -64,4 +64,4 @@ class PostFactoryImpl( mediaIds ) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt index a4c74a73..ecbe2c2b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/domainevent/SpringFrameworkDomainEventPublisher.kt @@ -27,4 +27,4 @@ class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher: override suspend fun publishEvent(domainEvent: DomainEvent) { applicationEventPublisher.publishEvent(domainEvent) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 0c749ee5..7487f8b8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -16,32 +16,16 @@ package dev.usbharu.hideout.core.interfaces.api.auth -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.CaptchaConfig -import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.ModelAttribute import org.springframework.web.bind.annotation.PostMapping -@Controller -class AuthController( - private val authApiService: AuthApiService, - private val captchaConfig: CaptchaConfig, - private val applicationConfig: ApplicationConfig -) { +interface AuthController { @GetMapping("/auth/sign_up") - fun signUp(model: Model): String { - model.addAttribute("siteKey", captchaConfig.reCaptchaSiteKey) - model.addAttribute("applicationConfig", applicationConfig) - return "sign_up" - } + fun signUp(model: Model): String @PostMapping("/auth/sign_up") - suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String { - - - return "redirect:" + registerAccount.url - } + suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/media/LocalFileController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/media/LocalFileController.kt index 3fd2dbe2..d41762c0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/media/LocalFileController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/media/LocalFileController.kt @@ -16,43 +16,21 @@ package dev.usbharu.hideout.core.interfaces.api.media -import dev.usbharu.hideout.application.config.LocalStorageConfig -import dev.usbharu.hideout.core.service.media.FileTypeDeterminationService import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.core.io.ClassPathResource -import org.springframework.core.io.PathResource import org.springframework.core.io.Resource import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable -import java.nio.file.Path -import kotlin.io.path.name @Controller @ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) -class LocalFileController( - localStorageConfig: LocalStorageConfig, - private val fileTypeDeterminationService: FileTypeDeterminationService -) { - - private val savePath = Path.of(localStorageConfig.path).toAbsolutePath() +interface LocalFileController { @GetMapping("/files/{id}") - fun files(@PathVariable("id") id: String): ResponseEntity { - val name = Path.of(id).name - val path = savePath.resolve(name) - - val mimeType = fileTypeDeterminationService.fileType(path, name) - val pathResource = PathResource(path) - - return ResponseEntity - .ok() - .contentType(MediaType(mimeType.type, mimeType.subtype)) - .contentLength(pathResource.contentLength()) - .body(pathResource) - } + fun files(@PathVariable("id") id: String): ResponseEntity @GetMapping("/users/{user}/icon.jpg", "/users/{user}/header.jpg") fun icons(): ResponseEntity { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt index 8efbd8b0..56b20ac3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt @@ -44,5 +44,4 @@ object RsaUtil { } fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded)) - } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actors2Test.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt similarity index 96% rename from hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actors2Test.kt rename to hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt index e9d5260f..3a65d48f 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actors2Test.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt @@ -3,7 +3,7 @@ package dev.usbharu.hideout.core.domain.model.actor import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -class Actors2Test { +class ActorsTest { @Test fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œã¦ã¯ã„ã‘ãªã„() { val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt index 7a30bdc6..28faa6a3 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.runBlocking import java.net.URI import java.time.Instant -object TestActor2Factory : Actor2.Actor2Factory() { +object TestActor2Factory : Actor.Actor2Factory() { private val idGenerateService = TwitterSnowflakeIdGenerateService fun create( @@ -31,8 +31,8 @@ object TestActor2Factory : Actor2.Actor2Factory() { followingCount: Int = 0, postCount: Int = 0, lastPostDate: Instant? = null, - suspend: Boolean = false - ): Actor2 { + suspend: Boolean = false, + ): Actor { return runBlocking { super.internalCreate( id = ActorId(id), From 92e13e3782ec471b2a2e2f32a99fcf95597d0b5f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:18:25 +0900 Subject: [PATCH 14/54] =?UTF-8?q?test:=20=E5=8B=95=E3=81=8B=E3=81=AA?= =?UTF-8?q?=E3=81=8F=E3=81=AA=E3=81=A3=E3=81=9F=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hideout-core/build.gradle.kts | 4 +- .../kotlin/mastodon/filter/FilterTest.kt | 1 - .../application/config/ActivityPubConfig.kt | 68 -- .../application/config/SecurityConfig.kt | 93 -- .../application/service/init/MetaService.kt | 26 - .../service/init/MetaServiceImpl.kt | 34 - .../service/init/ServerInitialiseService.kt | 24 - .../post/UpdateLocalNoteApplicationService.kt | 2 +- .../hideout/core/domain/model/actor/Actor.kt | 2 +- .../ActorInstanceRelationship.kt | 12 + .../hideout/core/domain/model/media/Media.kt | 16 +- .../hideout/core/domain/model/post/Post.kt | 46 +- .../actor/RemoteActorCheckDomainService.kt | 2 +- .../core/external/job/DeliverAcceptTask.kt | 70 -- .../core/external/job/DeliverCreateTask.kt | 34 - .../core/external/job/DeliverDeleteTask.kt | 34 - .../core/external/job/DeliverReactionTask.kt | 34 - .../core/external/job/DeliverRejectTask.kt | 34 - .../core/external/job/DeliverUndoTask.kt | 34 - .../hideout/core/external/job/InboxTask.kt | 57 -- .../core/external/job/ReceiveFollowTask.kt | 53 -- .../core/external/job/UpdateActorTask.kt | 32 - .../factory/ActorFactoryImpl.kt | 3 +- .../infrastructure/factory/PostFactoryImpl.kt | 6 +- .../httpsignature/HttpSignatureFilter.kt | 87 -- .../HttpSignatureHeaderChecker.kt | 58 -- .../httpsignature/HttpSignatureUser.kt | 70 -- .../HttpSignatureUserDetailsService.kt | 99 --- .../HttpSignatureVerifierComposite.kt | 56 -- .../oauth2/UserDetailsServiceImpl.kt | 60 -- .../core/service/post/PostContentFormatter.kt | 109 +++ .../usbharu/hideout/EqualsAndToStringTest.kt | 2 +- .../activitypub/domain/model/AnnounceTest.kt | 48 -- .../activitypub/domain/model/CreateTest.kt | 99 --- .../domain/model/DeleteSerializeTest.kt | 89 -- .../activitypub/domain/model/DocumentTest.kt | 53 -- .../domain/model/JsonLdSerializeTest.kt | 249 ------ .../domain/model/KeySerializeTest.kt | 40 - .../domain/model/NoteSerializeTest.kt | 184 ---- .../domain/model/PersonSerializeTest.kt | 155 ---- .../activitypub/domain/model/RejectTest.kt | 110 --- .../activitypub/domain/model/UndoTest.kt | 111 --- .../model/objects/ObjectSerializeTest.kt | 72 -- .../api/actor/ActorAPControllerImplTest.kt | 113 --- .../api/inbox/InboxControllerImplTest.kt | 570 ------------- .../api/note/NoteApControllerImplTest.kt | 137 --- .../api/outbox/OutboxControllerImplTest.kt | 78 -- .../api/webfinger/WebFingerControllerTest.kt | 119 --- .../activity/accept/ApAcceptProcessorTest.kt | 144 ---- .../follow/APSendFollowServiceImplTest.kt | 55 -- .../common/APRequestServiceImplTest.kt | 358 -------- .../APResourceResolveServiceImplTest.kt | 181 ---- .../service/common/APServiceImplTest.kt | 192 ----- .../objects/note/APNoteServiceImplTest.kt | 305 ------- .../hideout/ap/ContextDeserializerTest.kt | 68 -- .../hideout/ap/ContextSerializerTest.kt | 38 - .../ExposedPaginationExtensionKtTest.kt | 153 ---- .../infrastructure/exposed/PageTest.kt | 42 - .../exposed/PaginationListKtTest.kt | 63 -- .../TwitterSnowflakeIdGenerateServiceTest.kt | 47 -- .../service/init/MetaServiceImplTest.kt | 85 -- .../core/domain/model/actor/ActorTest.kt | 13 - .../domain/model/actor/TestActor2Factory.kt | 37 +- .../HttpSignatureHeaderCheckerTest.kt | 110 --- .../filter/MuteProcessServiceImplTest.kt | 404 --------- .../service/filter/MuteServiceImplTest.kt | 112 --- ...cheTikaFileTypeDeterminationServiceTest.kt | 35 - .../LocalFileSystemMediaDataStoreTest.kt | 137 --- .../service/media/MediaServiceImplTest.kt | 27 - .../FollowNotificationRequestTest.kt | 44 - .../FollowRequestNotificationRequestTest.kt | 44 - .../MentionNotificationRequestTest.kt | 44 - .../NotificationServiceImplTest.kt | 110 --- .../PostNotificationRequestTest.kt | 44 - .../ReactionNotificationRequestTest.kt | 44 - ...ipNotificationManagementServiceImplTest.kt | 41 - .../RepostNotificationRequestTest.kt | 44 - .../post/DefaultPostContentFormatterTest.kt | 151 ---- .../core/service/post/PostServiceImplTest.kt | 183 ---- .../reaction/ReactionServiceImplTest.kt | 141 ---- .../RelationshipServiceImplTest.kt | 795 ------------------ .../KtorResourceResolveServiceTest.kt | 69 -- .../service/timeline/TimelineServiceTest.kt | 121 --- .../core/service/user/ActorServiceTest.kt | 186 ---- .../domain/model/NotificationTypeTest.kt | 59 -- .../MastodonAccountApiControllerTest.kt | 169 ---- .../api/apps/MastodonAppsApiControllerTest.kt | 131 --- .../MastodonInstanceApiControllerTest.kt | 123 --- .../media/MastodonMediaApiControllerTest.kt | 109 --- .../MastodonStatusesApiControllerTest.kt | 150 ---- .../MastodonTimelineApiControllerTest.kt | 278 ------ .../account/AccountApiServiceImplTest.kt | 322 ------- .../dev/usbharu/hideout/util/EmojiUtilTest.kt | 57 -- .../src/test/kotlin/utils/JsonObjectMapper.kt | 38 - .../src/test/kotlin/utils/PostBuilder.kt | 62 -- .../kotlin/utils/TestApplicationConfig.kt | 24 - .../src/test/kotlin/utils/UserBuilder.kt | 110 --- 97 files changed, 188 insertions(+), 9600 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverCreateTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverDeleteTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverReactionTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/InboxTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/ReceiveFollowTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/UpdateActorTask.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderChecker.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureVerifierComposite.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/CreateTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DocumentTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextDeserializerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateServiceTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderCheckerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowNotificationRequestTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowRequestNotificationRequestTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/MentionNotificationRequestTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/PostNotificationRequestTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/ReactionNotificationRequestTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RepostNotificationRequestTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatterTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveServiceTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationTypeTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt delete mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/util/EmojiUtilTest.kt delete mode 100644 hideout-core/src/test/kotlin/utils/JsonObjectMapper.kt delete mode 100644 hideout-core/src/test/kotlin/utils/PostBuilder.kt delete mode 100644 hideout-core/src/test/kotlin/utils/TestApplicationConfig.kt delete mode 100644 hideout-core/src/test/kotlin/utils/UserBuilder.kt diff --git a/hideout-core/build.gradle.kts b/hideout-core/build.gradle.kts index ec38328f..52db4593 100644 --- a/hideout-core/build.gradle.kts +++ b/hideout-core/build.gradle.kts @@ -93,8 +93,8 @@ tasks.withType { kotlinOptions { freeCompilerArgs += "-Xjsr305=strict" } - dependsOn("openApiGenerateMastodonCompatibleApi") - mustRunAfter("openApiGenerateMastodonCompatibleApi") +// dependsOn("openApiGenerateMastodonCompatibleApi") +// mustRunAfter("openApiGenerateMastodonCompatibleApi") } diff --git a/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt b/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt index bb3dccae..2732663e 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt @@ -17,7 +17,6 @@ package mastodon.filter import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.application.config.ActivityPubConfig import dev.usbharu.hideout.domain.mastodon.model.generated.FilterKeywordsPostRequest import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequest import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequestKeyword diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt deleted file mode 100644 index afe658a1..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/ActivityPubConfig.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.annotation.JsonSetter -import com.fasterxml.jackson.annotation.Nulls -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject -import dev.usbharu.hideout.core.infrastructure.httpsignature.HttpRequestMixIn -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.sign.HttpSignatureSigner -import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import java.time.format.DateTimeFormatter -import java.util.* - -@Configuration -class ActivityPubConfig { - - @Bean - @Qualifier("activitypub") - fun objectMapper(): ObjectMapper { - val module = SimpleModule().addSerializer(StringOrObject::class.java, StringORObjectSerializer()) - - val objectMapper = jacksonObjectMapper() - .registerModules(module) - .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP)) - .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP)) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(JsonParser.Feature.ALLOW_COMMENTS, true) - .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) - .configure(JsonParser.Feature.ALLOW_TRAILING_COMMA, true) - .addMixIn(HttpRequest::class.java, HttpRequestMixIn::class.java) - - return objectMapper - } - - @Bean - @Qualifier("http") - fun dateTimeFormatter(): DateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - - @Bean - fun httpSignatureSigner(): HttpSignatureSigner = RsaSha256HttpSignatureSigner() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index 837a261c..0730dfb7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -16,43 +16,25 @@ package dev.usbharu.hideout.application.config -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.databind.module.SimpleModule import com.nimbusds.jose.jwk.JWKSet import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jose.jwk.source.ImmutableJWKSet import com.nimbusds.jose.jwk.source.JWKSource import com.nimbusds.jose.proc.SecurityContext -import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureVerifierComposite import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsServiceImpl import dev.usbharu.hideout.util.RsaUtil -import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner -import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser -import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier import jakarta.annotation.PostConstruct import jakarta.servlet.* import org.springframework.beans.factory.support.BeanDefinitionRegistry import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.Primary import org.springframework.core.annotation.Order import org.springframework.http.HttpMethod.GET import org.springframework.http.HttpMethod.POST import org.springframework.http.HttpStatus -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -import org.springframework.security.authentication.AccountStatusUserDetailsChecker import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.authentication.dao.DaoAuthenticationProvider import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder @@ -74,8 +56,6 @@ import org.springframework.security.oauth2.server.authorization.token.JwtEncodin import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer import org.springframework.security.web.FilterChainProxy import org.springframework.security.web.SecurityFilterChain -import org.springframework.security.web.access.ExceptionTranslationFilter -import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler import org.springframework.security.web.authentication.HttpStatusEntryPoint import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider @@ -83,7 +63,6 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn import org.springframework.security.web.debug.DebugFilter import org.springframework.security.web.firewall.HttpFirewall import org.springframework.security.web.firewall.RequestRejectedHandler -import org.springframework.security.web.savedrequest.RequestCacheAwareFilter import org.springframework.security.web.util.matcher.AnyRequestMatcher import org.springframework.web.filter.CompositeFilter import java.io.IOException @@ -105,14 +84,9 @@ class SecurityConfig { @Order(1) fun httpSignatureFilterChain( http: HttpSecurity, - httpSignatureFilter: HttpSignatureFilter, ): SecurityFilterChain { http { securityMatcher("/users/*/posts/*") - addFilterAt(httpSignatureFilter) - addFilterBefore( - ExceptionTranslationFilter(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) - ) authorizeHttpRequests { authorize(anyRequest, permitAll) } @@ -130,57 +104,6 @@ class SecurityConfig { return http.build() } - @Bean - fun getHttpSignatureFilter( - authenticationManager: AuthenticationManager, - httpSignatureHeaderChecker: HttpSignatureHeaderChecker, - ): HttpSignatureFilter { - val httpSignatureFilter = - HttpSignatureFilter(DefaultSignatureHeaderParser(), httpSignatureHeaderChecker) - httpSignatureFilter.setAuthenticationManager(authenticationManager) - httpSignatureFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false) - val authenticationEntryPointFailureHandler = - AuthenticationEntryPointFailureHandler(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) - authenticationEntryPointFailureHandler.setRethrowAuthenticationServiceException(false) - httpSignatureFilter.setAuthenticationFailureHandler(authenticationEntryPointFailureHandler) - return httpSignatureFilter - } - - @Bean - @Order(2) - fun daoAuthenticationProvider(userDetailsServiceImpl: UserDetailsServiceImpl): DaoAuthenticationProvider { - val daoAuthenticationProvider = DaoAuthenticationProvider() - daoAuthenticationProvider.setUserDetailsService(userDetailsServiceImpl) - - return daoAuthenticationProvider - } - - @Bean - @Order(1) - fun httpSignatureAuthenticationProvider( - transaction: Transaction, - actorRepository: ActorRepository, - ): PreAuthenticatedAuthenticationProvider { - val provider = PreAuthenticatedAuthenticationProvider() - val signatureHeaderParser = DefaultSignatureHeaderParser() - provider.setPreAuthenticatedUserDetailsService( - HttpSignatureUserDetailsService( - HttpSignatureVerifierComposite( - mapOf( - "rsa-sha256" to RsaSha256HttpSignatureVerifier( - signatureHeaderParser, RsaSha256HttpSignatureSigner() - ) - ), - signatureHeaderParser - ), - transaction, - signatureHeaderParser, - actorRepository - ) - ) - provider.setUserDetailsChecker(AccountStatusUserDetailsChecker()) - return provider - } @Bean @Order(2) @@ -291,22 +214,6 @@ class SecurityConfig { } } - @Bean - @Primary - fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer { - return Jackson2ObjectMapperBuilderCustomizer { - it.serializationInclusion(JsonInclude.Include.ALWAYS) - .modulesToInstall(SimpleModule().addSerializer(StringOrObject::class.java, StringORObjectSerializer())) - .serializers() - } - } - - @Bean - fun mappingJackson2HttpMessageConverter(): MappingJackson2HttpMessageConverter { - val builder = Jackson2ObjectMapperBuilder().serializationInclusion(JsonInclude.Include.NON_NULL) - builder.modulesToInstall(SimpleModule().addSerializer(StringOrObject::class.java, StringORObjectSerializer())) - return MappingJackson2HttpMessageConverter(builder.build()) - } // Spring Security 3.2.1 ã«å­˜åœ¨ã™ã‚‹ EnableWebSecurity(debug = true)ã«ã™ã‚‹ã¨ç™ºç”Ÿã™ã‚‹ã‚¨ãƒ©ãƒ¼ã«å¯¾å‡¦ã™ã‚‹ãŸã‚ã®ã‚³ãƒ¼ãƒ‰ // trueã«ã—ãªã„ã¨ãã¯ã‚³ãƒ¡ãƒ³ãƒˆã‚¢ã‚¦ãƒˆ diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt deleted file mode 100644 index 32615016..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaService.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.service.init - -import org.springframework.stereotype.Service - -@Service -interface MetaService { - suspend fun getMeta(): Meta - suspend fun updateMeta(meta: Meta) - suspend fun getJwtMeta(): Jwt -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt deleted file mode 100644 index 3ca63744..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImpl.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.service.init - -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.exception.NotInitException -import org.springframework.stereotype.Service - -@Service -class MetaServiceImpl(private val metaRepository: MetaRepository, private val transaction: Transaction) : - MetaService { - override suspend fun getMeta(): Meta = - transaction.transaction { metaRepository.get() ?: throw NotInitException("Meta is null") } - - override suspend fun updateMeta(meta: Meta): Unit = transaction.transaction { - metaRepository.save(meta) - } - - override suspend fun getJwtMeta(): Jwt = getMeta().jwt -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseService.kt deleted file mode 100644 index ffec5686..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/init/ServerInitialiseService.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.service.init - -import org.springframework.stereotype.Service - -@Service -interface ServerInitialiseService { - suspend fun init() -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt index 2a8c231e..0348c80f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt @@ -36,7 +36,7 @@ class UpdateLocalNoteApplicationService( post.content = postContentFactoryImpl.create(updateLocalNote.content) post.overview = updateLocalNote.overview?.let { PostOverview(it) } - post.mediaIds = updateLocalNote.mediaIds.map { MediaId(it) } + post.addMediaIds(updateLocalNote.mediaIds.map { MediaId(it) }) post.sensitive = updateLocalNote.sensitive postRepository.save(post) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 702f3246..8d13c8e6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -72,7 +72,7 @@ class Actor( var moveTo = moveTo set(value) { - require(moveTo != id) + require(value != id) addDomainEvent(ActorDomainEventFactory(this).createEvent(move)) field = value } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt index bd1c0c1b..178716a0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -85,4 +85,16 @@ data class ActorInstanceRelationship( result = 31 * result + instanceId.hashCode() return result } + + override fun toString(): String { + return "ActorInstanceRelationship(" + + "actorId=$actorId, " + + "instanceId=$instanceId, " + + "blocking=$blocking, " + + "muting=$muting, " + + "doNotSendPrivate=$doNotSendPrivate" + + ")" + } + + } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt index 529eb3af..5c3eddf6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt @@ -28,4 +28,18 @@ data class Media( val mimeType: MimeType, val blurHash: MediaBlurHash?, val description: MediaDescription? = null, -) +) { + override fun toString(): String { + return "Media(" + + "id=$id, " + + "name=$name, " + + "url=$url, " + + "remoteUrl=$remoteUrl, " + + "thumbnailUrl=$thumbnailUrl, " + + "type=$type, " + + "mimeType=$mimeType, " + + "blurHash=$blurHash, " + + "description=$description" + + ")" + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index d992a78e..f1846382 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -24,7 +24,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant -class Post private constructor( +class Post( val id: PostId, actorId: ActorId, overview: PostOverview? = null, @@ -191,8 +191,8 @@ class Post private constructor( return id.hashCode() } - abstract class PostFactory { - protected fun create( + companion object { + fun create( id: PostId, actorId: ActorId, overview: PostOverview? = null, @@ -206,24 +206,30 @@ class Post private constructor( apId: URI, deleted: Boolean, mediaIds: List, - hide: Boolean, + visibleActors: List = emptyList(), + hide: Boolean = false, + moveTo: PostId? = null, ): Post { - return Post( - id = id, - actorId = actorId, - overview = overview, - content = content, - createdAt = createdAt, - visibility = visibility, - url = url, - repostId = repostId, - replyId = replyId, - sensitive = sensitive, - apId = apId, - deleted = deleted, - mediaIds = mediaIds, - hide = hide - ).apply { addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.create)) } + val post = Post( + id, + actorId, + overview, + content, + createdAt, + visibility, + url, + repostId, + replyId, + sensitive, + apId, + deleted, + mediaIds, + visibleActors, + hide, + moveTo + ) + post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.create)) + return post } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt index c4ef154b..d7bf7ba8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt @@ -26,5 +26,5 @@ interface IRemoteActorCheckDomainService { @Service class RemoteActorCheckDomainService(private val applicationConfig: ApplicationConfig) : IRemoteActorCheckDomainService { - override fun isRemoteActor(actor: Actor): Boolean = actor.domain.domain == applicationConfig.url.host + override fun isRemoteActor(actor: Actor): Boolean = actor.domain.domain != applicationConfig.url.host } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptTask.kt deleted file mode 100644 index b7b19949..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverAcceptTask.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.owl.common.property.* -import dev.usbharu.owl.common.task.PropertyDefinition -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class DeliverAcceptTask( - val accept: Accept, - val inbox: String, - val signer: Long, -) : Task() - -@Component -data object DeliverAcceptTaskDef : TaskDefinition { - override val name: String - get() = "DeliverAccept" - override val priority: Int - get() = 10 - override val maxRetry: Int - get() = 5 - override val retryPolicy: String - get() = "" - override val timeoutMilli: Long - get() = 1000 - override val propertyDefinition: PropertyDefinition - get() = PropertyDefinition( - mapOf( - "accept" to PropertyType.binary, - "inbox" to PropertyType.string, - "signer" to PropertyType.number, - ) - ) - override val type: Class - get() = DeliverAcceptTask::class.java - - override fun serialize(task: DeliverAcceptTask): Map> { - return mapOf( - "accept" to ObjectPropertyValue(task.accept), - "inbox" to StringPropertyValue(task.inbox), - "signer" to LongPropertyValue(task.signer) - ) - } - - override fun deserialize(value: Map>): DeliverAcceptTask { - return DeliverAcceptTask( - value.getValue("accept").value as Accept, - value.getValue("inbox").value as String, - value.getValue("signer").value as Long, - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverCreateTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverCreateTask.kt deleted file mode 100644 index 2c645290..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverCreateTask.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Create -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class DeliverCreateTask( - val create: Create, - val inbox: String, - val actor: String, -) : Task() - -@Component -data object DeliverCreateTaskDef : TaskDefinition { - override val type: Class - get() = DeliverCreateTask::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverDeleteTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverDeleteTask.kt deleted file mode 100644 index 6ce63ad2..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverDeleteTask.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Delete -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class DeliverDeleteTask( - val delete: Delete, - val inbox: String, - val signer: Long, -) : Task() - -@Component -data object DeliverDeleteTaskDef : TaskDefinition { - override val type: Class - get() = DeliverDeleteTask::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverReactionTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverReactionTask.kt deleted file mode 100644 index c1c73154..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverReactionTask.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class DeliverReactionTask( - val actor: String, - val like: Like, - val inbox: String, -) : Task() - -@Component -data object DeliverReactionTaskDef : TaskDefinition { - override val type: Class - get() = DeliverReactionTask::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectTask.kt deleted file mode 100644 index 5bb47432..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverRejectTask.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Reject -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class DeliverRejectTask( - val reject: Reject, - val inbox: String, - val signer: Long, -) : Task() - -@Component -data object DeliverRejectTaskDef : TaskDefinition { - override val type: Class - get() = DeliverRejectTask::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoTask.kt deleted file mode 100644 index 3ae7f129..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/DeliverUndoTask.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Undo -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class DeliverUndoTask( - val undo: Undo, - val inbox: String, - val signer: Long, -) : Task() - -@Component -data object DeliverUndoTaskDef : TaskDefinition { - override val type: Class - get() = DeliverUndoTask::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/InboxTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/InboxTask.kt deleted file mode 100644 index de6b926f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/InboxTask.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.owl.common.property.ObjectPropertyValue -import dev.usbharu.owl.common.property.PropertyValue -import dev.usbharu.owl.common.property.StringPropertyValue -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class InboxTask( - val json: String, - val type: ActivityType, - val httpRequest: HttpRequest, - val headers: Map>, -) : Task() - -@Component -data object InboxTaskDef : TaskDefinition { - override val type: Class - get() = InboxTask::class.java - - override fun serialize(task: InboxTask): Map> { - return mapOf( - "json" to StringPropertyValue(task.json), - "type" to ObjectPropertyValue(task.type), - "httpRequest" to ObjectPropertyValue(task.httpRequest), - "headers" to ObjectPropertyValue(task.headers), - ) - } - - override fun deserialize(value: Map>): InboxTask { - return InboxTask( - value.getValue("json").value as String, - value.getValue("type").value as ActivityType, - value.getValue("httpRequest").value as HttpRequest, - value.getValue("headers").value as Map>, - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/ReceiveFollowTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/ReceiveFollowTask.kt deleted file mode 100644 index a72b0d5a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/ReceiveFollowTask.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.owl.common.property.ObjectPropertyValue -import dev.usbharu.owl.common.property.PropertyValue -import dev.usbharu.owl.common.property.StringPropertyValue -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class ReceiveFollowTask( - val actor: String, - val follow: Follow, - val targetActor: String, -) : Task() - -@Component -data object ReceiveFollowTaskDef : TaskDefinition { - override val type: Class - get() = ReceiveFollowTask::class.java - - override fun serialize(task: ReceiveFollowTask): Map> { - return mapOf( - "actor" to StringPropertyValue(task.actor), - "follow" to ObjectPropertyValue(task.follow), - "targetActor" to StringPropertyValue(task.targetActor) - ) - } - - override fun deserialize(value: Map>): ReceiveFollowTask { - return ReceiveFollowTask( - value.getValue("actor").value as String, - value.getValue("follow").value as Follow, - value.getValue("targetActor").value as String, - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/UpdateActorTask.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/UpdateActorTask.kt deleted file mode 100644 index 5fc1a272..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/job/UpdateActorTask.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.external.job - -import dev.usbharu.owl.common.task.Task -import dev.usbharu.owl.common.task.TaskDefinition -import org.springframework.stereotype.Component - -data class UpdateActorTask( - val id: Long, - val apId: String, -) : Task() - -@Component -data object UpdateActorTaskDef : TaskDefinition { - override val type: Class - get() = UpdateActorTask::class.java -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index 871b67b6..1273b68c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -59,7 +59,8 @@ class ActorFactoryImpl( postsCount = ActorPostsCount(0), lastPostAt = null, suspend = false, - emojiIds = emptySet() + emojiIds = emptySet(), + deleted = false ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index 9e240543..f28dd0d1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -34,7 +34,7 @@ class PostFactoryImpl( private val idGenerateService: IdGenerateService, private val postContentFactoryImpl: PostContentFactoryImpl, private val applicationConfig: ApplicationConfig, -) : Post.PostFactory() { +) { suspend fun createLocal( actorId: ActorId, actorName: ActorName, @@ -48,7 +48,7 @@ class PostFactoryImpl( ): Post { val id = idGenerateService.generateId() val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName + "/posts/" + id) - return super.create( + return Post.create( PostId(id), actorId, overview, @@ -61,7 +61,7 @@ class PostFactoryImpl( sensitive, url, false, - mediaIds + mediaIds, ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt deleted file mode 100644 index 27886cfb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature - -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.verify.SignatureHeaderParser -import jakarta.servlet.http.HttpServletRequest -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter -import java.net.URL - -class HttpSignatureFilter( - private val httpSignatureHeaderParser: SignatureHeaderParser, - private val httpSignatureHeaderChecker: HttpSignatureHeaderChecker, -) : - AbstractPreAuthenticatedProcessingFilter() { - override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any? { - val headersList = request?.headerNames?.toList().orEmpty() - - val headers = - headersList.associateWith { header -> request?.getHeaders(header)?.toList().orEmpty() } - - val signature = try { - httpSignatureHeaderParser.parse(HttpHeaders(headers)) - } catch (_: IllegalArgumentException) { - return null - } catch (_: RuntimeException) { - return "" - } - return signature.keyId - } - - override fun getPreAuthenticatedCredentials(request: HttpServletRequest?): Any? { - requireNotNull(request) - val url = request.requestURL.toString() - - val headersList = request.headerNames?.toList().orEmpty() - - val headers = - headersList.associateWith { header -> request.getHeaders(header)?.toList().orEmpty() } - - val method = when (val method = request.method.lowercase()) { - "get" -> HttpMethod.GET - "post" -> HttpMethod.POST - else -> { -// throw IllegalArgumentException("Unsupported method: $method") - return null - } - } - - try { - httpSignatureHeaderChecker.checkDate(request.getHeader("date")!!) - httpSignatureHeaderChecker.checkHost(request.getHeader("host")!!) - if (request.method.equals("post", true)) { - httpSignatureHeaderChecker.checkDigest( - request.inputStream.readAllBytes()!!, - request.getHeader("digest")!! - ) - } - } catch (_: NullPointerException) { - return null - } catch (_: IllegalArgumentException) { - return null - } - - return HttpRequest( - URL(url + request.queryString.orEmpty()), - HttpHeaders(headers), - method - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderChecker.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderChecker.kt deleted file mode 100644 index eab673cb..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderChecker.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.util.Base64Util -import org.springframework.stereotype.Component -import java.security.MessageDigest -import java.time.Instant -import java.time.format.DateTimeFormatter -import java.util.* - -@Component -class HttpSignatureHeaderChecker(private val applicationConfig: ApplicationConfig) { - fun checkDate(date: String) { - val from = Instant.from(dateFormat.parse(date)) - - if (from.isAfter(Instant.now()) || from.isBefore(Instant.now().minusSeconds(86400))) { - throw IllegalArgumentException("未æ¥") - } - } - - fun checkHost(host: String) { - if (applicationConfig.url.host.equals(host, true).not()) { - throw IllegalArgumentException("ホストåãŒé•ã†") - } - } - - fun checkDigest(byteArray: ByteArray, digest: String) { - val find = regex.find(digest) - val sha256 = MessageDigest.getInstance("SHA-256") - - val other = find?.groups?.get(2)?.value.orEmpty() - - if (Base64Util.encode(sha256.digest(byteArray)).equals(other, true).not()) { - throw IllegalArgumentException("リクエストボディãŒé•ã†") - } - } - - companion object { - private val dateFormat = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - private val regex = Regex("^([a-zA-Z0-9\\-]+)=(.+)$") - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt deleted file mode 100644 index 50fc2c4e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature - -import org.springframework.security.core.GrantedAuthority -import org.springframework.security.core.userdetails.User -import java.io.Serial - -class HttpSignatureUser( - username: String, - val domain: String, - val id: Long, - credentialsNonExpired: Boolean, - accountNonLocked: Boolean, - authorities: MutableCollection? -) : User( - username, - "", - true, - true, - credentialsNonExpired, - accountNonLocked, - authorities -) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is HttpSignatureUser) return false - if (!super.equals(other)) return false - - if (domain != other.domain) return false - if (id != other.id) return false - - return true - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + domain.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun toString(): String { - return "HttpSignatureUser(" + - "domain='$domain', " + - "id=$id" + - ")" + - " ${super.toString()}" - } - - companion object { - @Serial - private const val serialVersionUID: Long = -3330552099960982997L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt deleted file mode 100644 index 36c61d4c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature - -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException -import dev.usbharu.hideout.util.RsaUtil -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.common.PublicKey -import dev.usbharu.httpsignature.verify.FailedVerification -import dev.usbharu.httpsignature.verify.HttpSignatureVerifier -import dev.usbharu.httpsignature.verify.SignatureHeaderParser -import kotlinx.coroutines.runBlocking -import org.slf4j.LoggerFactory -import org.springframework.security.authentication.BadCredentialsException -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService -import org.springframework.security.core.userdetails.UserDetails -import org.springframework.security.core.userdetails.UsernameNotFoundException -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken - -class HttpSignatureUserDetailsService( - private val httpSignatureVerifier: HttpSignatureVerifier, - private val transaction: Transaction, - private val httpSignatureHeaderParser: SignatureHeaderParser, - private val actorRepository: ActorRepository -) : - AuthenticationUserDetailsService { - override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken): UserDetails = runBlocking { - check(token.principal is String) { "Token is not String" } - val credentials = token.credentials - - check(credentials is HttpRequest) { "Credentials is not HttpRequest" } - - val keyId = token.principal as String - val findByKeyId = transaction.transaction { - actorRepository.findByKeyId(keyId) ?: throw UsernameNotFoundException("keyId: $keyId not found.") - } - - val signature = httpSignatureHeaderParser.parse(credentials.headers) - - val requiredHeaders = when (credentials.method) { - HttpMethod.GET -> getRequiredHeaders - HttpMethod.POST -> postRequiredHeaders - } - if (signature.headers.containsAll(requiredHeaders).not()) { - logger.warn( - "FAILED Verify HTTP Signature. required headers: {} but actual: {}", - requiredHeaders, - signature.headers - ) - throw BadCredentialsException("HTTP Signature. required headers: $requiredHeaders") - } - - @Suppress("TooGenericExceptionCaught") - val verify = try { - httpSignatureVerifier.verify( - credentials, - PublicKey(RsaUtil.decodeRsaPublicKeyPem(findByKeyId.publicKey), keyId) - ) - } catch (e: RuntimeException) { - throw BadCredentialsException("", e) - } - - if (verify is FailedVerification) { - logger.warn("FAILED Verify HTTP Signature reason: {}", verify.reason) - throw HttpSignatureVerifyException(verify.reason) - } - - HttpSignatureUser( - username = findByKeyId.name, - domain = findByKeyId.domain, - id = findByKeyId.id, - credentialsNonExpired = true, - accountNonLocked = true, - authorities = mutableListOf() - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(HttpSignatureUserDetailsService::class.java) - private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") - private val getRequiredHeaders = listOf("(request-target)", "date", "host") - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureVerifierComposite.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureVerifierComposite.kt deleted file mode 100644 index 8dd83ee3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureVerifierComposite.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature - -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.common.PublicKey -import dev.usbharu.httpsignature.verify.HttpSignatureVerifier -import dev.usbharu.httpsignature.verify.SignatureHeaderParser -import dev.usbharu.httpsignature.verify.VerificationResult - -class HttpSignatureVerifierComposite( - private val map: Map, - private val httpSignatureHeaderParser: SignatureHeaderParser -) : HttpSignatureVerifier { - override fun verify(httpRequest: HttpRequest, key: PublicKey): VerificationResult { - val signature = httpSignatureHeaderParser.parse(httpRequest.headers) - val verify = map[signature.algorithm]?.verify(httpRequest, key) - if (verify != null) { - return verify - } - - throw IllegalArgumentException("Unsupported algorithm. ${signature.algorithm}") - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as HttpSignatureVerifierComposite - - if (map != other.map) return false - if (httpSignatureHeaderParser != other.httpSignatureHeaderParser) return false - - return true - } - - override fun hashCode(): Int { - var result = map.hashCode() - result = 31 * result + httpSignatureHeaderParser.hashCode() - return result - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt deleted file mode 100644 index 04b1f298..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import kotlinx.coroutines.runBlocking -import org.springframework.security.core.userdetails.UserDetails -import org.springframework.security.core.userdetails.UserDetailsService -import org.springframework.security.core.userdetails.UsernameNotFoundException -import org.springframework.stereotype.Service - -@Service -class UserDetailsServiceImpl( - private val applicationConfig: ApplicationConfig, - private val userDetailRepository: UserDetailRepository, - private val transaction: Transaction, - private val actorRepository: ActorRepository -) : - UserDetailsService { - override fun loadUserByUsername(username: String?): UserDetails = runBlocking { - if (username == null) { - throw UsernameNotFoundException("$username not found") - } - transaction.transaction { - val findById = - actorRepository.findByNameAndDomain(username, applicationConfig.url.host) - ?: throw UserNotFoundException.withNameAndDomain(username, applicationConfig.url.host) - - val userDetails = userDetailRepository.findByActorId(findById.id) - ?: throw UsernameNotFoundException("${findById.id} not found.") - UserDetailsImpl( - id = findById.id, - username = findById.name, - password = userDetails.password, - enabled = true, - accountNonExpired = true, - credentialsNonExpired = true, - accountNonLocked = true, - authorities = mutableListOf() - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt new file mode 100644 index 00000000..5819c47b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.service.post + +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import org.jsoup.nodes.TextNode +import org.jsoup.select.Elements +import org.owasp.html.PolicyFactory +import org.springframework.stereotype.Service + + +interface PostContentFormatter { + fun format(content: String): FormattedPostContent +} + +@Service +class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : PostContentFormatter { + override fun format(content: String): FormattedPostContent { + // ã¾ãšä¸æ­£ãªHTMLã‚’æ•´å½¢ã™ã‚‹ + val document = Jsoup.parseBodyFragment(content) + val outputSettings = Document.OutputSettings() + outputSettings.prettyPrint(false) + + document.outputSettings(outputSettings) + + val unsafeElement = document.getElementsByTag("body").first() ?: return FormattedPostContent( + "", + "" + ) + + // 文字ã ã‘ã®HTMLãªã©ã¯ã“ã“ã§pã‚¿ã‚°ã§å›²ã‚€ + val flattenHtml = unsafeElement.childNodes().mapNotNull { + if (it is Element) { + it + } else if (it is TextNode) { + Element("p").appendText(it.text()) + } else { + null + } + }.filter { it.text().isNotBlank() } + + // HTMLã®ã‚µãƒ‹ã‚¿ã‚¤ã‚ºã‚’ã™ã‚‹ + val unsafeHtml = Elements(flattenHtml).outerHtml() + + val safeHtml = policyFactory.sanitize(unsafeHtml) + + val safeDocument = + Jsoup.parseBodyFragment(safeHtml).getElementsByTag("body").first() ?: return FormattedPostContent("", "") + + val formattedHtml = mutableListOf() + + // 連続ã™ã‚‹brタグを段è½ã«å¤‰æ›ã™ã‚‹ + for (element in safeDocument.children()) { + var brCount = 0 + var prevIndex = 0 + val childNodes = element.childNodes() + for ((index, childNode) in childNodes.withIndex()) { + if (childNode is Element && childNode.tagName() == "br") { + brCount++ + } else if (brCount >= 2) { + formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, index - brCount))) + prevIndex = index + } + } + formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, childNodes.size))) + } + + val elements = Elements(formattedHtml) + + return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements)) + } + + private fun printHtml(element: Elements): String { + return element.joinToString("\n\n") { + it.childNodes().joinToString("") { node -> + if (node is Element && node.tagName() == "br") { + "\n" + } else if (node is Element) { + node.text() + } else if (node is TextNode) { + node.text() + } else { + "" + } + } + } + } +} + +data class FormattedPostContent( + val html: String, + val content: String, +) \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/EqualsAndToStringTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/EqualsAndToStringTest.kt index b25ec4a2..96f3655f 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/EqualsAndToStringTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/EqualsAndToStringTest.kt @@ -124,7 +124,7 @@ class EqualsAndToStringTest { } try { ToStringVerifier.forClass(it).withPreset(Presets.INTELLI_J).verify() - } catch (e: Exception) { + } catch (e: Throwable) { e.printStackTrace() } } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt deleted file mode 100644 index ed037bd6..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/AnnounceTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.junit.jupiter.api.Test - -class AnnounceTest{ - @Test - fun mastodonã®jsonをデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{ - "@context": "https://www.w3.org/ns/activitystreams", - "id": "https://kb.usbharu.dev/users/usbharu/statuses/111859915842276344/activity", - "type": "Announce", - "actor": "https://kb.usbharu.dev/users/usbharu", - "published": "2024-02-02T04:07:40Z", - "to": [ - "https://kb.usbharu.dev/users/usbharu/followers" - ], - "cc": [ - "https://kb.usbharu.dev/users/usbharu" - ], - "object": "https://kb.usbharu.dev/users/usbharu/statuses/111850484548963326" -}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/CreateTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/CreateTest.kt deleted file mode 100644 index ce633497..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/CreateTest.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.intellij.lang.annotations.Language -import org.junit.jupiter.api.Test - -class CreateTest { - @Test - fun Createã®ãƒ‡ã‚¤ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - @Language("JSON") val json = """{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "Hashtag": "as:Hashtag", - "quoteUrl": "as:quoteUrl", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "featured": "toot:featured", - "discoverable": "toot:discoverable", - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "misskey": "https://misskey-hub.net/ns#", - "_misskey_content": "misskey:_misskey_content", - "_misskey_quote": "misskey:_misskey_quote", - "_misskey_reaction": "misskey:_misskey_reaction", - "_misskey_votes": "misskey:_misskey_votes", - "isCat": "misskey:isCat", - "vcard": "http://www.w3.org/2006/vcard/ns#" - } - ], - "id": "https://misskey.usbharu.dev/notes/9f2i9cm88e/activity", - "actor": "https://misskey.usbharu.dev/users/97ws8y3rj6", - "type": "Create", - "published": "2023-05-22T14:26:53.600Z", - "object": { - "id": "https://misskey.usbharu.dev/notes/9f2i9cm88e", - "type": "Note", - "attributedTo": "https://misskey.usbharu.dev/users/97ws8y3rj6", - "content": "

@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦

", - "_misskey_content": "@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦", - "source": { - "content": "@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦", - "mediaType": "text/x.misskeymarkdown" - }, - "published": "2023-05-22T14:26:53.600Z", - "to": [ - "https://misskey.usbharu.dev/users/97ws8y3rj6/followers" - ], - "cc": [ - "https://www.w3.org/ns/activitystreams#Public", - "https://calckey.jp/users/9bu1xzwjyb" - ], - "inReplyTo": "https://calckey.jp/notes/9f2i7ymf1d", - "attachment": [], - "sensitive": false, - "tag": [ - { - "type": "Mention", - "href": "https://calckey.jp/users/9bu1xzwjyb", - "name": "@trapezial@calckey.jp" - } - ] - }, - "to": [ - "https://misskey.usbharu.dev/users/97ws8y3rj6/followers" - ], - "cc": [ - "https://www.w3.org/ns/activitystreams#Public", - "https://calckey.jp/users/9bu1xzwjyb" - ] -} -""" - - val objectMapper = ActivityPubConfig().objectMapper() - - objectMapper.readValue(json) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt deleted file mode 100644 index 6f964232..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DeleteSerializeTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.intellij.lang.annotations.Language -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class DeleteSerializeTest { - @Test - fun Misskeyã®ç™ºè¡Œã™ã‚‹JSONをデシリアライズã§ãã‚‹() { - @Language("JSON") val json = """{ - "@context" : [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { - "manuallyApprovesFollowers" : "as:manuallyApprovesFollowers", - "sensitive" : "as:sensitive", - "Hashtag" : "as:Hashtag", - "quoteUrl" : "as:quoteUrl", - "toot" : "http://joinmastodon.org/ns#", - "Emoji" : "toot:Emoji", - "featured" : "toot:featured", - "discoverable" : "toot:discoverable", - "schema" : "http://schema.org#", - "PropertyValue" : "schema:PropertyValue", - "value" : "schema:value" - } ], - "type" : "Delete", - "actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6", - "object" : { - "id" : "https://misskey.usbharu.dev/notes/9lkwqnwqk9", - "type" : "Tombstone" - }, - "published" : "2023-11-02T15:30:34.160Z", - "id" : "https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69" -} -""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - val expected = Delete( - actor = "https://misskey.usbharu.dev/users/97ws8y3rj6", - id = "https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69", - `object` = Tombstone( - id = "https://misskey.usbharu.dev/notes/9lkwqnwqk9", - ), - published = "2023-11-02T15:30:34.160Z", - ) - expected.context = Constant.context - assertEquals(expected, readValue) - } - - @Test - fun シリアライズã§ãã‚‹() { - val delete = Delete( - actor = "https://misskey.usbharu.dev/users/97ws8y3rj6", - id = "https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69", - `object` = Tombstone( - id = "https://misskey.usbharu.dev/notes/9lkwqnwqk9", - ), - published = "2023-11-02T15:30:34.160Z", - ) - - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(delete) - val expected = - """{"type":"Delete","actor":"https://misskey.usbharu.dev/users/97ws8y3rj6","id":"https://misskey.usbharu.dev/4b5b6ed5-9269-45f3-8403-cba1e74b4b69","object":{"type":"Tombstone","id":"https://misskey.usbharu.dev/notes/9lkwqnwqk9"},"published":"2023-11-02T15:30:34.160Z"}""" - assertEquals(expected, actual) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DocumentTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DocumentTest.kt deleted file mode 100644 index 168e88d4..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/DocumentTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.intellij.lang.annotations.Language -import org.junit.jupiter.api.Test - -class DocumentTest { - @Test - fun Documentをデシリアライズã§ãã‚‹() { - @Language("JSON") val json = """{ - "type": "Document", - "mediaType": "image/webp", - "url": "https://s3misskey.usbharu.dev/misskey-minio/misskey-minio/data/81ec9ad1-2581-466e-b90c-d9d2350ab95c.webp", - "name": "ALTテスト" - }""" - - val objectMapper = ActivityPubConfig().objectMapper() - - objectMapper.readValue(json) - } - - @Test - fun nameãŒnullãªDocumentã®ãƒ‡ã‚¤ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - //language=JSON - val json = """{ - "type": "Document", - "mediaType": "image/webp", - "url": "https://s3misskey.usbharu.dev/misskey-minio/misskey-minio/data/81ec9ad1-2581-466e-b90c-d9d2350ab95c.webp", - "name": null - }""" - - val objectMapper = ActivityPubConfig().objectMapper() - - objectMapper.readValue(json) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt deleted file mode 100644 index 9c9530cc..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/JsonLdSerializeTest.kt +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class JsonLdSerializeTest { - @Test - fun contextãŒæ–‡å­—列ã®ã¨ãデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{"@context":"https://example.com"}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals(JsonLd(listOf(StringOrObject("https://example.com"))), readValue) - } - - @Test - fun contextãŒæ–‡å­—列ã®é…列ã®ã¨ãデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{"@context":["https://example.com","https://www.w3.org/ns/activitystreams"]}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals( - JsonLd( - listOf( - StringOrObject("https://example.com"), - StringOrObject("https://www.w3.org/ns/activitystreams") - ) - ), readValue - ) - } - - @Test - fun contextãŒnullã®ã¨ã空ã®listã¨ã—ã¦è§£é‡ˆã—ã¦ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - //language=JSON - val json = """{"@context":null}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals(JsonLd(emptyList()), readValue) - } - - @Test - fun contextãŒnullã‚’å«ã‚€æ–‡å­—列ã®é…列ã®ã¨ãnullを無視ã—ã¦ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã§ãã‚‹() { - //language=JSON - val json = """{"@context":["https://example.com",null,"https://www.w3.org/ns/activitystreams"]}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals( - JsonLd( - listOf( - StringOrObject("https://example.com"), - StringOrObject("https://www.w3.org/ns/activitystreams") - ) - ), readValue - ) - } - - @Test - fun contextãŒã‚ªãƒ–ジェクトã®ã¨ã無視ã—ã¦ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - //language=JSON - val json = """{"@context":{"hoge": "fuga"}}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals(JsonLd(listOf(StringOrObject(mapOf("hoge" to "fuga")))), readValue) - } - - @Test - fun contextãŒã‚ªãƒ–ジェクトをå«ã‚€æ–‡å­—列ã®é…列ã®ã¨ãオブジェクトを無視ã—ã¦ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - //language=JSON - val json = """{"@context":["https://example.com",{"hoge": "fuga"},"https://www.w3.org/ns/activitystreams"]}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals( - JsonLd( - listOf( - StringOrObject("https://example.com"), - StringOrObject(mapOf("hoge" to "fuga")), - StringOrObject("https://www.w3.org/ns/activitystreams") - ) - ), readValue - ) - } - - @Test - fun contextãŒé…列ã®é…列ã®ã¨ã無視ã—ã¦ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - //language=JSON - val json = """{"@context":[["a","b"],["c","d"]]}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals(JsonLd(emptyList()), readValue) - } - - @Test - fun contextãŒç©ºã®ã¨ã無視ã—ã¦ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - val jsonLd = JsonLd(emptyList()) - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(jsonLd) - - assertEquals("{}", actual) - } - - @Test - fun contextãŒnullã®ã¨ã無視ã—ã¦ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - val jsonLd = JsonLd(listOf(null)) - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(jsonLd) - - assertEquals("{}", actual) - } - - @Test - fun contextãŒæ–‡å­—列ã®ã¨ã文字列ã¨ã—ã¦ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã•ã‚Œã‚‹() { - val jsonLd = JsonLd(listOf(StringOrObject("https://example.com"))) - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(jsonLd) - - assertEquals("""{"@context":"https://example.com"}""", actual) - } - - @Test - fun contextãŒæ–‡å­—列ã®é…列ã®ã¨ãé…列ã¨ã—ã¦ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã•ã‚Œã‚‹() { - val jsonLd = JsonLd( - listOf( - StringOrObject("https://example.com"), - StringOrObject("https://www.w3.org/ns/activitystreams") - ) - ) - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(jsonLd) - - assertEquals("""{"@context":["https://example.com","https://www.w3.org/ns/activitystreams"]}""", actual) - } - - @Test - fun contextãŒã‚ªãƒ–ジェクトã®ã¨ãシリアライズã§ãã‚‹() { - val jsonLd = JsonLd( - listOf( - StringOrObject(mapOf("hoge" to "fuga")) - ) - ) - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(jsonLd) - - assertEquals("""{"@context":{"hoge":"fuga"}}""", actual) - - } - - @Test - fun contextãŒè¤‡æ•°ã®ã‚ªãƒ–ジェクトã®ã¨ãシリアライズã§ãã‚‹() { - val jsonLd = JsonLd( - listOf( - StringOrObject(mapOf("hoge" to "fuga")), - StringOrObject(mapOf("foo" to "bar")) - ) - ) - - val objectMapper = ActivityPubConfig().objectMapper() - - val actual = objectMapper.writeValueAsString(jsonLd) - - assertEquals("""{"@context":[{"hoge":"fuga"},{"foo":"bar"}]}""", actual) - } - - @Test - fun contextãŒè¤‡æ•°ã®ã‚ªãƒ–ジェクトã®ã¨ãデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{"@context":["https://example.com",{"hoge": "fuga"},{"foo": "bar"}]}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals( - JsonLd( - listOf( - StringOrObject("https://example.com"), - StringOrObject(mapOf("hoge" to "fuga")), - StringOrObject(mapOf("foo" to "bar")) - ) - ), readValue - ) - } - - @Test - fun contextãŒã‚ªãƒ–ジェクトã®ã¨ãデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{"@context":{"hoge": "fuga"}}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - assertEquals( - JsonLd( - listOf( - StringOrObject(mapOf("hoge" to "fuga")) - ) - ), readValue - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt deleted file mode 100644 index c0ffbfaf..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/KeySerializeTest.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.junit.jupiter.api.Test - -class KeySerializeTest { - @Test - fun Keyã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - //language=JSON - val trimIndent = """ - { - "id": "https://mastodon.social/users/Gargron#main-key", - "owner": "https://mastodon.social/users/Gargron", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" - } - """.trimIndent() - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(trimIndent) - - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt deleted file mode 100644 index 8c4b3f9d..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/NoteSerializeTest.kt +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class NoteSerializeTest { - @Test - fun Noteã®ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - val note = Note( - id = "https://example.com", - attributedTo = "https://example.com/actor", - content = "Hello", - published = "2023-05-20T10:28:17.308Z", - ) - - val objectMapper = ActivityPubConfig().objectMapper() - - val writeValueAsString = objectMapper.writeValueAsString(note) - - assertEquals( - """{"type":"Note","id":"https://example.com","attributedTo":"https://example.com/actor","content":"Hello","published":"2023-05-20T10:28:17.308Z","sensitive":false}""", - writeValueAsString - ) - } - - @Test - fun Noteã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - //language=JSON - val json = """{ - "id": "https://misskey.usbharu.dev/notes/9f2i9cm88e", - "type": "Note", - "attributedTo": "https://misskey.usbharu.dev/users/97ws8y3rj6", - "content": "

@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦

", - "_misskey_content": "@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦", - "source": { - "content": "@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦", - "mediaType": "text/x.misskeymarkdown" - }, - "published": "2023-05-22T14:26:53.600Z", - "to": [ - "https://misskey.usbharu.dev/users/97ws8y3rj6/followers" - ], - "cc": [ - "https://www.w3.org/ns/activitystreams#Public", - "https://calckey.jp/users/9bu1xzwjyb" - ], - "inReplyTo": "https://calckey.jp/notes/9f2i7ymf1d", - "attachment": [], - "sensitive": false, - "tag": [ - - ] - }""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - val note = Note( - id = "https://misskey.usbharu.dev/notes/9f2i9cm88e", - type = listOf("Note"), - attributedTo = "https://misskey.usbharu.dev/users/97ws8y3rj6", - content = "

@trapezial@calckey.jp ã„ã‚„ãã†ã„ã†ã“ã¨ã˜ã‚ƒãªãã¦ã€é€£åˆå…ˆã¨è‡ªã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§çŠ¶æ…‹ãŒç‹‚ã†ã“ã¨ãŒå¤šã„ã®ã§ã©ã£ã¡ã«åˆã‚ã›ã‚‹ã¹ãã‹ã¨â€¦

", - published = "2023-05-22T14:26:53.600Z", - to = listOf("https://misskey.usbharu.dev/users/97ws8y3rj6/followers"), - cc = listOf(public, "https://calckey.jp/users/9bu1xzwjyb"), - sensitive = false, - inReplyTo = "https://calckey.jp/notes/9f2i7ymf1d", - attachment = emptyList() - ) - assertEquals(note, readValue) - } - - @Test - fun 絵文字付ãNoteã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - val json = """{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "Hashtag": "as:Hashtag", - "quoteUrl": "as:quoteUrl", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "featured": "toot:featured", - "discoverable": "toot:discoverable", - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value" - } - ], - "id": "https://misskey.usbharu.dev/notes/9nj1omt1rn", - "type": "Note", - "attributedTo": "https://misskey.usbharu.dev/users/97ws8y3rj6", - "content": "

​:oyasumi:​

", - "_misskey_content": ":oyasumi:", - "source": { - "content": ":oyasumi:", - "mediaType": "text/x.misskeymarkdown" - }, - "published": "2023-12-21T17:32:36.853Z", - "to": [ - "https://www.w3.org/ns/activitystreams#Public" - ], - "cc": [ - "https://misskey.usbharu.dev/users/97ws8y3rj6/followers" - ], - "inReplyTo": null, - "attachment": [], - "sensitive": false, - "tag": [ - { - "id": "https://misskey.usbharu.dev/emojis/oyasumi", - "type": "Emoji", - "name": ":oyasumi:", - "updated": "2023-04-07T08:21:25.246Z", - "icon": { - "type": "Image", - "mediaType": "image/png", - "url": "https://s3misskey.usbharu.dev/misskey-minio/misskey-minio/data/cf8db710-1d70-4076-8a00-dbb28131096e.png" - } - } - ] -}""" - - - val objectMapper = ActivityPubConfig().objectMapper() - - val expected = Note( - type = emptyList(), - id = "https://misskey.usbharu.dev/notes/9nj1omt1rn", - attributedTo = "https://misskey.usbharu.dev/users/97ws8y3rj6", - content = "

\u200B:oyasumi:\u200B

", - published = "2023-12-21T17:32:36.853Z", - to = listOf("https://www.w3.org/ns/activitystreams#Public"), - cc = listOf("https://misskey.usbharu.dev/users/97ws8y3rj6/followers"), - sensitive = false, - inReplyTo = null, - attachment = emptyList(), - tag = listOf( - Emoji( - type = emptyList(), - name = ":oyasumi:", - id = "https://misskey.usbharu.dev/emojis/oyasumi", - updated = "2023-04-07T08:21:25.246Z", - icon = Image( - type = emptyList(), - mediaType = "image/png", - url = "https://s3misskey.usbharu.dev/misskey-minio/misskey-minio/data/cf8db710-1d70-4076-8a00-dbb28131096e.png" - ) - ) - ) - ) - - expected.context = Constant.context - - val note = objectMapper.readValue(json) - - assertThat(note).isEqualTo(expected) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt deleted file mode 100644 index ddd39162..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/PersonSerializeTest.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.junit.jupiter.api.Test - -class PersonSerializeTest { - @Test - fun Mastodonã®Personã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - val personString = """ - { - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1" - ], - "id": "https://mastodon.social/users/Gargron", - "type": "Person", - "following": "https://mastodon.social/users/Gargron/following", - "followers": "https://mastodon.social/users/Gargron/followers", - "inbox": "https://mastodon.social/users/Gargron/inbox", - "outbox": "https://mastodon.social/users/Gargron/outbox", - "featured": "https://mastodon.social/users/Gargron/collections/featured", - "featuredTags": "https://mastodon.social/users/Gargron/collections/tags", - "preferredUsername": "Gargron", - "name": "Eugen Rochko", - "summary": "\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e", - "url": "https://mastodon.social/@Gargron", - "manuallyApprovesFollowers": false, - "discoverable": true, - "published": "2016-03-16T00:00:00Z", - "devices": "https://mastodon.social/users/Gargron/collections/devices", - "alsoKnownAs": [ - "https://tooting.ai/users/Gargron" - ], - "publicKey": { - "id": "https://mastodon.social/users/Gargron#main-key", - "owner": "https://mastodon.social/users/Gargron", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "tag": [], - "attachment": [ - { - "type": "PropertyValue", - "name": "Patreon", - "value": "\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - }, - { - "type": "PropertyValue", - "name": "GitHub", - "value": "\u003ca href=\"https://github.com/Gargron\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/Gargron\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - } - ], - "endpoints": { - "sharedInbox": "https://mastodon.social/inbox" - }, - "icon": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg" - }, - "image": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg" - } - } - - """.trimIndent() - - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(personString) - } - - @Test - fun Misskeyã®nameãŒnullã®Personã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - //language=JSON - val json = """{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "Hashtag": "as:Hashtag", - "quoteUrl": "as:quoteUrl", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "featured": "toot:featured", - "discoverable": "toot:discoverable", - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "misskey": "https://misskey-hub.net/ns#", - "_misskey_content": "misskey:_misskey_content", - "_misskey_quote": "misskey:_misskey_quote", - "_misskey_reaction": "misskey:_misskey_reaction", - "_misskey_votes": "misskey:_misskey_votes", - "_misskey_summary": "misskey:_misskey_summary", - "isCat": "misskey:isCat", - "vcard": "http://www.w3.org/2006/vcard/ns#" - } - ], - "type": "Person", - "id": "https://misskey.usbharu.dev/users/9ghwhv9zgg", - "inbox": "https://misskey.usbharu.dev/users/9ghwhv9zgg/inbox", - "outbox": "https://misskey.usbharu.dev/users/9ghwhv9zgg/outbox", - "followers": "https://misskey.usbharu.dev/users/9ghwhv9zgg/followers", - "following": "https://misskey.usbharu.dev/users/9ghwhv9zgg/following", - "featured": "https://misskey.usbharu.dev/users/9ghwhv9zgg/collections/featured", - "sharedInbox": "https://misskey.usbharu.dev/inbox", - "endpoints": { - "sharedInbox": "https://misskey.usbharu.dev/inbox" - }, - "url": "https://misskey.usbharu.dev/@relay_test", - "preferredUsername": "relay_test", - "name": null, - "summary": null, - "_misskey_summary": null, - "icon": null, - "image": null, - "tag": [], - "manuallyApprovesFollowers": true, - "discoverable": true, - "publicKey": { - "id": "https://misskey.usbharu.dev/users/9ghwhv9zgg#main-key", - "type": "Key", - "owner": "https://misskey.usbharu.dev/users/9ghwhv9zgg", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2n5yekTaI4ex5VDWzQfE\nJpWMURAMWl8RcXHLPyLQVQ/PrHp7qatGXmKJUnAOBcq1cwk+VCqTEqx8vJCOZsr1\nMq+D3FMcFdwgtJ0nivPJPx2457b5kfQ4LTkWajcFhj2qixa/XFq6hHei3LDaE6hJ\nGQbdj9NTVlMd7VpiFQkoU09vAPUwGxRoP9Qbc/sh7jrKYFB3iRmY/+zOc+PFpnfn\nG8V1d2v+lnkb9f7t0Z8y2ckk6TVcLPRZktF15eGClVptlgts3hwhrcyrpBs2Dn0U\n35KgIhkhZGAjzk0uyplpfKcserXuGvsjJvelZ3BtMGsuR4kGLHrmiRQp23mIoA1I\n8tfVuV0zPOyO3ruLk2fOjoeZ4XvFHGRNKo66Qx055/8G8Ug5vU8lvIGXm9sflaA9\ntR3AKDNsyxEfjAfrfgJ7cwlKSlLZmkU51jtYEqJ48ZkiIa6fMC0m4QGXdaXmhFWC\no1sGoIErRFpRHewdGlLC9S8R/cMxjex+n8maF0yh79y7aVvU+TS6pRWg5wYjY8r3\nZqAVg/PGRVGAbjVdIdcsjH5ClwAFBW16S633D3m7HJypwwVCzVOvMZqPqcQ/2o8c\nUk+xa88xQG+OPqoAaQqyV9iqsmCMgYM/AcX/BC2h7L2mE/PWoXnoCxGPxr5uvyBf\nHQakDGg4pFZcpVNrDlYo260CAwEAAQ==\n-----END PUBLIC KEY-----\n" - }, - "isCat": false -}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - objectMapper.readValue(json) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt deleted file mode 100644 index 65e26aac..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/RejectTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.assertj.core.api.Assertions.assertThat -import org.intellij.lang.annotations.Language -import org.junit.jupiter.api.Test -import org.springframework.boot.test.json.BasicJsonTester - -class RejectTest { - @Test - fun rejectDeserializeTest() { - @Language("JSON") val json = """{ - "@context" : [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { - "manuallyApprovesFollowers" : "as:manuallyApprovesFollowers", - "sensitive" : "as:sensitive", - "Hashtag" : "as:Hashtag", - "quoteUrl" : "as:quoteUrl", - "toot" : "http://joinmastodon.org/ns#", - "Emoji" : "toot:Emoji", - "featured" : "toot:featured", - "discoverable" : "toot:discoverable", - "schema" : "http://schema.org#", - "PropertyValue" : "schema:PropertyValue", - "value" : "schema:value" - } ], - "type" : "Reject", - "actor" : "https://misskey.usbharu.dev/users/97ws8y3rj6", - "object" : { - "id" : "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6", - "type" : "Follow", - "actor" : "https://test-hideout.usbharu.dev/users/test-user2", - "object" : "https://misskey.usbharu.dev/users/97ws8y3rj6" - }, - "id" : "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0" -} -""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val reject = objectMapper.readValue(json) - - val expected = Reject( - "https://misskey.usbharu.dev/users/97ws8y3rj6", - "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0", - Follow( - apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6", - actor = "https://test-hideout.usbharu.dev/users/test-user2", - id = "https://misskey.usbharu.dev/follows/9mxh6mawru/97ws8y3rj6" - ) - ).apply { - context = Constant.context - } - - assertThat(reject).isEqualTo(expected) - } - - @Test - fun rejectSerializeTest() { - val basicJsonTester = BasicJsonTester(javaClass) - - val reject = Reject( - "https://misskey.usbharu.dev/users/97ws8y3rj6", - "https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0", - Follow( - apObject = "https://misskey.usbharu.dev/users/97ws8y3rj6", - actor = "https://test-hideout.usbharu.dev/users/test-user2" - ) - ).apply { - context = listOf( - StringOrObject("https://www.w3.org/ns/activitystreams"), - StringOrObject("https://w3id.org/security/v1") - ) - } - - val objectMapper = ActivityPubConfig().objectMapper() - - val writeValueAsString = objectMapper.writeValueAsString(reject) - - val from = basicJsonTester.from(writeValueAsString) - - assertThat(from).extractingJsonPathStringValue("$.actor") - .isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6") - assertThat(from).extractingJsonPathStringValue("$.id") - .isEqualTo("https://misskey.usbharu.dev/06407419-5aeb-4e2d-8885-aa54b03decf0") - assertThat(from).extractingJsonPathStringValue("$.type").isEqualTo("Reject") - assertThat(from).extractingJsonPathStringValue("$.object.actor") - .isEqualTo("https://test-hideout.usbharu.dev/users/test-user2") - assertThat(from).extractingJsonPathStringValue("$.object.object") - .isEqualTo("https://misskey.usbharu.dev/users/97ws8y3rj6") - assertThat(from).extractingJsonPathStringValue("$.object.type").isEqualTo("Follow") - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt deleted file mode 100644 index f4688916..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/UndoTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model - -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.intellij.lang.annotations.Language -import org.junit.jupiter.api.Test -import java.time.Clock -import java.time.Instant -import java.time.ZoneId - -class UndoTest { - @Test - fun Undoã®ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - val undo = Undo( - emptyList(), - "https://follower.example.com/", - "https://follower.example.com/undo/1", - Follow( - emptyList(), - "https://follower.example.com/users/", - actor = "https://follower.exaple.com/users/1" - ), - Instant.now(Clock.tickMillis(ZoneId.systemDefault())).toString() - ) - val writeValueAsString = ActivityPubConfig().objectMapper().writeValueAsString(undo) - println(writeValueAsString) - } - - @Test - fun Undoをデシリアライズ出æ¥ã‚‹() { - @Language("JSON") - val json = """ - { - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "Hashtag": "as:Hashtag", - "quoteUrl": "as:quoteUrl", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "featured": "toot:featured", - "discoverable": "toot:discoverable", - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "misskey": "https://misskey-hub.net/ns#", - "_misskey_content": "misskey:_misskey_content", - "_misskey_quote": "misskey:_misskey_quote", - "_misskey_reaction": "misskey:_misskey_reaction", - "_misskey_votes": "misskey:_misskey_votes", - "isCat": "misskey:isCat", - "vcard": "http://www.w3.org/2006/vcard/ns#" - } - ], - "type": "Undo", - "id": "https://misskey.usbharu.dev/follows/97ws8y3rj6/9ezbh8qrh0/undo", - "actor": "https://misskey.usbharu.dev/users/97ws8y3rj6", - "object": { - "id": "https://misskey.usbharu.dev/follows/97ws8y3rj6/9ezbh8qrh0", - "type": "Follow", - "actor": "https://misskey.usbharu.dev/users/97ws8y3rj6", - "object": "https://test-hideout.usbharu.dev/users/test" - }, - "published": "2023-05-20T10:28:17.308Z" -} - - """.trimIndent() - - val undo = ActivityPubConfig().objectMapper().readValue(json, Undo::class.java) - println(undo) - } - - @Test - fun Mastodonã®Undoã®ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºãŒã§ãã‚‹() { - //language=JSON - val json = """{ - "@context" : "https://www.w3.org/ns/activitystreams", - "id" : "https://kb.usbharu.dev/users/usbharu#follows/12/undo", - "type" : "Undo", - "actor" : "https://kb.usbharu.dev/users/usbharu", - "object" : { - "id" : "https://kb.usbharu.dev/0347b269-4dcb-4eb1-b8c4-b5f157bb6957", - "type" : "Follow", - "actor" : "https://kb.usbharu.dev/users/usbharu", - "object" : "https://test-hideout.usbharu.dev/users/testuser15" - } -}""".trimIndent() - - val undo = ActivityPubConfig().objectMapper().readValue(json, Undo::class.java) - - println(undo) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt deleted file mode 100644 index a4981fe9..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectSerializeTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.domain.model.objects - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.application.config.ActivityPubConfig -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class ObjectSerializeTest { - @Test - fun typeãŒæ–‡å­—列ã®ã¨ãデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{"type": "Object"}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - val expected = Object( - listOf("Object") - ) - assertEquals(expected, readValue) - } - - @Test - fun typeãŒæ–‡å­—列ã®é…列ã®ã¨ãデシリアライズã§ãã‚‹() { - //language=JSON - val json = """{"type": ["Hoge","Object"]}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - val expected = Object( - listOf("Hoge", "Object") - ) - - assertEquals(expected, readValue) - } - - @Test - fun typeãŒç©ºã®ã¨ã無視ã—ã¦ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã™ã‚‹() { - //language=JSON - val json = """{"type": ""}""" - - val objectMapper = ActivityPubConfig().objectMapper() - - val readValue = objectMapper.readValue(json) - - val expected = Object( - emptyList() - ) - - assertEquals(expected, readValue) - } - -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt deleted file mode 100644 index 76239bdd..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/actor/ActorAPControllerImplTest.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.actor - -import dev.usbharu.hideout.activitypub.domain.model.Image -import dev.usbharu.hideout.activitypub.domain.model.Key -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders - -@ExtendWith(MockitoExtension::class) -class ActorAPControllerImplTest { - - private lateinit var mockMvc: MockMvc - - @Mock - private lateinit var apUserService: APUserService - - @InjectMocks - private lateinit var userAPControllerImpl: UserAPControllerImpl - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders - .standaloneSetup(userAPControllerImpl) - .setMessageConverters(MappingJackson2HttpMessageConverter(ActivityPubConfig().objectMapper())) - .build() - } - - @Test - fun `userAp 存在ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«GETã™ã‚‹ã¨PersonãŒè¿”ã£ã¦ãã‚‹`(): Unit = runTest { - val person = Person( - name = "Hoge", - id = "https://example.com/users/hoge", - preferredUsername = "hoge", - summary = "fuga", - inbox = "https://example.com/users/hoge/inbox", - outbox = "https://example.com/users/hoge/outbox", - url = "https://example.com/users/hoge", - icon = Image( - mediaType = "image/jpeg", - url = "https://example.com/users/hoge/icon.jpg" - ), - publicKey = Key( - id = "https://example.com/users/hoge#pubkey", - owner = "https://example.com/users/hoge", - publicKeyPem = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----" - ), - endpoints = mapOf("sharedInbox" to "https://example.com/inbox"), - followers = "https://example.com/users/hoge/followers", - following = "https://example.com/users/hoge/following", - manuallyApprovesFollowers = false - ) - whenever(apUserService.getPersonByName(eq("hoge"))).doReturn(person) - - val objectMapper = ActivityPubConfig().objectMapper() - - mockMvc - .get("/users/hoge") - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { this.json(objectMapper.writeValueAsString(person)) } } - } - - @Test - fun `userAP 存在ã—ãªã„ユーザーã«GETã™ã‚‹ã¨404ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - whenever(apUserService.getPersonByName(eq("fuga"))).doThrow(UserNotFoundException::class) - - mockMvc - .get("/users/fuga") - .asyncDispatch() - .andExpect { status { isNotFound() } } - } - - @Test - fun `userAP POSTã™ã‚‹ã¨405ãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc - .post("/users/hoge") - .andExpect { status { isMethodNotAllowed() } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt deleted file mode 100644 index 5ff88e2f..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/inbox/InboxControllerImplTest.kt +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.inbox - -import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import dev.usbharu.hideout.activitypub.service.common.APService -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker -import dev.usbharu.hideout.util.Base64Util -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import org.springframework.http.MediaType -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import java.net.URI -import java.security.MessageDigest -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.util.* - -@ExtendWith(MockitoExtension::class) -class InboxControllerImplTest { - - private lateinit var mockMvc: MockMvc - - @Spy - private val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("https://example.com").toURL())) - - @Mock - private lateinit var apService: APService - - @InjectMocks - private lateinit var inboxController: InboxControllerImpl - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(inboxController).build() - } - - - private val dateTimeFormatter: DateTimeFormatter = - DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - - @Test - fun `inbox 正常ãªPOSTリクエストをã—ãŸã¨ãAcceptãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - - val json = """{"type":"Follow"}""" - whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) - whenever( - apService.processActivity( - eq(json), eq(ActivityType.Follow), any(), any() - - ) - ).doReturn(Unit) - - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - - mockMvc.post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=" + digest) - }.asyncDispatch().andExpect { - status { isAccepted() } - } - - } - - @Test - fun `inbox parseActivityã«å¤±æ•—ã—ãŸã¨ãAcceptãŒè¿”ã£ã¦ãã‚‹`() = runTest { - val json = """{"type":"Hoge"}""" - whenever(apService.parseActivity(eq(json))).doThrow(JsonParseException::class) - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - mockMvc.post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - }.asyncDispatch().andExpect { - status { isAccepted() } - } - - } - - @Test - fun `inbox processActivityã«å¤±æ•—ã—ãŸã¨ãAcceptãŒè¿”ã£ã¦ãã‚‹`() = runTest { - val json = """{"type":"Follow"}""" - whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) - whenever( - apService.processActivity( - eq(json), eq(ActivityType.Follow), any(), any() - ) - ).doThrow(FailedToGetResourcesException::class) - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - mockMvc.post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - }.asyncDispatch().andExpect { - status { isAccepted() } - } - - } - - @Test - fun `inbox GETリクエストã«ã¯405ã‚’è¿”ã™`() { - mockMvc.get("/inbox").andExpect { status { isMethodNotAllowed() } } - } - - @Test - fun `user-inbox 正常ãªPOSTリクエストをã—ãŸã¨ãAcceptãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - - val json = """{"type":"Follow"}""" - whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) - whenever(apService.processActivity(eq(json), eq(ActivityType.Follow), any(), any())).doReturn( - Unit - ) - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - mockMvc.post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - }.asyncDispatch().andExpect { - status { isAccepted() } - } - - } - - @Test - fun `user-inbox parseActivityã«å¤±æ•—ã—ãŸã¨ãAcceptãŒè¿”ã£ã¦ãã‚‹`() = runTest { - val json = """{"type":"Hoge"}""" - whenever(apService.parseActivity(eq(json))).doThrow(JsonParseException::class) - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - mockMvc.post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - }.asyncDispatch().andExpect { - status { isAccepted() } - } - - } - - @Test - fun `user-inbox processActivityã«å¤±æ•—ã—ãŸã¨ãAcceptãŒè¿”ã£ã¦ãã‚‹`() = runTest { - val json = """{"type":"Follow"}""" - whenever(apService.parseActivity(eq(json))).doReturn(ActivityType.Follow) - whenever( - apService.processActivity( - eq(json), eq(ActivityType.Follow), any(), any() - ) - ).doThrow(FailedToGetResourcesException::class) - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - mockMvc.post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - }.asyncDispatch().andExpect { - status { isAccepted() } - } - - } - - @Test - fun `user-inbox GETリクエストã«ã¯405ã‚’è¿”ã™`() { - mockMvc.get("/users/hoge/inbox").andExpect { status { isMethodNotAllowed() } } - } - - @Test - fun `inbox DateヘッダーãŒç„¡ã„ã¨400`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - } - .asyncDispatch() - .andExpect { - status { - isBadRequest() - } - } - } - - @Test - fun `user-inbox DateヘッダーãŒç„¡ã„ã¨400`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - } - .asyncDispatch() - .andExpect { - status { - isBadRequest() - } - } - } - - @Test - fun `inbox DateヘッダーãŒæœªæ¥ã ã¨401`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().plusDays(1).format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { - isUnauthorized() - } - } - } - - @Test - fun `user-inbox DateヘッダーãŒæœªæ¥ã ã¨401`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().plusDays(1).format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { - isUnauthorized() - } - } - } - - @Test - fun `inbox DateヘッダーãŒéŽåŽ»éŽãŽã‚‹ã¨401`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().minusDays(1).format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { - isUnauthorized() - } - } - } - - @Test - fun `user-inbox DateヘッダーãŒéŽåŽ»éŽãŽã‚‹ã¨401`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().minusDays(1).format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { - isUnauthorized() - } - } - } - - @Test - fun `inbox HostヘッダーãŒç„¡ã„ã¨400`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { - isBadRequest() - } - } - } - - @Test - fun `user-inbox HostヘッダーãŒç„¡ã„ã¨400`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { - isBadRequest() - } - } - } - - @Test - fun `inbox HostヘッダーãŒé–“é•ã£ã¦ã‚‹ã¨401`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Host", "example.jp") - } - .asyncDispatch() - .andExpect { - status { - isUnauthorized() - } - } - } - - @Test - fun `user-inbox HostヘッダーãŒé–“é•ã£ã¦ã‚‹ã¨401`() { - val json = """{"type":"Follow"}""" - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Host", "example.jp") - } - .asyncDispatch() - .andExpect { - status { - isUnauthorized() - } - } - } - - @Test - fun `inbox DigestヘッダーãŒãªã„ã¨400`() = runTest { - - - val json = """{"type":"Follow"}""" - - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { isBadRequest() } - } - - } - - @Test - fun `inbox DigestヘッダーãŒé–“é•ã£ã¦ã‚‹ã¨401`() = runTest { - val json = """{"type":"Follow"}""" - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(("$json aaaaaaaa").toByteArray())) - - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - } - .asyncDispatch() - .andExpect { - status { isUnauthorized() } - } - } - - @Test - fun `user-inbox DigestヘッダーãŒãªã„ã¨400`() = runTest { - - - val json = """{"type":"Follow"}""" - - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - } - .asyncDispatch() - .andExpect { - status { isBadRequest() } - } - - } - - @Test - fun `user-inbox DigestヘッダーãŒé–“é•ã£ã¦ã‚‹ã¨401`() = runTest { - val json = """{"type":"Follow"}""" - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(("$json aaaaaaaa").toByteArray())) - - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - } - .asyncDispatch() - .andExpect { - status { isUnauthorized() } - } - } - - @Test - fun `inbox SignatureヘッダーãŒãªã„ã¨401`() = runTest { - - - val json = """{"type":"Follow"}""" - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - } - .asyncDispatch() - .andExpect { - status { isUnauthorized() } - } - - } - - @Test - fun `inbox SignatureヘッダーãŒç©ºã ã¨401`() = runTest { - val json = """{"type":"Follow"}""" - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - - mockMvc - .post("/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - } - .asyncDispatch() - .andExpect { - status { isUnauthorized() } - } - } - - @Test - fun `user-inbox DigestヘッダーãŒãªã„ã¨401`() = runTest { - - val json = """{"type":"Follow"}""" - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - } - .asyncDispatch() - .andExpect { - status { isUnauthorized() } - } - - } - - @Test - fun `user-inbox DigestヘッダーãŒç©ºã ã¨401`() = runTest { - val json = """{"type":"Follow"}""" - val sha256 = MessageDigest.getInstance("SHA-256") - - val digest = Base64Util.encode(sha256.digest(json.toByteArray())) - - mockMvc - .post("/users/hoge/inbox") { - content = json - contentType = MediaType.APPLICATION_JSON - header("Signature", "") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header("Digest", "SHA-256=$digest") - } - .asyncDispatch() - .andExpect { - status { isUnauthorized() } - } - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt deleted file mode 100644 index cb60896e..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/note/NoteApControllerImplTest.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.note - -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.service.objects.note.NoteApApiService -import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import java.net.URL - -@ExtendWith(MockitoExtension::class) -class NoteApControllerImplTest { - - private lateinit var mockMvc: MockMvc - - @Mock - private lateinit var noteApApiService: NoteApApiService - - @InjectMocks - private lateinit var noteApControllerImpl: NoteApControllerImpl - - @BeforeEach - fun setUp() { - - mockMvc = MockMvcBuilders.standaloneSetup(noteApControllerImpl) -// .apply( -// springSecurity( -// FilterChainProxy( -// DefaultSecurityFilterChain( -// AnyRequestMatcher.INSTANCE -// ) -// ) -// ) -// ) - .build() - } - - @Test - fun `postAP 匿åã§å–å¾—ã§ãã‚‹`() = runTest { - SecurityContextHolder.clearContext() - val note = Note( - id = "https://example.com/users/hoge/posts/1234", - attributedTo = "https://example.com/users/hoge", - content = "Hello", - published = "2023-11-02T15:30:34.160Z" - ) - whenever(noteApApiService.getNote(eq(1234), isNull())).doReturn( - note - ) - - val objectMapper = ActivityPubConfig().objectMapper() - - mockMvc - .get("/users/hoge/posts/1234") { -// with(anonymous()) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(note)) } } - } - - @Test - fun `postAP 存在ã—ãªã„å ´åˆã¯404`() = runTest { - SecurityContextHolder.clearContext() - whenever(noteApApiService.getNote(eq(123), isNull())).doReturn(null) - - mockMvc - .get("/users/hoge/posts/123") { -// with(anonymous()) - } - .asyncDispatch() - .andExpect { status { isNotFound() } } - } - - @Test - fun `postAP èªè¨¼ã«æˆåŠŸã—ã¦ã„ã‚‹å ´åˆuserIdãŒnullã§ãªã„`() = runTest { - val note = Note( - id = "https://example.com/users/hoge/posts/1234", - attributedTo = "https://example.com/users/hoge", - content = "Hello", - published = "2023-11-02T15:30:34.160Z" - ) - whenever(noteApApiService.getNote(eq(1234), isNotNull())).doReturn(note) - - val objectMapper = ActivityPubConfig().objectMapper() - - val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken( - "", HttpRequest( - URL("https://follower.example.com"), - HttpHeaders( - mapOf() - ), HttpMethod.GET - ) - ).apply { details = HttpSignatureUser("fuga", "follower.example.com", 123, true, true, mutableListOf()) } - SecurityContextHolder.getContext().authentication = preAuthenticatedAuthenticationToken - - mockMvc.get("/users/hoge/posts/1234") { -// with( -// authentication( -// preAuthenticatedAuthenticationToken -// ) -// ) - }.asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(note)) } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImplTest.kt deleted file mode 100644 index 927bd784..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/outbox/OutboxControllerImplTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.outbox - -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.junit.jupiter.MockitoExtension -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders - -@ExtendWith(MockitoExtension::class) -class OutboxControllerImplTest { - - private lateinit var mockMvc: MockMvc - - @InjectMocks - private lateinit var outboxController: OutboxControllerImpl - - @BeforeEach - fun setUp() { - mockMvc = - MockMvcBuilders.standaloneSetup(outboxController).build() - } - - @Test - fun `outbox GETã«501ã‚’è¿”ã™`() { - mockMvc - .get("/outbox") - .asyncDispatch() - .andDo { print() } - .andExpect { status { isNotImplemented() } } - } - - @Test - fun `user-outbox GETã«501ã‚’è¿”ã™`() { - mockMvc - .get("/users/hoge/outbox") - .asyncDispatch() - .andDo { print() } - .andExpect { status { isNotImplemented() } } - } - - @Test - fun `outbox POSTã«501ã‚’è¿”ã™`() { - mockMvc - .post("/outbox") - .asyncDispatch() - .andDo { print() } - .andExpect { status { isNotImplemented() } } - } - - @Test - fun `user-outbox POSTã«501ã‚’è¿”ã™`() { - mockMvc - .post("/users/hoge/outbox") - .asyncDispatch() - .andDo { print() } - .andExpect { status { isNotImplemented() } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt deleted file mode 100644 index a55770d4..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/interfaces/api/webfinger/WebFingerControllerTest.kt +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.interfaces.api.webfinger - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.webfinger.WebFinger -import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import utils.UserBuilder - -@ExtendWith(MockitoExtension::class) -class WebFingerControllerTest { - - private lateinit var mockMvc: MockMvc - - @Mock - private lateinit var webFingerApiService: WebFingerApiService - - @Mock - private lateinit var applicationConfig: ApplicationConfig - - @InjectMocks - private lateinit var webFingerController: WebFingerController - - @BeforeEach - fun setUp() { - this.mockMvc = MockMvcBuilders.standaloneSetup(webFingerController).build() - } - - @Test - fun `webfinger 存在ã™ã‚‹acctを指定ã—ãŸã¨ã200 OKã§WebFingerã®ãƒ¬ã‚¹ãƒãƒ³ã‚¹ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - val user = UserBuilder.localUserOf() - whenever( - webFingerApiService.findByNameAndDomain( - eq("hoge"), - eq("example.com") - ) - ).doReturn(user) - - val contentAsString = mockMvc.perform(get("/.well-known/webfinger?resource=acct:hoge@example.com")) - .andDo(print()) - .andExpect(status().isOk()) - .andReturn() - .response - .contentAsString - - val objectMapper = jacksonObjectMapper() - - val readValue = objectMapper.readValue(contentAsString) - - val expected = WebFinger( - subject = "acct:${user.name}@${user.domain}", - listOf( - WebFinger.Link( - "self", - "application/activity+json", - user.url - ) - ) - ) - - assertThat(readValue).isEqualTo(expected) - } - - @Test - fun `webfinger 存在ã—ãªã„acctを指定ã—ãŸã¨ã404 Not FoundãŒè¿”ã£ã¦ãã‚‹`() = runTest { - whenever( - webFingerApiService.findByNameAndDomain( - eq("fuga"), - eq("example.com") - ) - ).doThrow(UserNotFoundException::class) - - mockMvc.perform(get("/.well-known/webfinger?resource=acct:fuga@example.com")) - .andDo(print()) - .andExpect(status().isNotFound) - } - - @Test - fun `webfinger acctã¨ã—ã¦è§£é‡ˆã§ããªã„å ´åˆã¯400 Bad RequestãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc.perform(get("/.well-known/webfinger?resource=@hello@aa@aab@aaa")) - .andDo(print()) - .andExpect(status().isBadRequest) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt deleted file mode 100644 index f211ffff..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/accept/ApAcceptProcessorTest.kt +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.accept - -import dev.usbharu.hideout.activitypub.domain.exception.IllegalActivityPubObjectException -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.Like -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityType -import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.DynamicTest -import org.junit.jupiter.api.DynamicTest.dynamicTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestFactory -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.TestTransaction -import utils.UserBuilder -import java.net.URL - - -@ExtendWith(MockitoExtension::class) -class ApAcceptProcessorTest { - - @Mock - private lateinit var actorRepository: ActorRepository - - @Mock - private lateinit var relationshipService: RelationshipService - - @Spy - private val transaction = TestTransaction - - @InjectMocks - private lateinit var apAcceptProcessor: ApAcceptProcessor - - @Test - fun `internalProcess objectãŒFollowã®å ´åˆãƒ•ã‚©ãƒ­ãƒ¼ã‚’承èªã™ã‚‹`() = runTest { - - val json = """""" - val objectMapper = ActivityPubConfig().objectMapper() - val jsonNode = objectMapper.readTree(json) - - val accept = Accept( - apObject = Follow( - apObject = "https://example.com", - actor = "https://remote.example.com" - ), - actor = "https://example.com" - ) - val activity = ActivityPubProcessContext( - accept, jsonNode, HttpRequest( - URL("https://example.com"), - HttpHeaders(emptyMap()), HttpMethod.POST - ), null, true - ) - - val user = UserBuilder.localUserOf() - whenever(actorRepository.findByUrl(eq("https://example.com"))).doReturn(user) - val remoteUser = UserBuilder.remoteUserOf() - whenever(actorRepository.findByUrl(eq("https://remote.example.com"))).doReturn(remoteUser) - - apAcceptProcessor.internalProcess(activity) - - verify(relationshipService, times(1)).acceptFollowRequest(eq(user.id), eq(remoteUser.id), eq(false)) - } - - @Test - fun `internalProcess objectãŒFollow以外ã®å ´åˆIllegalActivityPubObjecExceptionãŒç™ºç”Ÿã™ã‚‹`() = runTest { - val json = """""" - val objectMapper = ActivityPubConfig().objectMapper() - val jsonNode = objectMapper.readTree(json) - - val accept = Accept( - apObject = Like( - apObject = "https://example.com", - actor = "https://remote.example.com", - content = "", - id = "" - ), - actor = "https://example.com" - ) - val activity = ActivityPubProcessContext( - accept, jsonNode, HttpRequest( - URL("https://example.com"), - HttpHeaders(emptyMap()), HttpMethod.POST - ), null, true - ) - - assertThrows { - apAcceptProcessor.internalProcess(activity) - } - } - - @Test - fun `isSupproted Acceptã«ã¯true`() { - val actual = apAcceptProcessor.isSupported(ActivityType.Accept) - assertThat(actual).isTrue() - } - - @TestFactory - fun `isSupported Accept以外ã«ã¯false`(): List { - return ActivityType - .values() - .filterNot { it == ActivityType.Accept } - .map { - dynamicTest("isSupported $it ã«ã¯false") { - - val actual = apAcceptProcessor.isSupported(it) - assertThat(actual).isFalse() - } - } - } - - @Test - fun `type Acceptã®classjavaãŒè¿”ã£ã¦ãã‚‹`() { - assertThat(apAcceptProcessor.type()).isEqualTo(Accept::class.java) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt deleted file mode 100644 index ab8fee9d..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/activity/follow/APSendFollowServiceImplTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.activity.follow - -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.application.config.ApplicationConfig -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import utils.UserBuilder -import java.net.URL - -class APSendFollowServiceImplTest { - @Test - fun `sendFollow フォローã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®inboxã«FollowオブジェクトãŒé€ã‚‰ã‚Œã‚‹`() = runTest { - val apRequestService = mock() - val applicationConfig = ApplicationConfig(URL("https://example.com")) - val apSendFollowServiceImpl = APSendFollowServiceImpl(apRequestService, applicationConfig) - - val sendFollowDto = SendFollowDto( - UserBuilder.localUserOf(), - UserBuilder.remoteUserOf() - ) - apSendFollowServiceImpl.sendFollow(sendFollowDto) - - val value = Follow( - apObject = sendFollowDto.followTargetActorId.url, - actor = sendFollowDto.actorId.url, - id = "${applicationConfig.url}/follow/${sendFollowDto.actorId.id}/${sendFollowDto.followTargetActorId.id}" - ) - verify(apRequestService, times(1)).apPost( - eq(sendFollowDto.followTargetActorId.inbox), - eq(value), - eq(sendFollowDto.actorId) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt deleted file mode 100644 index e8243f53..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APRequestServiceImplTest.kt +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.Constant -import dev.usbharu.hideout.activitypub.domain.model.Follow -import dev.usbharu.hideout.activitypub.domain.model.StringOrObject -import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.util.Base64Util -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.sign.HttpSignatureSigner -import dev.usbharu.httpsignature.sign.Signature -import io.ktor.client.* -import io.ktor.client.engine.mock.* -import io.ktor.util.* -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Test -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import utils.UserBuilder -import java.net.URL -import java.security.MessageDigest -import java.time.format.DateTimeFormatter -import java.util.* - - -class APRequestServiceImplTest { - @Test - fun `apGet signerãŒnullã®ã¨ãç½²åãªã—リクエストをã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl( - HttpClient(MockEngine { - assertTrue(it.headers.contains("Date")) - assertTrue(it.headers.contains("Accept")) - assertFalse(it.headers.contains("Signature")) - assertDoesNotThrow { - dateTimeFormatter.parse(it.headers["Date"]) - } - respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") - }), - ActivityPubConfig().objectMapper(), - mock(), - dateTimeFormatter - ) - - val responseClass = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apGet("https://example.com", responseClass = responseClass::class.java) - } - - @Test - fun `apGet signerãŒnullã§ã¯ãªã„ãŒprivateKeyãŒnullã®ã¨ãç½²åãªã—リクエストをã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl( - HttpClient(MockEngine { - assertTrue(it.headers.contains("Date")) - assertTrue(it.headers.contains("Accept")) - assertFalse(it.headers.contains("Signature")) - assertDoesNotThrow { - dateTimeFormatter.parse(it.headers["Date"]) - } - respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") - }), - ActivityPubConfig().objectMapper(), - mock(), - dateTimeFormatter - ) - - val responseClass = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apGet( - "https://example.com", - UserBuilder.remoteUserOf(), - responseClass = responseClass::class.java - ) - } - - @Test - fun `apGet signerã¨privatekeyãŒnullã§ã¯ãªã„ã¨ãç½²å付ãリクエストをã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val httpSignatureSigner = mock { - onBlocking { - sign( - any(), - any(), - eq(listOf("(request-target)", "date", "host", "accept")) - ) - } doReturn Signature( - HttpRequest(URL("https://example.com"), HttpHeaders(mapOf()), HttpMethod.GET), "", "" - ) - } - val apRequestServiceImpl = APRequestServiceImpl( - HttpClient(MockEngine { - assertTrue(it.headers.contains("Date")) - assertTrue(it.headers.contains("Accept")) - assertTrue(it.headers.contains("Signature")) - assertDoesNotThrow { - dateTimeFormatter.parse(it.headers["Date"]) - } - respond("""{"type":"Follow","object": "https://example.com","actor": "https://example.com"}""") - }), - ActivityPubConfig().objectMapper(), - httpSignatureSigner, - dateTimeFormatter - ) - - val responseClass = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apGet( - "https://example.com", - UserBuilder.localUserOf( - privateKey = "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJhNETcFVoZW36\n" + - "pDiaaUDa1FsWGqULUa6jDWYbMXFirbbceJEfvaasac+E8VUQ3krrEhYBArntB1do\n" + - "1Zq/MpI97WaQefwrBmjJwjYglB8AHF1RRqFlJ0aABMBvuHiIzuTPv4dLS4+pJQWl\n" + - "iE9TKsxXgUrEdWLmpSukZpyiWnrgFtJ8322LXRuL9+O4ivns1JfozbrHTprI4ohe\n" + - "6taZJX1mhGBXQT+U/UrEILk+z70P2rrwxwerdO7s6nkkC3ieJWdi924/AopDlg12\n" + - "8udubLPbpWVVrHbSKviUr3VKBKGe4xmvO7hqpGwKmctaXRVPjh/ue2mCIzv3qyxQ\n" + - "3n2Xyhb3AgMBAAECggEAGddiSC/bg+ud0spER+i/XFBm7cq052KuFlKdiVcpxxGn\n" + - "pVYApiVXvjxDVDTuR5950/MZxz9mQDL0zoi1s1b00eQjhttdrta/kT/KWRslboo0\n" + - "nTuFbsc+jyQM2Ua6jjCZvto8qzchUPtiYfu80Floor/9qnuzFwiPNCHEbD1WDG4m\n" + - "fLuH+INnGY6eRF+pgly1dykGs18DaR3vC9CWOqR9PWH+p/myksVymR5adKauMc+l\n" + - "gjLaeB1YjnzXnHYLqwtCgh053kedPG/xZZwq48YNP5npSBIHsd9g8JIPVNOOc6+s\n" + - "bbFqD9aQQxG/WaA5hxHRupLkKGjE6lw4SnVYzKMZIQKBgQDryFa3qzJIBrCQQa0r\n" + - "6YlmZeeCQ8mQL8d0gY0Ixo9Gm2/9J71m/oBkhOqnS6Z5e5UHS5iVaqM7sIOZ2Ony\n" + - "kPADAtxUsk71Il+z+JgyN3OQ+DROLREi2TIWS523hbtN7e/fRFs7KoN6cH7IeF13\n" + - "3pphg9+WWRGX7y1zMd1puY/gSwKBgQDazFrAt/oZbnDhkX350OdIybz62OHNyuZv\n" + - "UX9fFl9i93SF+UhOpJ8YvDJtfLEJUkwO+V3TB+we1OlOYMTqir5M8GFn6YDotwxB\n" + - "r6eT886UpJgtJwswwwW2yaXo7zXaeg3ovRE8RJ4y++Mhuqeq3ajIo7xlhQjzBDEf\n" + - "ZAqasSWwhQKBgQC0VbUlo1XAywUOQH0/oc4KOJS6CDjJBBIsZM3G0X9SBJ7B5Dwz\n" + - "4yG2QAbtT6oTLldMjiA036vbgmUVLVe5w+sekniMexhy2wiRsOhPOCQ20+/Ffyil\n" + - "G7P4Y3tMm4cn0n1tqW2RsjF/Wz1M/OqYPPSc8uz2pEcVisSbX582Nsv5QwKBgEuy\n" + - "vAtFG6BE14UTIzSVFA/YzCs1choTAtqspZauVN4WoxffASdESU7zfbbnlxCUin/7\n" + - "wnxKl2SrYPSfAkHrMp/H4stivBjHi9QGA8JqbaR7tbKZeYOrVYTCC0alzEoERF+r\n" + - "WhUx4FHfV9vJikzRV53jGEE/X7NEVgJ4SDrw4wtJAoGAAMJ2kOIL3HSQPd8csXeU\n" + - "nkxLNzBsFpF76LVmLdzJttlr8HWBjLP/EJFQZFzuf5Hd38cLUOWWD3FRZVw0dUcN\n" + - "RSqfIYT4yDc/9GSRb6rOkdmBUWpTsrZjXBo0MC3p1QE6sNO8JfvmxHTSAe8apBh/\n" + - "gaYuQGh0lNa23HwwFoJxuoc=\n" + - "-----END PRIVATE KEY-----" - ), - responseClass = responseClass::class.java - ) - } - - @Test - fun `apPost bodyãŒnullã§ãªã„ã¨ãcontextã«activitystreamã®URLを追加ã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - val readValue = ActivityPubConfig().objectMapper().readValue(it.body.toByteArray()) - - assertThat(readValue.context).containsAll(Constant.context) - - respondOk("{}") - }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) - - val body = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apPost("https://example.com", body, null) - } - - @Test - fun `apPost bodyãŒnullã®ã¨ãリクエストボディã¯ç©º`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - - assertEquals(0, it.body.toByteArray().size) - - respondOk("{}") - }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) - - apRequestServiceImpl.apPost("https://example.com", null, null) - } - - @Test - fun `apPost signerãŒnullã®ã¨ãç½²åãªã—リクエストをã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - val src = it.body.toByteArray() - val readValue = ActivityPubConfig().objectMapper().readValue(src) - - assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) - - val map = it.headers.toMap() - assertThat(map).containsKey("Date") - .containsKey("Digest") - .containsKey("Accept") - .doesNotContainKey("Signature") - - assertDoesNotThrow { - dateTimeFormatter.parse(it.headers["Date"]) - } - val messageDigest = MessageDigest.getInstance("SHA-256") - val digest = Base64Util.encode(messageDigest.digest(src)) - - assertEquals(digest, it.headers["Digest"].orEmpty().split("256=").last()) - - respondOk("{}") - }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) - - val body = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apPost("https://example.com", body, null) - } - - @Test - fun `apPost signerãŒnullã§ã¯ãªã„ãŒprivatekeyãŒnullã®ã¨ãç½²åãªã—リクエストをã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - val src = it.body.toByteArray() - val readValue = ActivityPubConfig().objectMapper().readValue(src) - - assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) - - val map = it.headers.toMap() - assertThat(map).containsKey("Date") - .containsKey("Digest") - .containsKey("Accept") - .doesNotContainKey("Signature") - - val messageDigest = MessageDigest.getInstance("SHA-256") - val digest = Base64Util.encode(messageDigest.digest(src)) - - assertEquals(digest, it.headers["Digest"].orEmpty().split("256=").last()) - - respondOk("{}") - }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) - - val body = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apPost("https://example.com", body, UserBuilder.remoteUserOf()) - } - - @Test - fun `apPost signerãŒnullã§ã¯ãªã„ã¨ãç½²å付ãリクエストをã™ã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val httpSignatureSigner = mock { - onBlocking { - sign( - any(), - any(), - eq(listOf("(request-target)", "date", "host", "digest")) - ) - } doReturn Signature( - HttpRequest(URL("https://example.com"), HttpHeaders(mapOf()), HttpMethod.POST), "", "" - ) - } - val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - val src = it.body.toByteArray() - val readValue = ActivityPubConfig().objectMapper().readValue(src) - - assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) - - val map = it.headers.toMap() - assertThat(map).containsKey("Date") - .containsKey("Digest") - .containsKey("Accept") - .containsKey("Signature") - - val messageDigest = MessageDigest.getInstance("SHA-256") - val digest = Base64Util.encode(messageDigest.digest(src)) - - assertEquals(digest, it.headers["Digest"].orEmpty().split("256=").last()) - - respondOk("{}") - }), ActivityPubConfig().objectMapper(), httpSignatureSigner, dateTimeFormatter) - - val body = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - apRequestServiceImpl.apPost( - "https://example.com", body, UserBuilder.localUserOf( - privateKey = "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1+pj+/t5WwU6P\n" + - "OiaAKfOHCUVMdOR5e2Jp0BUYfAFpim27pLsHRXVjdzs+D4gvDnQWC0FMltPyBldk\n" + - "gjisNMtTKgTTsYhlLlSi+yRDZvIQyH4b7xSX0hCeflTrTkt18ZldBRPfMHE0KSho\n" + - "mm3Lc7ubF32YzGoo3A3qEVDAR9dVQOnt/GXLiN4RHoStX+y5UiP6B4s49nyEwuLm\n" + - "+HE4ph3Loqn0dTEL4cEuI8ZX51J3mTKT3rmMo0wCXXOm8gD2Fu7hYEdr9ulWF8GO\n" + - "yVe7Miu9prbBlY/r4skdXc5o6uE8tsPT88Ly9lSr3xqbmn1/EhyqBRdcyoj28C65\n" + - "cThO38jvAgMBAAECggEAFbOaXkJ3smHgI/17zOnz1EU7QehovMIFlPfPJDnZk0QC\n" + - "XQ/CjBXw71kvM/H3PCFdn6lc8qzD/sdZ0a8j4glzu+m1ZKd1zBcv2bXYd79Fm9HF\n" + - "FEC5NHfFKpmHN/6AykJzFyA9Y+7reRx1aLAN6ubU1ySAgmHSQSgo8qJ4/k0y9UQS\n" + - "EbjxQL5ziXuxRBMn7InLUGLl5UfCC0V1R8MZQAe+fApKDXMQ0LHSJUg1A365PyhV\n" + - "seotqvhurHH3UVHf5n0/sFeqp2hI4ymR3cs4kd8IuNIXE7afh+89IyuVKMvJh+iQ\n" + - "ZGO1RL0v0mNtUpI81agSrrQ4LRBjSkP+5s5PdXTrSQKBgQD2lwMXLylhQzhRyhLx\n" + - "sSPRf9mKDUcretwA5Fh9GuAurKOz7SvIdzrUPFYUTUKSTwk8mVRRamkFtJ8IOB7Z\n" + - "MLenlFqxs4XrNGBcZxut5cPv68xn2F00Y4HwX9xmEi+vniNVrDpdVLxEoVfm1pBk\n" + - "02ZHCcfYVN0t8dnvXvlL+eJSqQKBgQC87GMoMvFnWgT23wdXtQH+F+gQAMUrkMWz\n" + - "Ld2uRwuSVQArgp+YgnwWMlYlFp/QIW90t7UVmf6bHIplO5bL2OwayIO1r/WxD1eN\n" + - "RLrFIeDbtCZWQTHUypnWtl+9lrh/RrCjZo/sZFl07OSIKgGM37j9taG6Nv6fV7gv\n" + - "T0q6eDCV1wKBgGh3CUQlIq6lv5JGvUfO95GlTA+EGIZ/Af0Ov74gSKD9Wky7STUf\n" + - "7bhD52OqZ218NjmJ64KiReO45TaiL89rKCLCYrmtiCpgggIjXEKLeDqH9ox3yOSM\n" + - "01t2APTs926629VLpV4sq6WXhJmyhHFybX3i0tr++MSiFOWnoo1hS1QhAoGAfVY6\n" + - "ppW9kDqppnrqrSZ6Lu//VnacWL3QW4JnWtLpe2iHF1auuQiAeF1mx25OEk/MWNvz\n" + - "+GPVBWUW7/hrn8vHQDGdJ/GYB6LNC/z4CAbk3f2TnY/dFnZfP5J4zBftSQtF7vIB\n" + - "M+yTaL4tE6UCqEpYuYFBzX/kxyP0Hvb09eb9HLsCgYEArFSgWpaLbADcWd+ygWls\n" + - "LNfch1Yl2bnqXKz1Dnw3J4l2gbVNcABXQLrB6upjtkytxj4ae66Sio7nf+dB5yJ6\n" + - "NVY7i4C0JrniY2OvLnuz2bKpaTgMPJxyZqGQ6Vu2b3x9WhcpiI83SCuCUgBKxjh/\n" + - "qEGv2ZqFfnNVrz5RXLHBoG4=\n" + - "-----END PRIVATE KEY-----" - ) - ) - } - - @Test - fun `apPost responseClassを指定ã—ãŸå ´åˆã¯jsonã§ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºã•ã‚Œã‚‹`() = runTest { - val dateTimeFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - val apRequestServiceImpl = APRequestServiceImpl(HttpClient(MockEngine { - val src = it.body.toByteArray() - val readValue = ActivityPubConfig().objectMapper().readValue(src) - - assertThat(readValue.context).contains(StringOrObject("https://www.w3.org/ns/activitystreams")) - - respondOk(src.decodeToString()) - }), ActivityPubConfig().objectMapper(), mock(), dateTimeFormatter) - - val body = Follow( - apObject = "https://example.com", - actor = "https://example.com" - ) - val actual = apRequestServiceImpl.apPost("https://example.com", body, null, body::class.java) - - assertThat(body).isEqualTo(actual) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt deleted file mode 100644 index bb96522a..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.core.service.resource.InMemoryCacheManager -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.UserBuilder -import dev.usbharu.hideout.activitypub.domain.model.objects.Object as APObject - -@ExtendWith(MockitoExtension::class) - -class APResourceResolveServiceImplTest { - - @Test - fun `å˜ç´”ãªä¸€å›žã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆ`() = runTest { - - - val actorRepository = mock() - - val user = UserBuilder.localUserOf() - whenever(actorRepository.findById(any())) doReturn user - - val apRequestService = mock { - onBlocking { - apGet( - eq("https"), - eq(user), - eq(APObject::class.java) - ) - } doReturn APObject( - emptyList() - ) - } - val apResourceResolveService = - APResourceResolveServiceImpl(apRequestService, actorRepository, InMemoryCacheManager()) - - apResourceResolveService.resolve("https", 0) - - verify(apRequestService, times(1)).apGet(eq("https"), eq(user), eq(APObject::class.java)) - } - - @Test - fun 複数回ã®åŒã˜ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒé‡è¤‡ã—ã¦ç™ºè¡Œã•ã‚Œãªã„() = runTest { - - - val actorRepository = mock() - - val user = UserBuilder.localUserOf() - whenever(actorRepository.findById(any())) doReturn user - - val apRequestService = mock { - onBlocking { - apGet( - eq("https"), - eq(user), - eq(APObject::class.java) - ) - } doReturn APObject( - emptyList() - ) - } - val apResourceResolveService = - APResourceResolveServiceImpl(apRequestService, actorRepository, InMemoryCacheManager()) - - apResourceResolveService.resolve("https", 0) - apResourceResolveService.resolve("https", 0) - apResourceResolveService.resolve("https", 0) - apResourceResolveService.resolve("https", 0) - - verify(apRequestService, times(1)).apGet( - eq("https"), - eq(user), - eq(APObject::class.java) - ) - } - - @Test - fun 複数回ã®åŒã˜ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒåŒæ™‚ã«ç™ºè¡Œã•ã‚Œã¦ã‚‚é‡è¤‡ã—ã¦ç™ºè¡Œã•ã‚Œãªã„() = runTest { - - - val actorRepository = mock() - val user = UserBuilder.localUserOf() - - whenever(actorRepository.findById(any())) doReturn user - - - val apRequestService = mock { - onBlocking { - apGet( - eq("https"), - eq(user), - eq(APObject::class.java) - ) - } doReturn APObject( - emptyList() - ) - } - val apResourceResolveService = - APResourceResolveServiceImpl(apRequestService, actorRepository, InMemoryCacheManager()) - - repeat(10) { - awaitAll( - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - async { apResourceResolveService.resolve("https", 0) }, - ) - } - - verify(apRequestService, times(1)).apGet( - eq("https"), - eq(user), - eq(APObject::class.java) - ) - } - - @Test - fun 関係ã®ãªã„リクエストã¯ç™ºè¡Œã™ã‚‹() = runTest { - - val actorRepository = mock() - - val user = UserBuilder.localUserOf() - whenever(actorRepository.findById(any())).doReturn( - user - ) - - val apRequestService = mock { - onBlocking { - apGet( - any(), - eq(user), - eq(APObject::class.java) - ) - } doReturn APObject( - emptyList() - ) - } - - val apResourceResolveService = - APResourceResolveServiceImpl(apRequestService, actorRepository, InMemoryCacheManager()) - - apResourceResolveService.resolve("abcd", 0) - apResourceResolveService.resolve("1234", 0) - apResourceResolveService.resolve("aaaa", 0) - - verify(apRequestService, times(3)).apGet( - any(), - eq(user), - eq(APObject::class.java) - ) - } - - -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt deleted file mode 100644 index b924baeb..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APServiceImplTest.kt +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.exception.JsonParseException -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.mockito.kotlin.mock -import utils.JsonObjectMapper.objectMapper -import kotlin.test.assertEquals - -class APServiceImplTest { - @Test - fun `parseActivity 正常ãªActivityをパースã§ãã‚‹`() { - val apServiceImpl = APServiceImpl( - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - val activityType = apServiceImpl.parseActivity("""{"type": "Follow"}""") - - assertEquals(ActivityType.Follow, activityType) - } - - @Test - fun `parseActivity TypeãŒé…列ã®Activityをパースã§ãã‚‹`() { - val apServiceImpl = APServiceImpl( - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - val activityType = apServiceImpl.parseActivity("""{"type": ["Follow"]}""") - - assertEquals(ActivityType.Follow, activityType) - } - - @Test - fun `parseActivity TypeãŒé…列ã§é–¢ä¿‚ãªã„物ãŒå…¥ã£ã¦ã„ã¦ã‚‚パースã§ãã‚‹`() { - val apServiceImpl = APServiceImpl( - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - val activityType = apServiceImpl.parseActivity("""{"type": ["Hello","Follow"]}""") - - assertEquals(ActivityType.Follow, activityType) - } - - @Test - fun `parseActivity jsonã¨ã—ã¦è§£é‡ˆã§ããªã„å ´åˆJsonParseExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("""hoge""") - } - } - - @Test - fun `parseActivity 空ã®å ´åˆJsonParseExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("") - } - } - - @Test - fun `parseActivity jsonã«typeプロパティãŒãªã„å ´åˆJsonParseExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("""{"actor": "https://example.com"}""") - } - } - - @Test - fun `parseActivity typeãŒé…列ã§ãªã„ã¨ãtypeãŒæœªå®šç¾©ã®å ´åˆIllegalArgumentExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("""{"type": "Hoge"}""") - } - } - - @Test - fun `parseActivity typeãŒé…列ã®ã¨ã定義済ã¿ã®typeを見ã¤ã‘られãªã‹ã£ãŸå ´åˆIllegalArgumentExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("""{"type": ["Hoge","Fuga"]}""") - } - } - - @Test - fun `parseActivity typeãŒç©ºã®å ´åˆIllegalArgumentExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("""{"type": ""}""") - } - } - - @Test - fun `parseActivity typeã«æŒ‡å®šã•ã‚Œã¦ã„る文字ã®åˆ¤å®šãŒcase-insensitiveã§è¡Œã‚れる`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - val activityType = apServiceImpl.parseActivity("""{"type": "FoLlOw"}""") - - assertEquals(ActivityType.Follow, activityType) - } - - @Test - fun `parseActivity typeãŒé…列ã®ã¨ã指定ã•ã‚Œã¦ã„る文字ã®åˆ¤å®šãŒcase-insensitiveã§è¡Œã‚れる`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - val activityType = apServiceImpl.parseActivity("""{"type": ["HoGE","fOllOw"]}""") - - assertEquals(ActivityType.Follow, activityType) - } - - @Test - fun `parseActivity activityãŒarrayã®ã¨ãJsonParseExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity("""[{"type": "Follow"},{"type": "Accept"}]""") - } - } - - @Test - fun `parseActivity activityãŒvalueã®ã¨ãJsonParseExceptionãŒthrowã•ã‚Œã‚‹`() { - val apServiceImpl = APServiceImpl( - - objectMapper = objectMapper, owlProducer = mock() - ) - - //language=JSON - assertThrows { - apServiceImpl.parseActivity(""""hoge"""") - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt deleted file mode 100644 index 33e745a8..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/activitypub/service/objects/note/APNoteServiceImplTest.kt +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - -package dev.usbharu.hideout.activitypub.service.objects.note - -import dev.usbharu.hideout.activitypub.domain.exception.FailedToGetActivityPubResourceException -import dev.usbharu.hideout.activitypub.domain.model.Image -import dev.usbharu.hideout.activitypub.domain.model.Key -import dev.usbharu.hideout.activitypub.domain.model.Note -import dev.usbharu.hideout.activitypub.domain.model.Person -import dev.usbharu.hideout.activitypub.query.AnnounceQueryService -import dev.usbharu.hideout.activitypub.query.NoteQueryService -import dev.usbharu.hideout.activitypub.service.common.APResourceResolveService -import dev.usbharu.hideout.activitypub.service.objects.emoji.EmojiService -import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl.Companion.public -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.application.config.HtmlSanitizeConfig -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter -import io.ktor.client.* -import io.ktor.client.call.* -import io.ktor.client.plugins.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.client.utils.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.util.* -import io.ktor.util.date.* -import jakarta.validation.Validation -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.PostBuilder -import utils.UserBuilder -import java.time.Instant - -@ExtendWith(MockitoExtension::class) -class APNoteServiceImplTest { - - @Mock - private lateinit var postRepository: PostRepository - - @Mock - private lateinit var apUserService: APUserService - - @Mock - private lateinit var postService: PostService - - @Mock - private lateinit var apResourceResolverService: APResourceResolveService - - @Spy - private val postBuilder: Post.PostBuilder = Post.PostBuilder( - CharacterLimit(), DefaultPostContentFormatter(HtmlSanitizeConfig().policy()), - Validation.buildDefaultValidatorFactory().validator - ) - - @Mock - private lateinit var noteQueryService: NoteQueryService - - @Mock - private lateinit var mediaService: MediaService - - @Mock - private lateinit var emojiService: EmojiService - - @Mock - private lateinit var announceQueryService: AnnounceQueryService - - @InjectMocks - private lateinit var apNoteServiceImpl: APNoteServiceImpl - - @Test - fun `fetchNote(String,String) ノートãŒæ—¢ã«å­˜åœ¨ã™ã‚‹å ´åˆã¯DBã‹ã‚‰å–å¾—ã—ãŸã‚‚ã®ã‚’è¿”ã™`() = runTest { - val url = "https://example.com/note" - val post = PostBuilder.of() - - val user = UserBuilder.localUserOf(id = post.actorId) - val expected = Note( - id = post.apId, - attributedTo = user.url, - content = post.text, - published = Instant.ofEpochMilli(post.createdAt).toString(), - to = listOfNotNull(public, user.followers), - sensitive = post.sensitive, - cc = listOfNotNull(public, user.followers), - inReplyTo = null - ) - - whenever(noteQueryService.findByApid(eq(url))).doReturn(expected to post) - - val actual = apNoteServiceImpl.fetchNote(url) - - assertEquals(expected, actual) - } - - @Test - fun `fetchNote(String,String) ノートãŒDBã«å­˜åœ¨ã—ãªã„å ´åˆãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«ã„ã`() = runTest { - val url = "https://example.com/note" - val post = PostBuilder.of() - - - val user = UserBuilder.localUserOf(id = post.actorId) - - val note = Note( - id = post.apId, - attributedTo = user.url, - content = post.text, - published = Instant.ofEpochMilli(post.createdAt).toString(), - to = listOfNotNull(public, user.followers), - sensitive = post.sensitive, - cc = listOfNotNull(public, user.followers), - inReplyTo = null - ) - - whenever(apResourceResolverService.resolve(eq(url), any(), isNull())).doReturn(note) - - whenever(noteQueryService.findByApid(eq(url))).doReturn(null) - - val person = Person( - name = user.name, - id = user.url, - preferredUsername = user.name, - summary = user.description, - inbox = user.inbox, - outbox = user.outbox, - url = user.url, - icon = Image( - type = emptyList(), - mediaType = "image/png", - url = user.url + "/icon.png" - ), - publicKey = Key( - id = user.keyId, - owner = user.url, - publicKeyPem = user.publicKey - ), - endpoints = mapOf("sharedInbox" to "https://example.com/inbox"), - followers = user.followers, - following = user.following, - manuallyApprovesFollowers = false - - ) - - whenever( - apUserService.fetchPersonWithEntity( - eq(note.attributedTo), - isNull(), - anyOrNull() - ) - ).doReturn(person to user) - - whenever(postRepository.generateId()).doReturn(TwitterSnowflakeIdGenerateService.generateId()) - - val actual = apNoteServiceImpl.fetchNote(url) - - assertEquals(note, actual) - } - - @OptIn(InternalAPI::class) - @Test - fun `fetchNote(String,String) ノートをリモートã‹ã‚‰å–å¾—ã—ãŸéš›ã«ã‚¨ãƒ©ãƒ¼ãŒè¿”ã£ã¦ããŸã‚‰FailedToGetActivityPubResourceExceptionãŒthrowã•ã‚Œã‚‹`() = - runTest { - val url = "https://example.com/note" - val responseData = HttpResponseData( - HttpStatusCode.BadRequest, - GMTDate(), - Headers.Empty, - HttpProtocolVersion.HTTP_1_1, - NullBody, - Dispatchers.IO - ) - whenever(apResourceResolverService.resolve(eq(url), any(), isNull())).doThrow( - ClientRequestException( - DefaultHttpResponse( - HttpClientCall( - HttpClient(), HttpRequestData( - Url("http://example.com"), - HttpMethod.Get, - Headers.Empty, - EmptyContent, - Job(null), - Attributes() - ), responseData - ), responseData - ), "" - ) - ) - - whenever(noteQueryService.findByApid(eq(url))).doReturn(null) - - assertThrows { apNoteServiceImpl.fetchNote(url) } - - } - - @Test - fun `fetchNote(Note,String) DBã«ç„¡ã„Noteã¯ä¿å­˜ã•ã‚Œã‚‹`() = runTest { - val user = UserBuilder.localUserOf() - val generateId = TwitterSnowflakeIdGenerateService.generateId() - val post = PostBuilder.of(id = generateId, userId = user.id) - - whenever(postRepository.generateId()).doReturn(generateId) - - val person = Person( - name = user.name, - id = user.url, - preferredUsername = user.name, - summary = user.name, - inbox = user.inbox, - outbox = user.outbox, - url = user.url, - icon = Image( - mediaType = "image/png", - url = user.url + "/icon.png" - ), - publicKey = Key( - id = user.keyId, - owner = user.url, - publicKeyPem = user.publicKey - ), - endpoints = mapOf("sharedInbox" to "https://example.com/inbox"), - following = user.following, - followers = user.followers - ) - - whenever(apUserService.fetchPersonWithEntity(eq(user.url), anyOrNull(), anyOrNull())).doReturn(person to user) - - whenever(noteQueryService.findByApid(eq(post.apId))).doReturn(null) - - val note = Note( - id = post.apId, - attributedTo = user.url, - content = post.text, - published = Instant.ofEpochMilli(post.createdAt).toString(), - to = listOfNotNull(public, user.followers), - sensitive = post.sensitive, - cc = listOfNotNull(public, user.followers), - inReplyTo = null - ) - - - val fetchNote = apNoteServiceImpl.fetchNote(note, null) - verify(postService, times(1)).createRemote( - eq( - PostBuilder.of( - id = generateId, userId = user.id, createdAt = post.createdAt - ) - ) - ) - assertEquals(note, fetchNote) - } - - @Test - fun `fetchNote DBã«å­˜åœ¨ã™ã‚‹å ´åˆå–å¾—ã—ã¦è¿”ã™`() = runTest { - - val user = UserBuilder.localUserOf() - val post = PostBuilder.of(userId = user.id) - - val note = Note( - id = post.apId, - attributedTo = user.url, - content = post.text, - published = Instant.ofEpochMilli(post.createdAt).toString(), - to = listOfNotNull(public, user.followers), - sensitive = post.sensitive, - cc = listOfNotNull(public, user.followers), - inReplyTo = null - ) - - whenever(noteQueryService.findByApid(post.apId)).doReturn(note to post) - - val fetchNote = apNoteServiceImpl.fetchNote(note, null) - assertEquals(note, fetchNote) - } - - -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextDeserializerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextDeserializerTest.kt deleted file mode 100644 index 5a21e241..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextDeserializerTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.ap - -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.activitypub.domain.model.Follow - -class ContextDeserializerTest { - - @org.junit.jupiter.api.Test - fun deserialize() { - //language=JSON - val s = """ - { - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "Hashtag": "as:Hashtag", - "quoteUrl": "as:quoteUrl", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "featured": "toot:featured", - "discoverable": "toot:discoverable", - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "misskey": "https://misskey-hub.net/ns#", - "_misskey_content": "misskey:_misskey_content", - "_misskey_quote": "misskey:_misskey_quote", - "_misskey_reaction": "misskey:_misskey_reaction", - "_misskey_votes": "misskey:_misskey_votes", - "_misskey_talk": "misskey:_misskey_talk", - "isCat": "misskey:isCat", - "vcard": "http://www.w3.org/2006/vcard/ns#" - } - ], - "id": "https://test-misskey-v12.usbharu.dev/follows/9bg1zu54y7/9cydqvpjcn", - "type": "Follow", - "actor": "https://test-misskey-v12.usbharu.dev/users/9bg1zu54y7", - "object": "https://test-hideout.usbharu.dev/users/test3" -} - -""" - val readValue = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue(s) - println(readValue) - println(readValue.actor) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt deleted file mode 100644 index 4500bc95..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/ap/ContextSerializerTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.ap - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.activitypub.domain.model.Accept -import dev.usbharu.hideout.activitypub.domain.model.Follow -import org.junit.jupiter.api.Test - -class ContextSerializerTest { - - @Test - fun serialize() { - val accept = Accept( - actor = "bbb", - apObject = Follow( - apObject = "ddd", - actor = "aaa" - ) - ) - val writeValueAsString = jacksonObjectMapper().writeValueAsString(accept) - println(writeValueAsString) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt deleted file mode 100644 index 6e57f239..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.exposed - -import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.transactions.transaction -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class ExposedPaginationExtensionKtTest { - - @BeforeEach - fun setUp(): Unit = transaction { - val map = (1..100).map { it to it.toString() } - - ExposePaginationTestTable.batchInsert(map){ - this[ExposePaginationTestTable.id] = it.first.toLong() - this[ExposePaginationTestTable.name] = it.second - } - } - - @AfterEach - fun tearDown():Unit = transaction { - ExposePaginationTestTable.deleteAll() - } - - @Test - fun パラメーター無ã—ã§ã®å–å¾—(): Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(), ExposePaginationTestTable.id) - - assertThat(pagination.next).isEqualTo(100) - assertThat(pagination.prev).isEqualTo(81) - assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(100) - assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(81) - assertThat(pagination).size().isEqualTo(20) - } - - @Test - fun maxIdを指定ã—ã¦å–å¾—(): Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(maxId = 100), ExposePaginationTestTable.id) - - assertThat(pagination.next).isEqualTo(99) - assertThat(pagination.prev).isEqualTo(80) - assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(99) - assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(80) - assertThat(pagination).size().isEqualTo(20) - } - - @Test - fun sinceIdを指定ã—ã¦å–å¾—(): Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(sinceId = 15), ExposePaginationTestTable.id) - - assertThat(pagination.next).isEqualTo(100) - assertThat(pagination.prev).isEqualTo(81) - assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(100) - assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(81) - assertThat(pagination).size().isEqualTo(20) - } - - @Test - fun minIdを指定ã—ã¦å–å¾—():Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(minId = 45), ExposePaginationTestTable.id) - - assertThat(pagination.next).isEqualTo(65) - assertThat(pagination.prev).isEqualTo(46) - assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(65) - assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(46) - assertThat(pagination).size().isEqualTo(20) - } - - @Test - fun maxIdã¨sinceIdを指定ã—ã¦å–å¾—(): Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(maxId = 45, sinceId = 34), ExposePaginationTestTable.id) - - assertThat(pagination.next).isEqualTo(44) - assertThat(pagination.prev).isEqualTo(35) - assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(44) - assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(35) - assertThat(pagination).size().isEqualTo(10) - } - - @Test - fun maxIdã¨minIdを指定ã—ã¦å–å¾—():Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(maxId = 54, minId = 45), ExposePaginationTestTable.id) - - assertThat(pagination.next).isEqualTo(53) - assertThat(pagination.prev).isEqualTo(46) - assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(53) - assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(46) - assertThat(pagination).size().isEqualTo(8) - } - - @Test - fun limitを指定ã—ã¦å–å¾—():Unit = transaction { - val pagination: PaginationList = ExposePaginationTestTable.selectAll().withPagination(Page.of(limit = 30), ExposePaginationTestTable.id) - assertThat(pagination).size().isEqualTo(30) - } - - @Test - fun çµæžœãŒ0件ã®å ´åˆã¯prevã¨nextãŒnullã«ãªã‚‹():Unit = transaction { - val pagination = ExposePaginationTestTable.selectAll().where { ExposePaginationTestTable.id.isNull() } - .withPagination(Page.of(), ExposePaginationTestTable.id) - - assertThat(pagination).isEmpty() - assertThat(pagination.next).isNull() - assertThat(pagination.prev).isNull() - } - - object ExposePaginationTestTable : Table(){ - val id = long("id") - val name = varchar("name",100) - - override val primaryKey: PrimaryKey - get() = PrimaryKey(id) - } - - companion object { - private lateinit var database: Database - - @JvmStatic - @BeforeAll - fun beforeAll(): Unit { - database = Database.connect( - url = "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;", - driver = "org.h2.Driver", - user = "sa", - password = "" - ) - - transaction(database) { - SchemaUtils.create(ExposePaginationTestTable) - SchemaUtils.createMissingTablesAndColumns(ExposePaginationTestTable) - } - } - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt deleted file mode 100644 index a0c4bba7..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.exposed - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -class PageTest { - @Test - fun minIdãŒæŒ‡å®šã•ã‚Œã¦ã„ã‚‹ã¨sinceIdã¯ç„¡è¦–ã•ã‚Œã‚‹() { - val page = Page.of(1, 2, 3, 4) - - assertThat(page.maxId).isEqualTo(1) - assertThat(page.sinceId).isNull() - assertThat(page.minId).isEqualTo(3) - assertThat(page.limit).isEqualTo(4) - } - - @Test - fun minIdãŒnullã®ã¨ãã¯sinceIdãŒä½¿ã‚れる() { - val page = Page.of(1, 2, null, 4) - - assertThat(page.maxId).isEqualTo(1) - assertThat(page.minId).isNull() - assertThat(page.sinceId).isEqualTo(2) - assertThat(page.limit).isEqualTo(4) - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt deleted file mode 100644 index af4db171..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.exposed - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -class PaginationListKtTest { - @Test - fun `toHttpHeader nextã¨prevãŒnullã§ãªã„å ´åˆä¸¡æ–¹ä½œæˆã•ã‚Œã‚‹`() { - val paginationList = PaginationList(emptyList(), 1, 2) - - val httpHeader = - paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) - - assertThat(httpHeader).isEqualTo("; rel=\"next\", ; rel=\"prev\"") - } - - @Test - fun `toHttpHeader nextãŒnullãªã‚‰ç‰‡æ–¹ã ã‘作æˆã•ã‚Œã‚‹`() { - val paginationList = PaginationList(emptyList(), 1,null) - - val httpHeader = - paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) - - assertThat(httpHeader).isEqualTo("; rel=\"next\"") - } - - @Test - fun `toHttpHeader prevãŒnullãªã‚‰ç‰‡æ–¹ã ã‘作æˆã•ã‚Œã‚‹`() { - val paginationList = PaginationList(emptyList(), null,2) - - val httpHeader = - paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) - - assertThat(httpHeader).isEqualTo("; rel=\"prev\"") - } - - @Test - fun `toHttpHeader 両方nullãªã‚‰nullãŒè¿”ã£ã¦ãã‚‹`() { - val paginationList = PaginationList(emptyList(), null, null) - - - val httpHeader = - paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) - - assertThat(httpHeader).isNull() - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateServiceTest.kt deleted file mode 100644 index eb09683c..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateServiceTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.service.id - -// import kotlinx.coroutines.NonCancellable.message -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class TwitterSnowflakeIdGenerateServiceTest { - @Test - fun noDuplicateTest() = runBlocking { - val mutex = Mutex() - val mutableListOf = mutableListOf() - coroutineScope { - repeat(500000) { - launch(Dispatchers.IO) { - val id = TwitterSnowflakeIdGenerateService.generateId() - mutex.withLock { - mutableListOf.add(id) - } - } - } - } - - assertEquals(0, mutableListOf.size - mutableListOf.toSet().size) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt deleted file mode 100644 index 532f6403..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/application/service/init/MetaServiceImplTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) - -package dev.usbharu.hideout.application.service.init - -import dev.usbharu.hideout.core.domain.exception.NotInitException -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.mockito.kotlin.* -import utils.TestTransaction -import java.util.* -import kotlin.test.assertEquals - -class MetaServiceImplTest { - @Test - fun `getMeta メタデータをå–å¾—ã§ãã‚‹`() = runTest { - val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda")) - val metaRepository = mock { - onBlocking { get() } doReturn meta - } - val metaService = MetaServiceImpl(metaRepository, TestTransaction) - val actual = metaService.getMeta() - assertEquals(meta, actual) - } - - @Test - fun `getMeta メタデータãŒç„¡ã„ã¨ãã¯NotInitExceptionãŒthrowã•ã‚Œã‚‹`() = runTest { - val metaRepository = mock { - onBlocking { get() } doReturn null - } - val metaService = MetaServiceImpl(metaRepository, TestTransaction) - assertThrows { metaService.getMeta() } - } - - @Test - fun `updateMeta メタデータをä¿å­˜ã§ãã‚‹`() = runTest { - val meta = Meta("1.0.1", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda")) - val metaRepository = mock { - onBlocking { save(any()) } doReturn Unit - } - val metaServiceImpl = MetaServiceImpl(metaRepository, TestTransaction) - metaServiceImpl.updateMeta(meta) - argumentCaptor { - verify(metaRepository).save(capture()) - assertEquals(meta, firstValue) - } - } - - @Test - fun `getJwtMeta Jwtメタデータをå–å¾—ã§ãã‚‹`() = runTest { - val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda")) - val metaRepository = mock { - onBlocking { get() } doReturn meta - } - val metaService = MetaServiceImpl(metaRepository, TestTransaction) - val actual = metaService.getJwtMeta() - assertEquals(meta.jwt, actual) - } - - @Test - fun `getJwtMeta メタデータãŒç„¡ã„ã¨ãã¯NotInitExceptionãŒthrowã•ã‚Œã‚‹`() = runTest { - val metaRepository = mock { - onBlocking { get() } doReturn null - } - val metaService = MetaServiceImpl(metaRepository, TestTransaction) - assertThrows { metaService.getJwtMeta() } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorTest.kt deleted file mode 100644 index 573dd862..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorTest.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.usbharu.hideout.core.domain.model.actor - -import org.junit.jupiter.api.Test -import utils.UserBuilder - -class ActorTest { - @Test - fun validator() { - org.junit.jupiter.api.assertThrows { - UserBuilder.localUserOf(name = "ã†ã‚“ã“") - } - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt index 28faa6a3..220b4056 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt @@ -1,13 +1,14 @@ package dev.usbharu.hideout.core.domain.model.actor import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService +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 kotlinx.coroutines.runBlocking import java.net.URI import java.time.Instant -object TestActor2Factory : Actor.Actor2Factory() { +object TestActor2Factory { private val idGenerateService = TwitterSnowflakeIdGenerateService fun create( @@ -32,14 +33,18 @@ object TestActor2Factory : Actor.Actor2Factory() { postCount: Int = 0, lastPostDate: Instant? = null, suspend: Boolean = false, + alsoKnownAs: Set = emptySet(), + moveTo: ActorId? = null, + emojiIds: Set = emptySet(), + deleted: Boolean = false, ): Actor { return runBlocking { - super.internalCreate( + Actor( id = ActorId(id), name = ActorName(actorName), domain = Domain(domain), - screenName = TestActorScreenNameFactory.create(actorScreenName), - description = TestActorDescriptionFactory.create(description), + screenName = ActorScreenName(actorScreenName), + description = ActorDescription(description), inbox = inbox, outbox = outbox, url = uri, @@ -54,8 +59,12 @@ object TestActor2Factory : Actor.Actor2Factory() { followersCount = ActorRelationshipCount(followersCount), followingCount = ActorRelationshipCount(followingCount), postsCount = ActorPostsCount(postCount), - lastPostDate = lastPostDate, - suspend = suspend + lastPostAt = lastPostDate, + suspend = suspend, + alsoKnownAs = alsoKnownAs, + moveTo = moveTo, + emojiIds = emojiIds, + deleted = deleted, ) } } @@ -63,20 +72,4 @@ object TestActor2Factory : Actor.Actor2Factory() { private fun generateId(): Long = runBlocking { idGenerateService.generateId() } -} - -object TestActorScreenNameFactory : ActorScreenName.ActorScreenNameFactory() { - fun create(name: String): ActorScreenName { - return runBlocking { - super.create(name, emptyList()) - } - } -} - -object TestActorDescriptionFactory : ActorDescription.ActorDescriptionFactory() { - fun create(description: String): ActorDescription { - return runBlocking { - super.create(description, emptyList()) - } - } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderCheckerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderCheckerTest.kt deleted file mode 100644 index 4a150e10..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureHeaderCheckerTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.util.Base64Util -import org.intellij.lang.annotations.Language -import org.junit.jupiter.api.Assertions.assertDoesNotThrow -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import java.net.URI -import java.security.MessageDigest -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.util.* - -class HttpSignatureHeaderCheckerTest { - - val format = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) - - @Test - fun `checkDate 未æ¥ã¯ãƒ€ãƒ¡`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("http://example.com").toURL())) - - val s = ZonedDateTime.now().plusDays(1).format(format) - - assertThrows { - httpSignatureHeaderChecker.checkDate(s) - } - } - - - @Test - fun `checkDate éŽåŽ»ã¯OK`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("http://example.com").toURL())) - - val s = ZonedDateTime.now().minusHours(1).format(format) - - assertDoesNotThrow { - httpSignatureHeaderChecker.checkDate(s) - } - } - - @Test - fun `checkDate 86400秒以上昔ã¯ãƒ€ãƒ¡`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("http://example.com").toURL())) - - val s = ZonedDateTime.now().minusSeconds(86401).format(format) - - assertThrows { - httpSignatureHeaderChecker.checkDate(s) - } - } - - @Test - fun `checkHost 大文字å°æ–‡å­—ã®é•ã„ã¯ã‚»ãƒ¼ãƒ•`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("https://example.com").toURL())) - - assertDoesNotThrow { - httpSignatureHeaderChecker.checkHost("example.com") - httpSignatureHeaderChecker.checkHost("EXAMPLE.COM") - } - } - - @Test - fun `checkHost サブドメインã¯ãƒ€ãƒ¡`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("https://example.com").toURL())) - - assertThrows { - httpSignatureHeaderChecker.checkHost("follower.example.com") - } - } - - @Test - fun `checkDigest リクエストボディãŒåŒã˜ãªã‚‰ä½•ã‚‚ã—ãªã„`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("https://example.com").toURL())) - - - val sha256 = MessageDigest.getInstance("SHA-256") - - @Language("JSON") val requestBody = """{"@context":"","type":"hoge"}""" - - val digest = Base64Util.encode(sha256.digest(requestBody.toByteArray())) - - assertDoesNotThrow { - httpSignatureHeaderChecker.checkDigest(requestBody.toByteArray(), "SHA-256=" + digest) - } - } - - @Test - fun `checkDigest リクエストボディãŒã¡ã‚‡ã£ã¨ã§ã‚‚é•ã†ã¨ãƒ€ãƒ¡`() { - val httpSignatureHeaderChecker = - HttpSignatureHeaderChecker(ApplicationConfig(URI.create("https://example.com").toURL())) - - - val sha256 = MessageDigest.getInstance("SHA-256") - - @Language("JSON") val requestBody = """{"type":"hoge","@context":""}""" - @Language("JSON") val requestBody2 = """{"@context":"","type":"hoge"}""" - val digest = Base64Util.encode(sha256.digest(requestBody.toByteArray())) - - assertThrows { - httpSignatureHeaderChecker.checkDigest(requestBody2.toByteArray(), digest) - } - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImplTest.kt deleted file mode 100644 index b6ae6af4..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteProcessServiceImplTest.kt +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.FilterAction -import dev.usbharu.hideout.core.domain.model.filter.FilterMode -import dev.usbharu.hideout.core.domain.model.filter.FilterType -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import utils.PostBuilder - -class MuteProcessServiceImplTest { - @Test - fun å˜ç´”ãªæ–‡å­—列ã«ãƒžãƒƒãƒã™ã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "mute", - FilterMode.NONE - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "mute")) - } - - @Test - fun 複数ã®æ–‡å­—列ã§ãƒžãƒƒãƒã™ã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "mate", - FilterMode.NONE - ), - FilterKeyword( - 1, - 1, - "mata", - FilterMode.NONE - ), - FilterKeyword( - 1, - 1, - "mute", - FilterMode.NONE - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "mute")) - } - - @Test - fun å˜èªžã«ãƒžãƒƒãƒã™ã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "mute", - FilterMode.WHOLE_WORD - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "mute")) - } - - @Test - fun å˜èªžä»¥å¤–ã«ã¯ãƒžãƒƒãƒã—ãªã„() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mutetest") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "mute", - FilterMode.WHOLE_WORD - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isNull() - } - - @Test - fun 複数ã®å˜èªžã«ãƒžãƒƒãƒã™ã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "mate", - FilterMode.WHOLE_WORD - ), - FilterKeyword( - 1, - 1, - "mata", - FilterMode.WHOLE_WORD - ), - FilterKeyword( - 1, - 1, - "mute", - FilterMode.WHOLE_WORD - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "mute")) - } - - @Test - fun æ­£è¦è¡¨ç¾ã‚‚使ãˆã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "e\\st", - FilterMode.REGEX - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "e t")) - } - - @Test - fun cw文字ã«ãƒžãƒƒãƒã™ã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(overview = "mute test", text = "hello") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "e\\st", - FilterMode.REGEX - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "e t")) - } - - @Test - fun 文字列ã¨å˜èªžã¨æ­£è¦è¡¨ç¾ã‚’åŒæ™‚ã«ä½¿ãˆã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "e\\st", - FilterMode.REGEX - ), - FilterKeyword( - 2, - 1, - "mute", - FilterMode.NONE - ), - FilterKeyword( - 3, - 1, - "test", - FilterMode.WHOLE_WORD - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isEqualTo(FilterResult(filterQueryModel, "mute")) - } - - @Test - fun 複数ã®æŠ•ç¨¿ã‚’処ç†ã§ãã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "mute", - FilterMode.NONE - ) - ) - ) - val posts = listOf( - PostBuilder.of(text = "mute"), PostBuilder.of(text = "mutes"), PostBuilder.of(text = "hoge") - ) - val actual = muteProcessServiceImpl.processMutes( - posts, - FilterType.entries.toList(), - listOf( - filterQueryModel - ) - ) - - assertThat(actual) - .hasSize(2) - .containsEntry(posts[0], FilterResult(filterQueryModel, "mute")) - .containsEntry(posts[1], FilterResult(filterQueryModel, "mute")) - - } - - @Test - fun 何もマッãƒã—ãªã„ã¨nullãŒè¿”ã£ã¦ãã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "fuga", - FilterMode.NONE - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isNull() - } - - @Test - fun Cwã§ä½•ã‚‚マッãƒã—ãªã„ã¨æœ¬æ–‡ã‚’確èªã™ã‚‹() = runTest { - val muteProcessServiceImpl = MuteProcessServiceImpl() - - val post = PostBuilder.of(overview = "hage", text = "mute test") - - val filterQueryModel = FilterQueryModel( - 1, - 2, - "mute test", - FilterType.entries, - FilterAction.warn, - listOf( - FilterKeyword( - 1, - 1, - "fuga", - FilterMode.NONE - ) - ) - ) - val actual = muteProcessServiceImpl.processMute( - post, FilterType.entries.toList(), listOf( - filterQueryModel - ) - ) - - assertThat(actual).isNull() - } - -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImplTest.kt deleted file mode 100644 index f37310a4..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/filter/MuteServiceImplTest.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.filter - -import dev.usbharu.hideout.core.domain.model.filter.* -import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository -import dev.usbharu.hideout.core.query.model.FilterQueryModel -import dev.usbharu.hideout.core.query.model.FilterQueryService -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* - -@ExtendWith(MockitoExtension::class) -class MuteServiceImplTest { - - @Mock - private lateinit var filterRepository: FilterRepository - - @Mock - private lateinit var filterKeywordRepository: FilterKeywordRepository - - @Mock - private lateinit var filterQueryService: FilterQueryService - - @InjectMocks - private lateinit var muteServiceImpl: MuteServiceImpl - - @Test - fun createFilter() = runTest { - whenever(filterRepository.generateId()).doReturn(1) - whenever(filterKeywordRepository.generateId()).doReturn(1) - - whenever(filterRepository.save(any())).doAnswer { it.arguments[0]!! as Filter } - - val createFilter = muteServiceImpl.createFilter( - title = "hoge", - context = listOf(FilterType.home, FilterType.public), - action = FilterAction.warn, - keywords = listOf( - FilterKeyword( - "fuga", - FilterMode.NONE - ) - ), - loginUser = 1 - ) - - assertThat(createFilter).isEqualTo( - FilterQueryModel( - 1, - 1, - "hoge", - listOf(FilterType.home, FilterType.public), - FilterAction.warn, - keywords = listOf( - dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword(1, 1, "fuga", FilterMode.NONE) - ) - ) - ) - } - - @Test - fun getFilters() = runTest { - whenever(filterQueryService.findByUserIdAndType(any(), any())).doReturn( - listOf( - FilterQueryModel( - 1, - 1, - "hoge", - listOf(FilterType.home), - FilterAction.warn, - listOf( - dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword( - 1, - 1, - "fuga", - FilterMode.NONE - ) - ) - ) - ) - ) - - muteServiceImpl.getFilters(1, listOf(FilterType.home)) - } - - @Test - fun `getFilters 何も指定ã—ãªã„`() = runTest { - whenever(filterQueryService.findByUserIdAndType(any(), eq(emptyList()))).doReturn(emptyList()) - - muteServiceImpl.getFilters(1) - } -} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt deleted file mode 100644 index ee3aee68..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/ApatcheTikaFileTypeDeterminationServiceTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import kotlin.io.path.toPath - -class ApatcheTikaFileTypeDeterminationServiceTest { - @Test - fun png() { - val apatcheTikaFileTypeDeterminationService = ApatcheTikaFileTypeDeterminationService() - - val mimeType = apatcheTikaFileTypeDeterminationService.fileType( - String.javaClass.classLoader.getResource("400x400.png").toURI().toPath(), "400x400.png" - ) - - assertThat(mimeType.type).isEqualTo("image") - assertThat(mimeType.subtype).isEqualTo("png") - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt deleted file mode 100644 index 580c64c4..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/LocalFileSystemMediaDataStoreTest.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.LocalStorageConfig -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import java.net.URL -import java.nio.file.Path -import java.util.* -import kotlin.io.path.readBytes -import kotlin.io.path.toPath - -class LocalFileSystemMediaDataStoreTest { - - private val path = String.javaClass.classLoader.getResource("400x400.png")?.toURI()?.toPath()!! - - @Test - fun `save inputStreamを使用ã—ã¦æ­£å¸¸ã«ä¿å­˜ã§ãã‚‹`() = runTest { - val applicationConfig = ApplicationConfig(URL("https://example.com")) - val storageConfig = LocalStorageConfig("files", null) - - val localFileSystemMediaDataStore = LocalFileSystemMediaDataStore(applicationConfig, storageConfig) - - val fileInputStream = path.readBytes() - - assertThat(fileInputStream.size).isNotEqualTo(0) - - val mediaSave = MediaSave( - "test-media-1${UUID.randomUUID()}.png", - "", - fileInputStream, - fileInputStream - ) - - val save = localFileSystemMediaDataStore.save(mediaSave) - - assertThat(save).isInstanceOf(SuccessSavedMedia::class.java) - - save as SuccessSavedMedia - - assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) - .exists() - .hasSize(fileInputStream.size.toLong()) - assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) - .exists() - .hasSize(fileInputStream.size.toLong()) - } - - @Test - fun 一時ファイルを使用ã—ã¦æ­£å¸¸ã«ä¿å­˜ã§ãã‚‹() = runTest { - val applicationConfig = ApplicationConfig(URL("https://example.com")) - val storageConfig = LocalStorageConfig("files", null) - - val localFileSystemMediaDataStore = LocalFileSystemMediaDataStore(applicationConfig, storageConfig) - - val fileInputStream = path.readBytes() - - assertThat(fileInputStream.size).isNotEqualTo(0) - - val saveRequest = MediaSaveRequest( - "test-media-2${UUID.randomUUID()}.png", - "", - path, - path - ) - - val save = localFileSystemMediaDataStore.save(saveRequest) - - assertThat(save).isInstanceOf(SuccessSavedMedia::class.java) - - save as SuccessSavedMedia - - assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) - .exists() - .hasSize(fileInputStream.size.toLong()) - assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) - .exists() - .hasSize(fileInputStream.size.toLong()) - } - - @Test - fun idを使用ã—ã¦å‰Šé™¤ã§ãã‚‹() = runTest { - val applicationConfig = ApplicationConfig(URL("https://example.com")) - val storageConfig = LocalStorageConfig("files", null) - - val localFileSystemMediaDataStore = LocalFileSystemMediaDataStore(applicationConfig, storageConfig) - - val fileInputStream = path.readBytes() - - assertThat(fileInputStream.size).isNotEqualTo(0) - - val saveRequest = MediaSaveRequest( - "test-media-2${UUID.randomUUID()}.png", - "", - path, - path - ) - - val save = localFileSystemMediaDataStore.save(saveRequest) - - assertThat(save).isInstanceOf(SuccessSavedMedia::class.java) - - save as SuccessSavedMedia - - assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) - .exists() - .hasSize(fileInputStream.size.toLong()) - assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) - .exists() - .hasSize(fileInputStream.size.toLong()) - - - localFileSystemMediaDataStore.delete(save.name) - - assertThat(Path.of("files").toAbsolutePath().resolve(save.name)) - .doesNotExist() - assertThat(Path.of("files").toAbsolutePath().resolve("thumbnail-" + save.name)) - .doesNotExist() - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt deleted file mode 100644 index affa4b36..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImplTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.media - - -import org.junit.jupiter.api.Test - -class MediaServiceImplTest { - @Test - fun pngç”»åƒã‚’アップロードã§ãã‚‹() { - - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowNotificationRequestTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowNotificationRequestTest.kt deleted file mode 100644 index dcbcceb9..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowNotificationRequestTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import java.time.Instant - -class FollowNotificationRequestTest { - - @Test - fun buildNotification() { - val createdAt = Instant.now() - val actual = FollowNotificationRequest(1, 2).buildNotification(1, createdAt) - - Assertions.assertThat(actual).isEqualTo( - Notification( - id = 1, - type = "follow", - userId = 1, - sourceActorId = 2, - postId = null, - text = null, - reactionId = null, - createdAt = createdAt - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowRequestNotificationRequestTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowRequestNotificationRequestTest.kt deleted file mode 100644 index 0e577ffa..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/FollowRequestNotificationRequestTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import java.time.Instant - -class FollowRequestNotificationRequestTest { - - @Test - fun buildNotification() { - val createdAt = Instant.now() - val actual = FollowRequestNotificationRequest(1, 2).buildNotification(1, createdAt) - - Assertions.assertThat(actual).isEqualTo( - Notification( - id = 1, - type = "follow-request", - userId = 1, - sourceActorId = 2, - postId = null, - text = null, - reactionId = null, - createdAt = createdAt - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/MentionNotificationRequestTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/MentionNotificationRequestTest.kt deleted file mode 100644 index 5bc72946..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/MentionNotificationRequestTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import java.time.Instant - -class MentionNotificationRequestTest { - - @Test - fun buildNotification() { - val createdAt = Instant.now() - val actual = MentionNotificationRequest(1, 2, 3).buildNotification(1, createdAt) - - assertThat(actual).isEqualTo( - Notification( - id = 1, - type = "mention", - userId = 1, - sourceActorId = 2, - postId = 3, - text = null, - reactionId = null, - createdAt = createdAt - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt deleted file mode 100644 index c1daea13..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/NotificationServiceImplTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.notification.Notification -import dev.usbharu.hideout.core.domain.model.notification.NotificationRepository -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.UserBuilder -import java.net.URL - -@ExtendWith(MockitoExtension::class) -class NotificationServiceImplTest { - - - @Mock - private lateinit var relationshipNotificationManagementService: RelationshipNotificationManagementService - - @Mock - private lateinit var relationshipRepository: RelationshipRepository - - @Spy - private val notificationStoreList: MutableList = mutableListOf() - - @Mock - private lateinit var notificationRepository: NotificationRepository - - @Mock - private lateinit var actorRepository: ActorRepository - - @Mock - private lateinit var postRepository: PostRepository - - @Mock - private lateinit var reactionRepository: ReactionRepository - - @Spy - private val applicationConfig = ApplicationConfig(URL("https://example.com")) - - @InjectMocks - private lateinit var notificationServiceImpl: NotificationServiceImpl - - @Test - fun `publishNotifi ローカルユーザーã¸ã®é€šçŸ¥ã‚’発行ã™ã‚‹`() = runTest { - - val actor = UserBuilder.localUserOf(domain = "example.com") - - whenever(actorRepository.findById(eq(1))).doReturn(actor) - - val id = TwitterSnowflakeIdGenerateService.generateId() - - whenever(notificationRepository.generateId()).doReturn(id) - - whenever(notificationRepository.save(any())).doAnswer { it.arguments[0] as Notification } - - - val actual = notificationServiceImpl.publishNotify(PostNotificationRequest(1, 2, 3)) - - assertThat(actual).isNotNull() - - verify(notificationRepository, times(1)).save(any()) - } - - @Test - fun `publishNotify ユーザーãŒå­˜åœ¨ã—ãªã„ã¨ãã¯ç™ºè¡Œã—ãªã„`() = runTest { - val actual = notificationServiceImpl.publishNotify(PostNotificationRequest(1, 2, 3)) - - assertThat(actual).isNull() - } - - @Test - fun `publishNotify ユーザーãŒãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã®å ´åˆã¯ç™ºè¡Œã—ãªã„`() = runTest { - val actor = UserBuilder.remoteUserOf(domain = "remote.example.com") - - whenever(actorRepository.findById(eq(1))).doReturn(actor) - - val actual = notificationServiceImpl.publishNotify(PostNotificationRequest(1, 2, 3)) - - assertThat(actual).isNull() - } - - @Test - fun unpublishNotify() = runTest { - notificationServiceImpl.unpublishNotify(1) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/PostNotificationRequestTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/PostNotificationRequestTest.kt deleted file mode 100644 index bc5bee91..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/PostNotificationRequestTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import java.time.Instant - -class PostNotificationRequestTest { - - @Test - fun buildNotification() { - val createdAt = Instant.now() - val actual = PostNotificationRequest(1, 2, 3).buildNotification(1, createdAt) - - Assertions.assertThat(actual).isEqualTo( - Notification( - id = 1, - type = "post", - userId = 1, - sourceActorId = 2, - postId = 3, - text = null, - reactionId = null, - createdAt = createdAt - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/ReactionNotificationRequestTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/ReactionNotificationRequestTest.kt deleted file mode 100644 index 87483dcd..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/ReactionNotificationRequestTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import java.time.Instant - -class ReactionNotificationRequestTest { - - @Test - fun buildNotification() { - val createdAt = Instant.now() - val actual = ReactionNotificationRequest(1, 2, 3, 4).buildNotification(1, createdAt) - - Assertions.assertThat(actual).isEqualTo( - Notification( - id = 1, - type = "reaction", - userId = 1, - sourceActorId = 2, - postId = 3, - text = null, - reactionId = 4, - createdAt = createdAt - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt deleted file mode 100644 index 30508ea1..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RelationshipNotificationManagementServiceImplTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship -import org.junit.jupiter.api.Test -import kotlin.test.assertTrue - -class RelationshipNotificationManagementServiceImplTest { - @Test - fun `sendNotification ミューã¨ã—ã¦ã„ãªã„å ´åˆé€ä¿¡ã™ã‚‹`() { - val notification = RelationshipNotificationManagementServiceImpl().sendNotification( - Relationship( - 1, - 2, - false, - false, - false, - false, - false - ), PostNotificationRequest(1, 2, 3) - ) - - assertTrue(notification) - - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RepostNotificationRequestTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RepostNotificationRequestTest.kt deleted file mode 100644 index b50f9551..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/notification/RepostNotificationRequestTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.notification - -import dev.usbharu.hideout.core.domain.model.notification.Notification -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import java.time.Instant - -class RepostNotificationRequestTest { - - @Test - fun buildNotification() { - val createdAt = Instant.now() - val actual = RepostNotificationRequest(1, 2, 3).buildNotification(1, createdAt) - - Assertions.assertThat(actual).isEqualTo( - Notification( - id = 1, - type = "repost", - userId = 1, - sourceActorId = 2, - postId = 3, - text = null, - reactionId = null, - createdAt = createdAt - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatterTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatterTest.kt deleted file mode 100644 index 8725dd63..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/DefaultPostContentFormatterTest.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.application.config.HtmlSanitizeConfig -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -class DefaultPostContentFormatterTest { - val defaultPostContentFormatter = DefaultPostContentFormatter(HtmlSanitizeConfig().policy()) - - @Test - fun pã‚¿ã‚°ãŒpã‚¿ã‚°ã«ãªã‚‹() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun hã‚¿ã‚°ãŒpã‚¿ã‚°ã«ãªã‚‹() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun pã‚¿ã‚°ã®ãƒã‚¹ãƒˆã¯ç ´æ£„ã•ã‚Œã‚‹() { - //language=HTML - val html = """

hoge

fuga

piyo

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

fuga

piyo

", "hoge\n\nfuga\n\npiyo")) - } - - @Test - fun spanã‚¿ã‚°ã¯ç„¡è¦–ã•ã‚Œã‚‹() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun `2連続改行ã¯æ®µè½ã«å¤‰æ›ã•ã‚Œã‚‹`() { - //language=HTML - val html = """

hoge

fuga

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

fuga

", "hoge\n\nfuga")) - } - - @Test - fun iã‚¿ã‚°ã¯ç„¡è¦–ã•ã‚Œã‚‹() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun aã‚¿ã‚°ã¯hrefã®ä¸­èº«ã®ã¿å¼•ã継ãŒã‚Œã‚‹() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun aã‚¿ã‚°ã®ä¸­ã®spanã¯ç„¡è¦–ã•ã‚Œã‚‹() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun brã‚¿ã‚°ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯æ”¹è¡Œã«ãªã‚‹() { - //language=HTML - val html = """

hoge
fuga

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge
fuga

", "hoge\nfuga")) - } - - @Test - fun ã„ããªã‚Šãƒ†ã‚­ã‚¹ãƒˆãŒæ¥ãŸã‚‰pã‚¿ã‚°ã§å›²ã‚€() { - //language=HTML - val html = """hoge""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun bodyã‚¿ã‚°ãŒå«ã¾ã‚Œã¦ã„ãŸå ´åˆæ¶ˆã™() { - //language=HTML - val html = """

hoge

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo(FormattedPostContent("

hoge

", "hoge")) - } - - @Test - fun pã‚¿ã‚°ã®ä¸­ã®spanã¯ç„¡è¦–ã•ã‚Œã‚‹() { - //language=HTML - val html = - """

@testuser14 tes

""" - - val actual = defaultPostContentFormatter.format(html) - - assertThat(actual).isEqualTo( - FormattedPostContent( - "

@testuser14 tes

", - "@testuser14 tes" - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt deleted file mode 100644 index 8d7c1676..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/post/PostServiceImplTest.kt +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.post - -import dev.usbharu.hideout.activitypub.service.activity.create.ApSendCreateService -import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.application.config.HtmlSanitizeConfig -import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import jakarta.validation.Validation -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.mockStatic -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.PostBuilder -import utils.UserBuilder -import java.time.Instant - -@ExtendWith(MockitoExtension::class) -class PostServiceImplTest { - - @Mock - private lateinit var postRepository: PostRepository - - @Mock - private lateinit var actorRepository: ActorRepository - - @Mock - private lateinit var timelineService: TimelineService - @Spy - private var postBuilder: Post.PostBuilder = Post.PostBuilder( - CharacterLimit(), DefaultPostContentFormatter( - HtmlSanitizeConfig().policy() - ), Validation.buildDefaultValidatorFactory().validator - ) - - @Mock - private lateinit var apSendCreateService: ApSendCreateService - - @Mock - private lateinit var reactionRepository: ReactionRepository - - @Mock - private lateinit var apSendDeleteService: APSendDeleteService - - @InjectMocks - private lateinit var postServiceImpl: PostServiceImpl - - @Test - fun `createLocal 正常ã«postを作æˆã§ãã‚‹`() = runTest { - - val now = Instant.now() - val post = PostBuilder.of(createdAt = now.toEpochMilli()) - - whenever(postRepository.save(eq(post))).doReturn(post) - whenever(postRepository.generateId()).doReturn(post.id) - whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.localUserOf(id = post.actorId)) - whenever(timelineService.publishTimeline(eq(post), eq(true))).doReturn(Unit) - - mockStatic(Instant::class.java, Mockito.CALLS_REAL_METHODS).use { - - it.`when`(Instant::now).doReturn(now) - val createLocal = postServiceImpl.createLocal( - PostCreateDto( - post.text, - post.overview, - post.visibility, - post.repostId, - post.replyId, - post.actorId, - post.mediaIds - ) - ) - - assertThat(createLocal).isEqualTo(post) - } - - verify(postRepository, times(1)).save(eq(post)) - verify(timelineService, times(1)).publishTimeline(eq(post), eq(true)) - verify(apSendCreateService, times(1)).createNote(eq(post)) - } - - @Test - fun `createRemote 正常ã«ãƒªãƒ¢ãƒ¼ãƒˆã®postを作æˆã§ãã‚‹`() = runTest { - val post = PostBuilder.of() - - whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doReturn(post) - whenever(timelineService.publishTimeline(eq(post), eq(false))).doReturn(Unit) - - - val createLocal = postServiceImpl.createRemote(post) - - assertThat(createLocal).isEqualTo(post) - - - verify(postRepository, times(1)).save(eq(post)) - verify(timelineService, times(1)).publishTimeline(eq(post), eq(false)) - } - - @Test - fun `createRemote æ—¢ã«ä½œæˆã•ã‚Œã¦ã„ãŸå ´åˆã¯ãã®ã¾ã¾å¸°ã™`() = runTest { - val post = PostBuilder.of() - - whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doAnswer { throw DuplicateException() } - whenever(postRepository.findByApId(eq(post.apId))).doReturn(post) - - val createLocal = postServiceImpl.createRemote(post) - - assertThat(createLocal).isEqualTo(post) - - verify(postRepository, times(1)).save(eq(post)) - verify(timelineService, times(0)).publishTimeline(any(), any()) - } - - @Test - fun `createRemote æ—¢ã«ä½œæˆã•ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’検知出æ¥ãšã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«push出æ¥ãªã‹ã£ãŸå ´åˆä½•ã‚‚ã—ãªã„`() = runTest { - val post = PostBuilder.of() - - whenever(actorRepository.findById(eq(post.actorId))).doReturn(UserBuilder.remoteUserOf(id = post.actorId)) - whenever(postRepository.save(eq(post))).doReturn(post) - whenever(timelineService.publishTimeline(eq(post), eq(false))).doThrow(DuplicateException::class) - whenever(postRepository.findByApId(eq(post.apId))).doReturn(post) - - val createLocal = postServiceImpl.createRemote(post) - - assertThat(createLocal).isEqualTo(post) - - verify(postRepository, times(1)).save(eq(post)) - verify(timelineService, times(1)).publishTimeline(eq(post), eq(false)) - } - - @Test - fun `deleteLocal DeleteãŒé…é€ã•ã‚Œã‚‹`() = runTest { - val post = PostBuilder.of() - - val localUserOf = UserBuilder.localUserOf() - - whenever(actorRepository.findById(eq(post.actorId))).doReturn(localUserOf) - - postServiceImpl.deleteLocal(post) - - verify(reactionRepository, times(1)).deleteByPostId(eq(post.id)) - verify(postRepository, times(1)).save(eq(post.delete())) - verify(apSendDeleteService, times(1)).sendDeleteNote(eq(post)) - verify(actorRepository, times(1)).save(eq(localUserOf.decrementPostsCount())) - } - - @Test - fun `deleteLocal 削除済ã¿æŠ•ç¨¿ã¯ä½•ã‚‚ã—ãªã„`() = runTest { - val delete = PostBuilder.of().delete() - - postServiceImpl.deleteLocal(delete) - - verify(reactionRepository, never()).deleteByPostId(any()) - verify(postRepository, never()).save(any()) - verify(apSendDeleteService, never()).sendDeleteNote(any()) - verify(actorRepository, never()).save(any()) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt deleted file mode 100644 index c02bfa10..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/reaction/ReactionServiceImplTest.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.reaction - - -import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.domain.model.reaction.Reaction -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.service.notification.NotificationService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.PostBuilder - -@ExtendWith(MockitoExtension::class) -class ReactionServiceImplTest { - - @Mock - private lateinit var notificationService: NotificationService - - @Mock - private lateinit var postRepository: PostRepository - - @Mock - private lateinit var reactionRepository: ReactionRepository - - @Mock - private lateinit var apReactionService: APReactionService - - @InjectMocks - private lateinit var reactionServiceImpl: ReactionServiceImpl - - @Test - fun `receiveReaction リアクションãŒå­˜åœ¨ã—ãªã„ã¨ãä¿å­˜ã™ã‚‹`() = runTest { - - val post = PostBuilder.of() - - whenever(reactionRepository.existByPostIdAndActor(eq(post.id), eq(post.actorId))).doReturn( - false - ) - whenever(postRepository.findById(eq(post.id))).doReturn(post) - whenever(reactionRepository.save(any())).doAnswer { it.arguments[0] as Reaction } - - val generateId = TwitterSnowflakeIdGenerateService.generateId() - whenever(reactionRepository.generateId()).doReturn(generateId) - - reactionServiceImpl.receiveReaction(UnicodeEmoji("â¤"), post.actorId, post.id) - - verify(reactionRepository, times(1)).save(eq(Reaction(generateId, UnicodeEmoji("â¤"), post.id, post.actorId))) - } - - - @Test - fun `receiveReaction リアクションãŒæ—¢ã«ä½œæˆã•ã‚Œã¦ã„ã‚‹å ´åˆå‰Šé™¤ã—ã¦æ–°ã—ã作æˆ`() = runTest { - val post = PostBuilder.of() - whenever(reactionRepository.existByPostIdAndActor(eq(post.id), eq(post.actorId))).doReturn( - true - ) - whenever(postRepository.findById(eq(post.id))).doReturn(post) - whenever(reactionRepository.save(any())).doAnswer { it.arguments[0] as Reaction } - val generateId = TwitterSnowflakeIdGenerateService.generateId() - - whenever(reactionRepository.generateId()).doReturn(generateId) - - reactionServiceImpl.receiveReaction(UnicodeEmoji("â¤"), post.actorId, post.id) - - verify(reactionRepository, times(1)).deleteByPostIdAndActorId(post.id, post.actorId) - verify(reactionRepository, times(1)).save(Reaction(generateId, UnicodeEmoji("â¤"), post.id, post.actorId)) - } - - @Test - fun `sendReaction リアクションãŒå­˜åœ¨ã—ãªã„ã¨ãä¿å­˜ã—ã¦é…é€ã™ã‚‹`() = runTest { - val post = PostBuilder.of() - whenever(reactionRepository.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( - null - ) - whenever(postRepository.findById(eq(post.id))).doReturn(post) - whenever(reactionRepository.save(any())).doAnswer { it.arguments[0] as Reaction } - val generateId = TwitterSnowflakeIdGenerateService.generateId() - whenever(reactionRepository.generateId()).doReturn(generateId) - - reactionServiceImpl.sendReaction(UnicodeEmoji("â¤"), post.actorId, post.id) - - verify(reactionRepository, times(1)).save(eq(Reaction(generateId, UnicodeEmoji("â¤"), post.id, post.actorId))) - verify(apReactionService, times(1)).reaction(eq(Reaction(generateId, UnicodeEmoji("â¤"), post.id, post.actorId))) - } - - @Test - fun `sendReaction リアクションãŒå­˜åœ¨ã™ã‚‹ã¨ãã¯å‰Šé™¤ã—ã¦ä¿å­˜ã—ã¦é…é€ã™ã‚‹`() = runTest { - val post = PostBuilder.of() - val id = TwitterSnowflakeIdGenerateService.generateId() - whenever(reactionRepository.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( - Reaction(id, UnicodeEmoji("â¤"), post.id, post.actorId) - ) - whenever(postRepository.findById(eq(post.id))).doReturn(post) - whenever(reactionRepository.save(any())).doAnswer { it.arguments[0] as Reaction } - val generateId = TwitterSnowflakeIdGenerateService.generateId() - whenever(reactionRepository.generateId()).doReturn(generateId) - - reactionServiceImpl.sendReaction(UnicodeEmoji("â¤"), post.actorId, post.id) - - - verify(reactionRepository, times(1)).delete(eq(Reaction(id, UnicodeEmoji("â¤"), post.id, post.actorId))) - verify(reactionRepository, times(1)).save(eq(Reaction(generateId, UnicodeEmoji("â¤"), post.id, post.actorId))) - verify(apReactionService, times(1)).removeReaction(eq(Reaction(id, UnicodeEmoji("â¤"), post.id, post.actorId))) - verify(apReactionService, times(1)).reaction(eq(Reaction(generateId, UnicodeEmoji("â¤"), post.id, post.actorId))) - } - - @Test - fun `removeReaction リアクションãŒå­˜åœ¨ã™ã‚‹å ´åˆå‰Šé™¤ã—ã¦é…é€`() = runTest { - val post = PostBuilder.of() - whenever(reactionRepository.findByPostIdAndActorIdAndEmojiId(eq(post.id), eq(post.actorId), eq(0))).doReturn( - Reaction(0, UnicodeEmoji("â¤"), post.id, post.actorId) - ) - - reactionServiceImpl.removeReaction(post.actorId, post.id) - - verify(reactionRepository, times(1)).delete(eq(Reaction(0, UnicodeEmoji("â¤"), post.id, post.actorId))) - verify(apReactionService, times(1)).removeReaction(eq(Reaction(0, UnicodeEmoji("â¤"), post.id, post.actorId))) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt deleted file mode 100644 index acff5e20..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/relationship/RelationshipServiceImplTest.kt +++ /dev/null @@ -1,795 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.relationship - -import dev.usbharu.hideout.activitypub.service.activity.accept.ApSendAcceptService -import dev.usbharu.hideout.activitypub.service.activity.follow.APSendFollowService -import dev.usbharu.hideout.activitypub.service.activity.reject.ApSendRejectService -import dev.usbharu.hideout.activitypub.service.activity.undo.APSendUndoService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.service.notification.NotificationService -import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.UserBuilder -import java.net.URL - -@ExtendWith(MockitoExtension::class) -class RelationshipServiceImplTest { - - - @Mock - private lateinit var notificationService: NotificationService - - @Spy - private val applicationConfig = ApplicationConfig(URL("https://example.com")) - - @Mock - private lateinit var relationshipRepository: RelationshipRepository - - @Mock - private lateinit var apSendFollowService: APSendFollowService - - @Mock - private lateinit var apSendAcceptService: ApSendAcceptService - - @Mock - private lateinit var apSendRejectService: ApSendRejectService - - @Mock - private lateinit var apSendUndoService: APSendUndoService - - @Mock - private lateinit var actorRepository: ActorRepository - - @InjectMocks - private lateinit var relationshipServiceImpl: RelationshipServiceImpl - - @Test - fun `followRequest ローカルã®å ´åˆfollowRequestフラグãŒtrueã§æ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - - relationshipServiceImpl.followRequest(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `followRequest リモートã®å ´åˆFollowアクティビティãŒé…é€ã•ã‚Œã‚‹`() = runTest { - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorRepository.findById(eq(1234))).doReturn(localUser) - val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) - - relationshipServiceImpl.followRequest(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendFollowService, times(1)).sendFollow(eq(SendFollowDto(localUser, remoteUser))) - } - - @Test - fun `followRequest ブロックã•ã‚Œã¦ã„ã‚‹å ´åˆãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆå‡ºæ¥ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn(null) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.followRequest(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `followRequest ブロックã—ã¦ã„ã‚‹å ´åˆãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆå‡ºæ¥ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.followRequest(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `followRequest æ—¢ã«ãƒ•ã‚©ãƒ­ãƒ¼ã—ã¦ã„ã‚‹å ´åˆã¯å¿µã®ç‚ºãƒ•ã‚©ãƒ­ãƒ¼æ‰¿èªã‚’自動ã§è¡Œã†`() = runTest { - val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorRepository.findById(eq(1234))).doReturn(remoteUser) - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorRepository.findById(eq(5678))).doReturn(localUser) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.followRequest(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendAcceptService, times(1)).sendAcceptFollow(eq(localUser), eq(remoteUser)) - verify(apSendFollowService, never()).sendFollow(any()) - } - - @Test - fun `followRequest フォローリクエスト無視ã®å ´åˆã¯ç„¡è¦–ã™ã‚‹`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = true - ) - ) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.followRequest(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `block ローカルユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - - relationshipServiceImpl.block(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `block リモートユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã¦é…é€ã•ã‚Œã‚‹`() = runTest { - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorRepository.findById(eq(1234))).doReturn(localUser) - val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) - - relationshipServiceImpl.block(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `acceptFollowRequest ローカルユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - - verify(relationshipRepository, times(1)).save( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - verify(apSendAcceptService, never()).sendAcceptFollow(any(), any()) - } - - @Test - fun `acceptFollowRequest リモートユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã¦é…é€ã•ã‚Œã‚‹`() = runTest { - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorRepository.findById(eq(1234))).doReturn(localUser) - val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendAcceptService, times(1)).sendAcceptFollow(eq(localUser), eq(remoteUser)) - } - - @Test - fun `acceptFollowRequest RelationshipãŒå­˜åœ¨ã—ãªã„ã¨ãã¯ä½•ã‚‚ã—ãªã„`() = runTest { - relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - - verify(apSendAcceptService, never()).sendAcceptFollow(any(), any()) - } - - @Test - fun `acceptFollowRequest フォローリクエストãŒå­˜åœ¨ã›ãšforceãŒfalseã®ã¨ã何もã—ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - 5678, 1234, false, false, false, false, false - ) - ) - - relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - - verify(apSendAcceptService, never()).sendAcceptFollow(any(), any()) - } - - @Test - fun `acceptFollowRequest フォローリクエストãŒå­˜åœ¨ã›ãšforceãŒtrueã®ã¨ãフォローを承èªã™ã‚‹`() = runTest { - whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - 5678, 1234, false, false, false, false, false - ) - ) - - relationshipServiceImpl.acceptFollowRequest(1234, 5678, true) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `acceptFollowRequest ブロックã—ã¦ã„ã‚‹å ´åˆã¯ä½•ã‚‚ã—ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - 5678, 1234, false, true, false, true, false - ) - ) - - assertThrows { - relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - } - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `acceptFollowRequest ブロックã•ã‚Œã¦ã„ã‚‹å ´åˆã¯ä½•ã‚‚ã—ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - 1234, 5678, false, false, false, true, false - ) - ) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - 5678, 1234, false, true, false, true, false - ) - ) - - assertThrows { - relationshipServiceImpl.acceptFollowRequest(1234, 5678, false) - } - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `rejectFollowRequest ローカルユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.rejectFollowRequest(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendRejectService, never()).sendRejectFollow(any(), any()) - } - - @Test - fun `rejectFollowRequest リモートユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã¦é…é€ã•ã‚Œã‚‹`() = runTest { - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorRepository.findById(eq(1234))).doReturn(localUser) - - val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = true, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.rejectFollowRequest(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendRejectService, times(1)).sendRejectFollow(eq(localUser), eq(remoteUser)) - } - - @Test - fun `rejectFollowRequest RelationshipãŒå­˜åœ¨ã—ãªã„ã¨ã何もã—ãªã„`() = runTest { - - relationshipServiceImpl.rejectFollowRequest(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `rejectFollowRequest フォローリクエストãŒå­˜åœ¨ã—ãªã„å ´åˆä½•ã‚‚ã—ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(5678), eq(1234))).doReturn( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.rejectFollowRequest(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `ignoreFollowRequest 永続化ã•ã‚Œã‚‹`() = runTest { - relationshipServiceImpl.ignoreFollowRequest(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 5678, - targetActorId = 1234, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = true - ) - ) - ) - } - - @Test - fun `unfollow ローカルユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.remoteUserOf(domain = "remote.example.com")) - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(domain = "example.com")) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unfollow(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendUndoService, never()).sendUndoFollow(any(), any()) - } - - @Test - fun `unfollow リモートユーザー場åˆæ°¸ç¶šåŒ–ã•ã‚Œã¦é…é€ã•ã‚Œã‚‹`() = runTest { - val localUser = UserBuilder.localUserOf(domain = "example.com") - whenever(actorRepository.findById(eq(1234))).doReturn(localUser) - - val remoteUser = UserBuilder.remoteUserOf(domain = "remote.example.com") - whenever(actorRepository.findById(eq(5678))).doReturn(remoteUser) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unfollow(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - - verify(apSendUndoService, times(1)).sendUndoFollow(eq(localUser), eq(remoteUser)) - } - - @Test - fun `unfollow RelationshipãŒå­˜åœ¨ã—ãªã„ã¨ãã¯ä½•ã‚‚ã—ãªã„`() = runTest { - relationshipServiceImpl.unfollow(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `unfollow フォローã—ã¦ã„ãªã‹ã£ãŸå ´åˆã¯ä½•ã‚‚ã—ãªã„`() = runTest { - whenever(actorRepository.findById(eq(1234))).doReturn(UserBuilder.localUserOf(id = 1234)) - whenever(actorRepository.findById(eq(5678))).doReturn(UserBuilder.localUserOf(id = 5678)) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unfollow(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `unblock ローカルユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unblock(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `unblock リモートユーザーã®å ´åˆæ°¸ç¶šåŒ–ã•ã‚Œã¦é…é€ã•ã‚Œã‚‹`() = runTest { - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = true, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unblock(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - 1234, - 5678, - false, - false, - false, - false, - false - ) - ) - ) - } - - @Test - fun `unblock RelationshipãŒãªã„å ´åˆä½•ã‚‚ã—ãªã„`() = runTest { - relationshipServiceImpl.unblock(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `unblock ブロックã—ã¦ã„ãªã„å ´åˆã¯ä½•ã‚‚ã—ãªã„`() = runTest { - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unblock(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } - - @Test - fun `mute ミュートãŒæ°¸ç¶šåŒ–ã•ã‚Œã‚‹`() = runTest { - relationshipServiceImpl.mute(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = true, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `unmute 永続化ã•ã‚Œã‚‹`() = runTest { - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(1234), eq(5678))).doReturn( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = true, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - relationshipServiceImpl.unmute(1234, 5678) - - verify(relationshipRepository, times(1)).save( - eq( - Relationship( - actorId = 1234, - targetActorId = 5678, - following = false, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - ) - } - - @Test - fun `unmute RelationshipãŒå­˜åœ¨ã—ãªã„å ´åˆã¯ä½•ã‚‚ã—ãªã„`() = runTest { - relationshipServiceImpl.unmute(1234, 5678) - - verify(relationshipRepository, never()).save(any()) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveServiceTest.kt deleted file mode 100644 index a170e13e..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveServiceTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.resource - -import dev.usbharu.hideout.application.config.MediaConfig -import dev.usbharu.hideout.core.domain.exception.media.RemoteMediaFileSizeException -import io.ktor.client.* -import io.ktor.client.engine.mock.* -import io.ktor.http.* -import io.ktor.http.HttpHeaders.ContentLength -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension - -@ExtendWith(MockitoExtension::class) -class KtorResourceResolveServiceTest { - - @Spy - private val httpClient: HttpClient = HttpClient(MockEngine { - when (it.url.encodedPath) { - "/over-size-limit" -> { - respond(ByteArray(1000), HttpStatusCode.OK, Headers.build { - append(ContentLength, "1000") - }) - } - - else -> { - respond("Not Found", HttpStatusCode.NotFound) - } - } - }) { - expectSuccess = true - } - - @Spy - private val cacheManager: CacheManager = InMemoryCacheManager() - - @Spy - private val mediaConfig: MediaConfig = MediaConfig() - - @InjectMocks - private lateinit var ktorResourceResolveService: KtorResourceResolveService - - @Test - fun ファイルサイズ制é™ã‚’超ãˆãŸã¨ãRemoteMediaFileSizeExceptionãŒç™ºç”Ÿã™ã‚‹() = runTest { - ktorResourceResolveService.sizeLimit = 100L - assertThrows { - ktorResourceResolveService.resolve("https://example.com/over-size-limit") - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt deleted file mode 100644 index 71c05e4a..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/timeline/TimelineServiceTest.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.service.timeline - -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Visibility -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.PostBuilder -import utils.UserBuilder - -@ExtendWith(MockitoExtension::class) -class TimelineServiceTest { - - @Mock - private lateinit var followerQueryService: FollowerQueryService - - @Mock - private lateinit var actorRepository: ActorRepository - - @Mock - private lateinit var timelineRepository: TimelineRepository - - @InjectMocks - private lateinit var timelineService: TimelineService - - @Captor - private lateinit var captor: ArgumentCaptor> - - @Test - fun `publishTimeline ローカルã®æŠ•ç¨¿ã¯ãƒ­ãƒ¼ã‚«ãƒ«ã®ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ã¨æŠ•ç¨¿è€…ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«è¿½åŠ ã•ã‚Œã‚‹`() = runTest { - val post = PostBuilder.of() - val listOf = listOf(UserBuilder.localUserOf(), UserBuilder.localUserOf()) - val localUserOf = UserBuilder.localUserOf(id = post.actorId) - - whenever(followerQueryService.findFollowersById(eq(post.actorId))).doReturn(listOf) - whenever(actorRepository.findById(eq(post.actorId))).doReturn(localUserOf) - whenever(timelineRepository.generateId()).doReturn(TwitterSnowflakeIdGenerateService.generateId()) - - - timelineService.publishTimeline(post, true) - - verify(timelineRepository).saveAll(capture(captor)) - val timelineList = captor.value - - assertThat(timelineList).hasSize(4).anyMatch { it.userId == post.actorId } - } - - @Test - fun `publishTimeline リモートã®æŠ•ç¨¿ã¯ãƒ­ãƒ¼ã‚«ãƒ«ã®ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«è¿½åŠ ã•ã‚Œã‚‹`() = runTest { - val post = PostBuilder.of() - val listOf = listOf(UserBuilder.localUserOf(), UserBuilder.localUserOf()) - - whenever(followerQueryService.findFollowersById(eq(post.actorId))).doReturn(listOf) - whenever(timelineRepository.generateId()).doReturn(TwitterSnowflakeIdGenerateService.generateId()) - - - timelineService.publishTimeline(post, false) - - verify(timelineRepository).saveAll(capture(captor)) - val timelineList = captor.value - - assertThat(timelineList).hasSize(3) - } - - @Test - fun `publishTimeline パブリック投稿ã¯ãƒ‘ブリックタイムラインã«ã‚‚追加ã•ã‚Œã‚‹`() = runTest { - val post = PostBuilder.of() - val listOf = listOf(UserBuilder.localUserOf(), UserBuilder.localUserOf()) - - whenever(followerQueryService.findFollowersById(eq(post.actorId))).doReturn(listOf) - whenever(timelineRepository.generateId()).doReturn(TwitterSnowflakeIdGenerateService.generateId()) - - - timelineService.publishTimeline(post, false) - - verify(timelineRepository).saveAll(capture(captor)) - val timelineList = captor.value - - assertThat(timelineList).hasSize(3).anyMatch { it.userId == 0L } - } - - @Test - fun `publishTimeline パブリック投稿ã§ã¯ãªã„å ´åˆã¯ãƒ­ãƒ¼ã‚«ãƒ«ã®ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ã®ã¿ã«è¿½åŠ ã•ã‚Œã‚‹`() = runTest { - val post = PostBuilder.of(visibility = Visibility.UNLISTED) - val listOf = listOf(UserBuilder.localUserOf(), UserBuilder.localUserOf()) - - whenever(followerQueryService.findFollowersById(eq(post.actorId))).doReturn(listOf) - whenever(timelineRepository.generateId()).doReturn(TwitterSnowflakeIdGenerateService.generateId()) - - - timelineService.publishTimeline(post, false) - - verify(timelineRepository).saveAll(capture(captor)) - val timelineList = captor.value - - assertThat(timelineList).hasSize(2).noneMatch { it.userId == 0L } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt deleted file mode 100644 index feb0cc4f..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/service/user/ActorServiceTest.kt +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) - -package dev.usbharu.hideout.core.service.user - -import dev.usbharu.hideout.activitypub.service.activity.delete.APSendDeleteService -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository -import dev.usbharu.hideout.core.domain.model.instance.Instance -import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository -import dev.usbharu.owl.producer.api.OwlProducer -import jakarta.validation.Validation -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.ArgumentMatchers.anyString -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.TestApplicationConfig.testApplicationConfig -import java.net.URL -import java.security.KeyPairGenerator -import java.time.Instant -import kotlin.test.assertEquals -import kotlin.test.assertNull - -@ExtendWith(MockitoExtension::class) -class ActorServiceTest { - - @Mock - private lateinit var actorRepository: ActorRepository - - @Mock - private lateinit var userAuthService: UserAuthService - - @Spy - private val actorBuilder = Actor.UserBuilder( - CharacterLimit(), - ApplicationConfig(URL("https://example.com")), - Validation.buildDefaultValidatorFactory().validator - ) - - @Spy - private val applicationConfig: ApplicationConfig = testApplicationConfig.copy(private = false) - - @Mock - private lateinit var instanceService: InstanceService - - @Mock - private lateinit var userDetailRepository: UserDetailRepository - - @Mock - private lateinit var deletedActorRepository: DeletedActorRepository - - @Mock - private lateinit var reactionRepository: ReactionRepository - - @Mock - private lateinit var relationshipRepository: RelationshipRepository - - @Mock - private lateinit var postService: PostService - - @Mock - private lateinit var apSendDeleteService: APSendDeleteService - - @Mock - private lateinit var postRepository: PostRepository - - @Mock - private lateinit var owlProducer: OwlProducer - - @InjectMocks - private lateinit var userService: UserServiceImpl - - @Test - fun `createLocalUser ローカルユーザーを作æˆã§ãã‚‹`() = runTest { - - val generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair() - whenever(actorRepository.nextId()).doReturn(110001L) - whenever(userAuthService.hash(anyString())).doReturn("hashedPassword") - whenever(userAuthService.generateKeyPair()).doReturn(generateKeyPair) - - - - userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) - verify(actorRepository, times(1)).save(any()) - argumentCaptor { - verify(actorRepository, times(1)).save(capture()) - assertEquals("test", firstValue.name) - assertEquals("testUser", firstValue.screenName) - assertEquals("XXXXXXXXXXXXX", firstValue.description) - assertEquals(110001L, firstValue.id) - assertEquals("https://example.com/users/test", firstValue.url) - assertEquals("example.com", firstValue.domain) - assertEquals("https://example.com/users/test/inbox", firstValue.inbox) - assertEquals("https://example.com/users/test/outbox", firstValue.outbox) - assertEquals(generateKeyPair.public.toPem(), firstValue.publicKey) - assertEquals(generateKeyPair.private.toPem(), firstValue.privateKey) - } - } - - @Test - fun `createLocalUser applicationconfig privateãŒtrueã®ã¨ãアカウントを作æˆã§ããªã„`() = runTest { - whenever(applicationConfig.private).thenReturn(true) - - assertThrows { - userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) - } - - } - - @Test - fun `createRemoteUser リモートユーザーを作æˆã§ãã‚‹`() = runTest { - - whenever(actorRepository.nextId()).doReturn(113345L) - whenever(instanceService.fetchInstance(eq("https://remote.example.com"), isNull())).doReturn( - Instance( - 12345L, - "", - "", - "https://remote.example.com", - "https://remote.example.com/favicon.ico", - null, - "unknown", - "", - false, - false, - "", - Instant.now() - ) - ) - - val user = RemoteUserCreateDto( - name = "test", - domain = "remote.example.com", - screenName = "testUser", - description = "test user", - inbox = "https://remote.example.com/inbox", - outbox = "https://remote.example.com/outbox", - url = "https://remote.example.com", - publicKey = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", - keyId = "a", - following = "", - followers = "", - sharedInbox = null, - locked = false - ) - userService.createRemoteUser(user) - verify(actorRepository, times(1)).save(any()) - argumentCaptor { - verify(actorRepository, times(1)).save(capture()) - assertEquals("test", firstValue.name) - assertEquals("testUser", firstValue.screenName) - assertEquals("test user", firstValue.description) - assertEquals(113345L, firstValue.id) - assertEquals("https://remote.example.com", firstValue.url) - assertEquals("remote.example.com", firstValue.domain) - assertEquals("https://remote.example.com/inbox", firstValue.inbox) - assertEquals("https://remote.example.com/outbox", firstValue.outbox) - assertEquals("-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", firstValue.publicKey) - assertNull(firstValue.privateKey) - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationTypeTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationTypeTest.kt deleted file mode 100644 index 1dd7e5b2..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/domain/model/NotificationTypeTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.domain.model - -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.Arguments.arguments -import org.junit.jupiter.params.provider.MethodSource -import org.junit.jupiter.params.provider.ValueSource -import java.util.stream.Stream -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class NotificationTypeTest { - @ParameterizedTest - @MethodSource("parseSuccessProvider") - fun parseã«æˆåŠŸã™ã‚‹(s: String, notificationType: NotificationType) { - assertEquals(notificationType, NotificationType.parse(s)) - } - - @ParameterizedTest - @ValueSource(strings = ["hoge", "fuga", "0x1234", "follow_reject", "test", "mentiooon", "emoji_reaction", "reaction"]) - fun parseã«å¤±æ•—ã™ã‚‹(s: String) { - assertNull(NotificationType.parse(s)) - } - - companion object { - @JvmStatic - fun parseSuccessProvider(): Stream { - return Stream.of( - arguments("mention", NotificationType.mention), - arguments("status", NotificationType.status), - arguments("reblog", NotificationType.reblog), - arguments("follow", NotificationType.follow), - arguments("follow_request", NotificationType.follow_request), - arguments("favourite", NotificationType.favourite), - arguments("poll", NotificationType.poll), - arguments("update", NotificationType.update), - arguments("admin.sign_up", NotificationType.admin_sign_up), - arguments("admin.report", NotificationType.admin_report), - arguments("servered_relationships", NotificationType.severed_relationships) - ) - } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt deleted file mode 100644 index 9ecb49ff..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.account - -import dev.usbharu.hideout.application.config.ActivityPubConfig -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.core.infrastructure.springframework.security.OAuth2JwtLoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.AccountSource -import dev.usbharu.hideout.domain.mastodon.model.generated.CredentialAccount -import dev.usbharu.hideout.domain.mastodon.model.generated.Role -import dev.usbharu.hideout.mastodon.service.account.AccountApiService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever -import org.springframework.http.MediaType -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import utils.TestTransaction -import java.net.URL - -@ExtendWith(MockitoExtension::class) -class MastodonAccountApiControllerTest { - - private lateinit var mockMvc: MockMvc - - @Spy - private val loginUserContextHolder = OAuth2JwtLoginUserContextHolder() - - @Spy - private lateinit var testTransaction: TestTransaction - - @Mock - private lateinit var accountApiService: AccountApiService - - @Spy - private val applicationConfig: ApplicationConfig = ApplicationConfig(URL("https://example.com")) - - @InjectMocks - private lateinit var mastodonAccountApiController: MastodonAccountApiController - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(mastodonAccountApiController).build() - } - - @Test - fun `apiV1AccountsVerifyCredentialsGet JWTã§èªè¨¼æ™‚ã«200ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - val createEmptyContext = SecurityContextHolder.createEmptyContext() - createEmptyContext.authentication = JwtAuthenticationToken( - Jwt.withTokenValue("a").header("alg", "RS236").claim("uid", "1234").build() - ) - SecurityContextHolder.setContext(createEmptyContext) - val credentialAccount = CredentialAccount( - id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - source = AccountSource( - note = "", - fields = emptyList(), - privacy = AccountSource.Privacy.public, - sensitive = false, - followRequestsCount = 0 - ), - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0, - role = Role(0, "ADMIN", "", 0, false) - ) - whenever(accountApiService.verifyCredentials(eq(1234))).doReturn(credentialAccount) - - val objectMapper = ActivityPubConfig().objectMapper() - - mockMvc - .get("/api/v1/accounts/verify_credentials") - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(credentialAccount)) } } - } - - @Test - fun `apiV1AccountsVerifyCredentialsGet POSTã¯405ãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc.post("/api/v1/accounts/verify_credentials") - .andExpect { status { isMethodNotAllowed() } } - } - - @Test - fun `apiV1AccountsPost GETã¯405ãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc.get("/api/v1/accounts") - .andExpect { status { isMethodNotAllowed() } } - } - - @Test - fun `apiV1AccountsPost アカウント作æˆæˆåŠŸæ™‚302ã¨ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®urlãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("username", "hoge") - param("password", "very_secure_password") - param("email", "email@example.com") - param("agreement", "true") - param("locale", "true") - }.asyncDispatch() - .andExpect { header { string("location", "/users/hoge") } } - .andExpect { status { isFound() } } - } - - @Test - fun `apiV1AccountsIdFollowPost フォローæˆåŠŸæ™‚ã¯200ãŒè¿”ã£ã¦ãã‚‹`() { - val createEmptyContext = SecurityContextHolder.createEmptyContext() - createEmptyContext.authentication = JwtAuthenticationToken( - Jwt.withTokenValue("a").header("alg", "RS236").claim("uid", "1234").build() - ) - SecurityContextHolder.setContext(createEmptyContext) - mockMvc - .post("/api/v1/accounts/1/follow") { - contentType = MediaType.APPLICATION_JSON - } - .asyncDispatch() - .andExpect { status { isOk() } } - - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiControllerTest.kt deleted file mode 100644 index fca0dce0..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/apps/MastodonAppsApiControllerTest.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.apps - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.domain.mastodon.model.generated.Application -import dev.usbharu.hideout.domain.mastodon.model.generated.AppsRequest -import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor -import dev.usbharu.hideout.mastodon.service.app.AppApiService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever -import org.springframework.http.MediaType -import org.springframework.http.converter.HttpMessageConverter -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.web.method.annotation.ModelAttributeMethodProcessor -import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - -@ExtendWith(MockitoExtension::class) -class MastodonAppsApiControllerTest { - - @Mock - private lateinit var appApiService: AppApiService - - @InjectMocks - private lateinit var mastodonAppsApiController: MastodonAppsApiController - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(mastodonAppsApiController).setCustomArgumentResolvers( - JsonOrFormModelMethodProcessor( - ModelAttributeMethodProcessor(false), RequestResponseBodyMethodProcessor( - mutableListOf>( - MappingJackson2HttpMessageConverter() - ) - ) - ) - ).build() - } - - @Test - fun `apiV1AppsPost JSONã§ä½œæˆã«æˆåŠŸã—ãŸã‚‰200ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - val appsRequest = AppsRequest( - "test", - "https://example.com", - "write", - null - ) - val application = Application( - "test", - "", - null, - "safdash;", - "aksdhgoa" - ) - - whenever(appApiService.createApp(eq(appsRequest))).doReturn(application) - - val objectMapper = jacksonObjectMapper() - val writeValueAsString = objectMapper.writeValueAsString(appsRequest) - - mockMvc - .post("/api/v1/apps") { - contentType = MediaType.APPLICATION_JSON - content = writeValueAsString - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(application)) } } - } - - @Test - fun `apiV1AppsPost FORMã§ä½œæˆã«æˆåŠŸã—ãŸã‚‰200ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - val appsRequest = AppsRequest( - "test", - "https://example.com", - "write", - null - ) - val application = Application( - "test", - "", - null, - "safdash;", - "aksdhgoa" - ) - - whenever(appApiService.createApp(eq(appsRequest))).doReturn(application) - - val objectMapper = jacksonObjectMapper() - - mockMvc - .post("/api/v1/apps") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("client_name", "test") - param("redirect_uris", "https://example.com") - param("scopes", "write") - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(application)) } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiControllerTest.kt deleted file mode 100644 index bca820da..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/instance/MastodonInstanceApiControllerTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.instance - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.domain.mastodon.model.generated.* -import dev.usbharu.hideout.mastodon.service.instance.InstanceApiService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.whenever -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders - -@ExtendWith(MockitoExtension::class) -class MastodonInstanceApiControllerTest { - - @Mock - private lateinit var instanceApiService: InstanceApiService - - @InjectMocks - private lateinit var mastodonInstanceApiController: MastodonInstanceApiController - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(mastodonInstanceApiController).build() - } - - @Test - fun `apiV1InstanceGet GETã—ãŸã‚‰200ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - val v1Instance = V1Instance( - uri = "https://example.com", - title = "hideout", - shortDescription = "test", - description = "test instance", - email = "test@example.com", - version = "0.0.1", - urls = V1InstanceUrls(streamingApi = "https://example.com/atreaming"), - stats = V1InstanceStats(userCount = 1, statusCount = 0, domainCount = 0), - thumbnail = "https://example.com", - languages = emptyList(), - registrations = false, - approvalRequired = false, - invitesEnabled = false, - configuration = V1InstanceConfiguration( - accounts = V1InstanceConfigurationAccounts(0), - V1InstanceConfigurationStatuses(100, 4, 23), - V1InstanceConfigurationMediaAttachments(emptyList(), 100, 100, 100, 100, 100), - V1InstanceConfigurationPolls( - 10, 10, 10, 10 - ) - ), - contactAccount = Account( - id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - emptyList() - ) - whenever(instanceApiService.v1Instance()).doReturn(v1Instance) - - val objectMapper = jacksonObjectMapper() - - mockMvc - .get("/api/v1/instance") - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(objectMapper)) } } - } - - @Test - fun `apiV1InstanceGet POSTã—ãŸã‚‰405ãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc - .post("/api/v1/instance") - .andExpect { status { isMethodNotAllowed() } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiControllerTest.kt deleted file mode 100644 index 24e79604..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/media/MastodonMediaApiControllerTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.media - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment -import dev.usbharu.hideout.mastodon.service.media.MediaApiService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.whenever -import org.springframework.mock.web.MockMultipartFile -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.multipart -import org.springframework.test.web.servlet.setup.MockMvcBuilders - -@ExtendWith(MockitoExtension::class) -class MastodonMediaApiControllerTest { - - @Mock - private lateinit var mediaApiService: MediaApiService - - @InjectMocks - private lateinit var mastodonMediaApiController: MastodonMediaApiController - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(mastodonMediaApiController).build() - } - - @Test - fun `apiV1MediaPost ファイルã¨ã‚µãƒ ãƒã‚¤ãƒ«ã‚’アップロードã§ãã‚‹`() = runTest { - - val mediaAttachment = MediaAttachment( - id = "1234", - type = MediaAttachment.Type.image, - url = "https://example.com", - previewUrl = "https://example.com", - remoteUrl = "https://example.com", - description = "pngImageStream", - blurhash = "", - textUrl = "https://example.com" - ) - whenever(mediaApiService.postMedia(any())).doReturn(mediaAttachment) - - val objectMapper = jacksonObjectMapper() - - mockMvc - .multipart("/api/v1/media") { - file(MockMultipartFile("file", "test.png", "image/png", "jpgImageStream".toByteArray())) - file(MockMultipartFile("thumbnail", "thumbnail.png", "image/png", "pngImageStream".toByteArray())) - param("description", "jpgImage") - param("focus", "") - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(mediaAttachment)) } } - } - - @Test - fun `apiV1MediaPost ファイルã ã‘をアップロードã§ãã‚‹`() = runTest { - - val mediaAttachment = MediaAttachment( - id = "1234", - type = MediaAttachment.Type.image, - url = "https://example.com", - previewUrl = "https://example.com", - remoteUrl = "https://example.com", - description = "pngImageStream", - blurhash = "", - textUrl = "https://example.com" - ) - whenever(mediaApiService.postMedia(any())).doReturn(mediaAttachment) - - val objectMapper = jacksonObjectMapper() - - mockMvc - .multipart("/api/v1/media") { - file(MockMultipartFile("file", "test.png", "image/png", "jpgImageStream".toByteArray())) - param("description", "jpgImage") - param("focus", "") - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(mediaAttachment)) } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiControllerTest.kt deleted file mode 100644 index ff2047ea..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/MastodonStatusesApiControllerTest.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.status - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.core.infrastructure.springframework.security.OAuth2JwtLoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor -import dev.usbharu.hideout.mastodon.service.status.StatusesApiService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever -import org.springframework.http.MediaType -import org.springframework.http.converter.HttpMessageConverter -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.web.method.annotation.ModelAttributeMethodProcessor -import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - -@ExtendWith(MockitoExtension::class) -class MastodonStatusesApiControllerTest { - - @Spy - private val loginUserContextHolder = OAuth2JwtLoginUserContextHolder() - - @Mock - private lateinit var statusesApiService: StatusesApiService - - @InjectMocks - private lateinit var mastodonStatusesApiController: MastodonStatusesApiContoller - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(mastodonStatusesApiController).setCustomArgumentResolvers( - JsonOrFormModelMethodProcessor( - ModelAttributeMethodProcessor(false), RequestResponseBodyMethodProcessor( - mutableListOf>( - MappingJackson2HttpMessageConverter() - ) - ) - ) - ).build() - } - - @Test - fun `apiV1StatusesPost JWTèªè¨¼æ™‚POSTã™ã‚‹ã¨æŠ•ç¨¿ã§ãã‚‹`() = runTest { - val createEmptyContext = SecurityContextHolder.createEmptyContext() - createEmptyContext.authentication = JwtAuthenticationToken( - Jwt.withTokenValue("a").header("alg", "RS236").claim("uid", "1234").build() - ) - SecurityContextHolder.setContext(createEmptyContext) - val status = Status( - id = "", - uri = "", - createdAt = "", - account = Account( - id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null - - ) - - val objectMapper = jacksonObjectMapper() - - val statusesRequest = StatusesRequest() - - statusesRequest.status = "hello" - - whenever(statusesApiService.postStatus(eq(statusesRequest), eq(1234))).doReturn(status) - - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - content = objectMapper.writeValueAsString(statusesRequest) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(status)) } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt deleted file mode 100644 index 8302d0cf..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.interfaces.api.timeline - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.infrastructure.springframework.security.OAuth2JwtLoginUserContextHolder -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.service.timeline.TimelineApiService -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import java.net.URL - -@ExtendWith(MockitoExtension::class) -class MastodonTimelineApiControllerTest { - - @Spy - private val loginUserContextHolder = OAuth2JwtLoginUserContextHolder() - - @Mock - private lateinit var timelineApiService: TimelineApiService - - @Spy - private val applicationConfig: ApplicationConfig = ApplicationConfig(URL("https://example.com")) - - @InjectMocks - private lateinit var mastodonTimelineApiController: MastodonTimelineApiController - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.standaloneSetup(mastodonTimelineApiController).build() - } - - val statusList = PaginationList( - listOf( - Status( - id = "", - uri = "", - createdAt = "", - account = Account( - id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null - - ), - Status( - id = "", - uri = "", - createdAt = "", - account = Account( - id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null - - ) - ), null, null - ) - - @Test - fun `apiV1TimelineHogeGet JWTèªè¨¼ã§ãƒ­ã‚°ã‚¤ãƒ³ã˜200ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - - val createEmptyContext = SecurityContextHolder.createEmptyContext() - createEmptyContext.authentication = JwtAuthenticationToken( - Jwt.withTokenValue("a").header("alg", "RS236").claim("uid", "1234").build() - ) - SecurityContextHolder.setContext(createEmptyContext) - - whenever( - timelineApiService.homeTimeline( - eq(1234), - any() - ) - ).doReturn(statusList) - - val objectMapper = jacksonObjectMapper() - - mockMvc - .get("/api/v1/timelines/home?max_id=123456&since_id=1234567&min_id=54321&limit=20") - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(statusList)) } } - } - - @Test - fun `apiV1TimelineHomeGet パラメーターãŒãªãã¦ã‚‚å–å¾—ã§ãã‚‹`() = runTest { - val createEmptyContext = SecurityContextHolder.createEmptyContext() - createEmptyContext.authentication = JwtAuthenticationToken( - Jwt.withTokenValue("a").header("alg", "RS236").claim("uid", "1234").build() - ) - SecurityContextHolder.setContext(createEmptyContext) - - whenever( - timelineApiService.homeTimeline( - eq(1234), - any() - ) - ).doReturn(statusList) - - val objectMapper = jacksonObjectMapper() - - mockMvc - .get("/api/v1/timelines/home") - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(statusList)) } } - } - - @Test - fun `apiV1TimelineHomeGet POSTã«ã¯405ã‚’è¿”ã™`() { - mockMvc - .post("/api/v1/timelines/home?max_id=123456&since_id=1234567&min_id=54321&limit=20") - .andExpect { status { isMethodNotAllowed() } } - } - - @Test - fun `apiV1TimelinePublicGet GETã§200ãŒè¿”ã£ã¦ãã‚‹`() = runTest { - whenever( - timelineApiService.publicTimeline( - localOnly = eq(false), - remoteOnly = eq(true), - mediaOnly = eq(false), - any() - ) - ).doAnswer { - println(it.arguments.joinToString()) - statusList - } - - val objectMapper = jacksonObjectMapper() - - mockMvc - .get("/api/v1/timelines/public?local=false&remote=true&only_media=false&max_id=1234&since_id=12345&min_id=4321&limit=20") - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(statusList)) } } - } - - @Test - fun `apiV1TimelinePublicGet POSTã§405ãŒè¿”ã£ã¦ãã‚‹`() { - mockMvc.post("/api/v1/timelines/public") - .andExpect { status { isMethodNotAllowed() } } - } - - @Test - fun `apiV1TimelinePublicGet パラメーターãŒãªãã¦ã‚‚å–å¾—ã§ãã‚‹`() = runTest { - whenever( - timelineApiService.publicTimeline( - localOnly = eq(false), - remoteOnly = eq(false), - mediaOnly = eq(false), - any() - ) - ).doAnswer { - println(it.arguments.joinToString()) - statusList - } - - val objectMapper = jacksonObjectMapper() - - mockMvc - .get("/api/v1/timelines/public") - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { content { json(objectMapper.writeValueAsString(statusList)) } } - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt deleted file mode 100644 index 0640f8ba..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.mastodon.service.account - -import dev.usbharu.hideout.application.infrastructure.exposed.Page -import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.service.media.MediaService -import dev.usbharu.hideout.domain.mastodon.model.generated.Account -import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.hideout.mastodon.query.StatusQueryService -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.* -import utils.TestTransaction - -@ExtendWith(MockitoExtension::class) -class AccountApiServiceImplTest { - - @Mock - private lateinit var accountService: AccountService - - @Mock - private lateinit var userService: UserService - - @Mock - private lateinit var actorRepository: ActorRepository - - @Mock - private lateinit var followerQueryService: FollowerQueryService - - @Mock - private lateinit var statusQueryService: StatusQueryService - - @Spy - private val transaction: Transaction = TestTransaction - - @Mock - private lateinit var relationshipService: RelationshipService - - @Mock - private lateinit var relationshipRepository: RelationshipRepository - - @Mock - private lateinit var mediaService: MediaService - - @InjectMocks - private lateinit var accountApiServiceImpl: AccountApiServiceImpl - - private val statusList = PaginationList( - listOf( - Status( - id = "", - uri = "", - createdAt = "", - account = Account( - id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, - createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null - ) - ), null, null - ) - - @Test - fun `accountsStatuses éžãƒ­ã‚°ã‚¤ãƒ³æ™‚ã¯éžå…¬é–‹æŠ•ç¨¿ã‚’見れãªã„`() = runTest { - val userId = 1234L - - whenever( - statusQueryService.accountsStatus( - accountId = eq(userId), - onlyMedia = eq(false), - excludeReplies = eq(false), - excludeReblogs = eq(false), - pinned = eq(false), - tagged = isNull(), - includeFollowers = eq(false), - page = any() - ) - ).doReturn( - statusList - ) - - - val accountsStatuses = accountApiServiceImpl.accountsStatuses( - userid = userId, - onlyMedia = false, - excludeReplies = false, - excludeReblogs = false, - pinned = false, - tagged = null, - loginUser = null, - Page.of() - ) - - assertThat(accountsStatuses).hasSize(1) - - verify(followerQueryService, never()).alreadyFollow(any(), any()) - } - - @Test - fun `accountsStatuses ログイン時フォロワーã˜ã‚ƒãªã„å ´åˆã¯éžå…¬é–‹æŠ•ç¨¿ã‚’見れãªã„`() = runTest { - val userId = 1234L - val loginUser = 1L - whenever( - statusQueryService.accountsStatus( - accountId = eq(userId), - onlyMedia = eq(false), - excludeReplies = eq(false), - excludeReblogs = eq(false), - pinned = eq(false), - tagged = isNull(), - includeFollowers = eq(false), - page = any() - ) - ).doReturn(statusList) - - val accountsStatuses = accountApiServiceImpl.accountsStatuses( - userid = userId, - onlyMedia = false, - excludeReplies = false, - excludeReblogs = false, - pinned = false, - tagged = null, - loginUser = loginUser, - Page.of() - ) - - assertThat(accountsStatuses).hasSize(1) - } - - @Test - fun `accountsStatuses ログイン時フォロワーã®å ´åˆã¯éžå…¬é–‹æŠ•ç¨¿ã‚’見れる`() = runTest { - val userId = 1234L - val loginUser = 2L - whenever( - statusQueryService.accountsStatus( - accountId = eq(userId), - onlyMedia = eq(false), - excludeReplies = eq(false), - excludeReblogs = eq(false), - pinned = eq(false), - tagged = isNull(), - includeFollowers = eq(true), - page = any() - ) - ).doReturn(statusList) - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(loginUser), eq(userId))).doReturn( - dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = loginUser, - targetActorId = userId, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - - val accountsStatuses = accountApiServiceImpl.accountsStatuses( - userid = userId, - onlyMedia = false, - excludeReplies = false, - excludeReblogs = false, - pinned = false, - tagged = null, - loginUser = loginUser, - Page.of() - ) - - assertThat(accountsStatuses).hasSize(1) - } - - @Test - fun `follow 未フォローã®å ´åˆãƒ•ã‚©ãƒ­ãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒç™ºç”Ÿã™ã‚‹`() = runTest { - val userId = 1234L - val followeeId = 1L - - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(followeeId), eq(userId))).doReturn( - dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = followeeId, - targetActorId = userId, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - whenever(relationshipRepository.findByUserIdAndTargetUserId(eq(userId), eq(followeeId))).doReturn( - dev.usbharu.hideout.core.domain.model.relationship.Relationship( - actorId = userId, - targetActorId = followeeId, - following = true, - blocking = false, - muting = false, - followRequest = false, - ignoreFollowRequestToTarget = false - ) - ) - - - val follow = accountApiServiceImpl.follow(userId, followeeId) - - val expected = Relationship( - id = followeeId.toString(), - following = true, - showingReblogs = true, - notifying = false, - followedBy = true, - blocking = false, - blockedBy = false, - muting = false, - mutingNotifications = false, - requested = false, - domainBlocking = false, - endorsed = false, - note = "" - ) - assertThat(follow).isEqualTo(expected) - - verify(relationshipService, times(1)).followRequest(eq(userId), eq(followeeId)) - } - - @Test - fun `relationships idãŒé•·ã™ãŽãŸã‚‰çœç•¥ã™ã‚‹`() = runTest { - - val relationships = accountApiServiceImpl.relationships( - userid = 1234L, - id = (1..30L).toList(), - withSuspended = false - ) - - assertThat(relationships).hasSize(20) - } - - @Test - fun `relationships id0ã®å ´åˆå³æ™‚return`() = runTest { - val relationships = accountApiServiceImpl.relationships( - userid = 1234L, - id = emptyList(), - withSuspended = false - ) - - assertThat(relationships).hasSize(0) - verify(followerQueryService, never()).alreadyFollow(any(), any()) - } - - @Test - fun `relationships idã«æŒ‡å®šã•ã‚ŒãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®é–¢ä¿‚ã‚’å–å¾—ã™ã‚‹`() = runTest { - - val relationships = accountApiServiceImpl.relationships( - userid = 1234L, - id = (1..15L).toList(), - withSuspended = false - ) - - assertThat(relationships).hasSize(15) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/util/EmojiUtilTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/util/EmojiUtilTest.kt deleted file mode 100644 index 937f7e8b..00000000 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/util/EmojiUtilTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.util - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource - -class EmojiUtilTest { - - @Test - fun 絵文字を判定ã§ãã‚‹() { - val emoji = "â¤" - val actual = EmojiUtil.isEmoji(emoji) - - assertThat(actual).isTrue() - } - - @Test - fun ãŸã ã®æ–‡å­—を判定ã§ãã‚‹() { - val moji = "blobblinkhyper" - val actual = EmojiUtil.isEmoji(moji) - - assertThat(actual).isFalse() - } - - @ParameterizedTest - @ValueSource(strings = ["â¤", "🌄", "🤗", "⛺", "🧑â€ðŸ¤â€ðŸ§‘", "ðŸ–ðŸ¿"]) - fun `絵文字判定`(s: String) { - val actual = EmojiUtil.isEmoji(s) - - assertThat(actual).isTrue() - } - - @ParameterizedTest - @ValueSource(strings = ["â„¢", "ã‚", "㌠"]) - fun `文字判定`(s: String) { - val actual = EmojiUtil.isEmoji(s) - - assertThat(actual).isFalse() - } -} diff --git a/hideout-core/src/test/kotlin/utils/JsonObjectMapper.kt b/hideout-core/src/test/kotlin/utils/JsonObjectMapper.kt deleted file mode 100644 index 41cb81c3..00000000 --- a/hideout-core/src/test/kotlin/utils/JsonObjectMapper.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package utils - -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.annotation.JsonSetter -import com.fasterxml.jackson.annotation.Nulls -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper - -object JsonObjectMapper { - val objectMapper: com.fasterxml.jackson.databind.ObjectMapper = - jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - - init { - objectMapper.configOverride(List::class.java).setSetterInfo( - JsonSetter.Value.forValueNulls( - Nulls.AS_EMPTY - ) - ) - } -} diff --git a/hideout-core/src/test/kotlin/utils/PostBuilder.kt b/hideout-core/src/test/kotlin/utils/PostBuilder.kt deleted file mode 100644 index 41b9f746..00000000 --- a/hideout-core/src/test/kotlin/utils/PostBuilder.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package utils - -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.application.config.HtmlSanitizeConfig -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.service.post.DefaultPostContentFormatter -import jakarta.validation.Validation -import kotlinx.coroutines.runBlocking -import java.time.Instant - -object PostBuilder { - - private val postBuilder = - Post.PostBuilder( - CharacterLimit(), - DefaultPostContentFormatter(HtmlSanitizeConfig().policy()), - Validation.buildDefaultValidatorFactory().validator - ) - - private val idGenerator = TwitterSnowflakeIdGenerateService - - fun of( - id: Long = generateId(), - userId: Long = generateId(), - overview: String? = null, - text: String = "Hello World", - createdAt: Long = Instant.now().toEpochMilli(), - visibility: Visibility = Visibility.PUBLIC, - url: String = "https://example.com/users/$userId/posts/$id", - ): Post { - return postBuilder.of( - id = id, - actorId = userId, - overview = overview, - content = text, - createdAt = createdAt, - visibility = visibility, - url = url, - ) - } - - private fun generateId(): Long = runBlocking { - idGenerator.generateId() - } -} diff --git a/hideout-core/src/test/kotlin/utils/TestApplicationConfig.kt b/hideout-core/src/test/kotlin/utils/TestApplicationConfig.kt deleted file mode 100644 index 699e8777..00000000 --- a/hideout-core/src/test/kotlin/utils/TestApplicationConfig.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package utils - -import dev.usbharu.hideout.application.config.ApplicationConfig -import java.net.URL - -object TestApplicationConfig { - val testApplicationConfig = ApplicationConfig(URL("https://example.com")) -} diff --git a/hideout-core/src/test/kotlin/utils/UserBuilder.kt b/hideout-core/src/test/kotlin/utils/UserBuilder.kt deleted file mode 100644 index fd929c81..00000000 --- a/hideout-core/src/test/kotlin/utils/UserBuilder.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package utils - -import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.config.CharacterLimit -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService -import jakarta.validation.Validation -import kotlinx.coroutines.runBlocking -import java.net.URL -import java.time.Instant - -object UserBuilder { - private val actorBuilder = Actor.UserBuilder( - CharacterLimit(), ApplicationConfig(URL("https://example.com")), - Validation.buildDefaultValidatorFactory().validator - ) - - private val idGenerator = TwitterSnowflakeIdGenerateService - - fun localUserOf( - id: Long = generateId(), - name: String = "test-user-$id", - domain: String = "example.com", - screenName: String = name, - description: String = "This user is test user.", - inbox: String = "https://$domain/users/$id/inbox", - outbox: String = "https://$domain/users/$id/outbox", - url: String = "https://$domain/users/$id", - publicKey: String = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", - privateKey: String = "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----", - createdAt: Instant = Instant.now(), - keyId: String = "https://$domain/users/$id#pubkey", - followers: String = "https://$domain/users/$id/followers", - following: String = "https://$domain/users/$id/following", - ): Actor { - return actorBuilder.of( - id = id, - name = name, - domain = domain, - screenName = screenName, - description = description, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = privateKey, - createdAt = createdAt, - keyId = keyId, - followers = followers, - following = following, - locked = false, - instance = 0 - ) - } - - fun remoteUserOf( - id: Long = generateId(), - name: String = "test-user-$id", - domain: String = "remote.example.com", - screenName: String = name, - description: String = "This user is test user.", - inbox: String = "https://$domain/$id/inbox", - outbox: String = "https://$domain/$id/outbox", - url: String = "https://$domain/$id/", - publicKey: String = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", - createdAt: Instant = Instant.now(), - keyId: String = "https://$domain/$id#pubkey", - followers: String = "https://$domain/$id/followers", - following: String = "https://$domain/$id/following", - instanceId: Long = generateId(), - ): Actor { - return actorBuilder.of( - id = id, - name = name, - domain = domain, - screenName = screenName, - description = description, - inbox = inbox, - outbox = outbox, - url = url, - publicKey = publicKey, - privateKey = null, - createdAt = createdAt, - keyId = keyId, - followers = followers, - following = following, - locked = false, - instance = instanceId - ) - } - - private fun generateId(): Long = runBlocking { - idGenerator.generateId() - } -} From d5cb84027064552dcf91b5d4c38604c9c06a5676 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 2 Jun 2024 21:35:03 +0900 Subject: [PATCH 15/54] wip --- .../domain/model/actor/ActorDescription.kt | 6 +- .../core/domain/model/actor/ActorName.kt | 6 + .../domain/model/actor/ActorPostsCount.kt | 3 - .../model/actor/ActorRelationshipCount.kt | 3 - .../domain/model/actor/ActorScreenName.kt | 7 +- .../core/domain/model/shared/Domain.kt | 4 + .../core/domain/model/actor/ActorNameTest.kt | 35 ++++++ .../domain/model/actor/ActorScreenNameTest.kt | 10 +- .../core/domain/model/actor/ActorsTest.kt | 104 ++++++++++++++++++ .../domain/model/actor/TestActor2Factory.kt | 4 +- .../core/domain/model/post/PostTest.kt | 10 ++ .../test/kotlin/utils/AssertDomainEvent.kt | 35 ++++++ 12 files changed, 214 insertions(+), 13 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorNameTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt create mode 100644 hideout-core/src/test/kotlin/utils/AssertDomainEvent.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt index 3d734d97..7bc487a1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -16,8 +16,10 @@ package dev.usbharu.hideout.core.domain.model.actor -@JvmInline -value class ActorDescription(val description: String) { + +class ActorDescription(description: String) { + val description: String = description.take(length) + companion object { val length = 10000 val empty = ActorDescription("") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt index d65cd986..6819fc8e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorName.kt @@ -18,8 +18,14 @@ package dev.usbharu.hideout.core.domain.model.actor @JvmInline value class ActorName(val name: String) { + init { + require(name.isNotBlank()) + require(name.length <= length) + require(regex.matches(name)) + } companion object { val length = 300 + private val regex = Regex("^[a-zA-Z0-9_-]{1,$length}\$") } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt index e24819e4..8c344c05 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPostsCount.kt @@ -22,9 +22,6 @@ value class ActorPostsCount(val postsCount: Int) { require(0 <= this.postsCount) { "Posts count must be greater than 0" } } - operator fun inc() = ActorPostsCount(postsCount + 1) - operator fun dec() = ActorPostsCount(postsCount - 1) - companion object { val ZERO = ActorPostsCount(0) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt index 7543996d..e21b75f4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRelationshipCount.kt @@ -21,7 +21,4 @@ value class ActorRelationshipCount(val relationshipCount: Int) { init { require(0 <= relationshipCount) { "Followers count must be > 0" } } - - operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount + 1) - operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount - 1) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt index 343e8f8a..4f7a8aed 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt @@ -16,8 +16,11 @@ package dev.usbharu.hideout.core.domain.model.actor -@JvmInline -value class ActorScreenName(val screenName: String) { + +class ActorScreenName(screenName: String) { + + val screenName: String = screenName.take(length) + companion object { val length = 300 val empty = ActorScreenName("") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt index 7ac333af..963c2574 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt @@ -18,6 +18,10 @@ package dev.usbharu.hideout.core.domain.model.shared @JvmInline value class Domain(val domain: String) { + init { + require(domain.length <= length) + } + companion object { val length = 1000 } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorNameTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorNameTest.kt new file mode 100644 index 00000000..477f799d --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorNameTest.kt @@ -0,0 +1,35 @@ +package dev.usbharu.hideout.core.domain.model.actor + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows + +class ActorNameTest { + @Test + fun blankã¯ãƒ€ãƒ¡() { + assertThrows { + ActorName("") + } + } + + @Test + fun é•·éŽãŽã‚‹ã¨ãƒ€ãƒ¡() { + assertThrows { + ActorName("a".repeat(1000)) + } + } + + @Test + fun 指定外ã®æ–‡å­—ã¯ä½¿ãˆãªã„() { + assertThrows { + ActorName("ã‚") + } + } + + @Test + fun 普通ã«ä½œæˆã§ãã‚‹() { + assertDoesNotThrow { + ActorName("test-user") + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt index febff754..163dbaf3 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenNameTest.kt @@ -1,5 +1,13 @@ package dev.usbharu.hideout.core.domain.model.actor -class ActorScreenNameTest { +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +class ActorScreenNameTest { + @Test + fun screenNameãŒlengthを超ãˆã‚‹ã¨ç„¡è¦–ã•ã‚Œã‚‹() { + val actorScreenName = ActorScreenName("a".repeat(1000)) + + assertEquals(ActorScreenName.length, actorScreenName.screenName.length) + } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt index 3a65d48f..d35fb0f8 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt @@ -1,9 +1,53 @@ package dev.usbharu.hideout.core.domain.model.actor +import dev.usbharu.hideout.core.domain.event.actor.ActorEvent import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import utils.AssertDomainEvent.assertContainsEvent +import utils.AssertDomainEvent.assertEmpty +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull class ActorsTest { + @Test + fun suspendãŒtrueã®ã¨ãactorSuspendイベントãŒç™ºç”Ÿã™ã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + actor.suspend = true + + assertContainsEvent(actor, ActorEvent.actorSuspend.eventName) + } + + @Test + fun suspendãŒfalseã«ãªã£ãŸã¨ãactorUnsuspendイベントãŒç™ºç”Ÿã™ã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""), suspend = true) + + actor.suspend = false + + assertContainsEvent(actor, ActorEvent.actorUnsuspend.eventName) + } + + @Test + fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œãªã„å ´åˆæ›´æ–°ã•ã‚Œã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + val actorIds = setOf(ActorId(100), ActorId(200)) + actor.alsoKnownAs = actorIds + + assertEquals(actorIds, actor.alsoKnownAs) + } + + @Test + fun moveToã«è‡ªåˆ†è‡ªèº«ãŒè¨­å®šã•ã‚ŒãŸå ´åˆmoveイベントãŒç™ºç”Ÿã—æ›´æ–°ã•ã‚Œã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + + actor.moveTo = ActorId(100) + + assertContainsEvent(actor, ActorEvent.move.eventName) + } + @Test fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œã¦ã¯ã„ã‘ãªã„() { val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) @@ -22,5 +66,65 @@ class ActorsTest { } } + @Test + fun descriptionãŒæ›´æ–°ã•ã‚ŒãŸã¨ãupdateイベントãŒç™ºç”Ÿã™ã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + actor.description = ActorDescription("hoge fuga") + + assertContainsEvent(actor, ActorEvent.update.eventName) + } + + @Test + fun screenNameãŒæ›´æ–°ã•ã‚ŒãŸã¨ãupdateイベントãŒç™ºç”Ÿã™ã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + actor.screenName = ActorScreenName("fuga hoge") + + assertContainsEvent(actor, ActorEvent.update.eventName) + } + + @Test + fun deleteãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãã™ã§ã«deletedãŒtrueãªã‚‰ä½•ã‚‚ã—ãªã„() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""), deleted = true) + + actor.delete() + + assertEmpty(actor) + } + + @Test + fun deleteãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãdeletedãŒfalseãªã‚‰deleteイベントãŒç™ºç”Ÿã™ã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + actor.delete() + + assertEquals(ActorScreenName.empty, actor.screenName) + assertEquals(ActorDescription.empty, actor.description) + assertEquals(emptySet(), actor.emojis) + assertNull(actor.lastPostAt) + assertEquals(ActorPostsCount.ZERO, actor.postsCount) + assertNull(actor.followersCount) + assertNull(actor.followingCount) + assertContainsEvent(actor, ActorEvent.delete.eventName) + } + + @Test + fun restoreãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãcheckUpdateイベントãŒç™ºç”Ÿã™ã‚‹() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""), deleted = true) + + actor.restore() + + assertFalse(actor.deleted) + assertContainsEvent(actor, ActorEvent.checkUpdate.eventName) + } + + @Test + fun checkUpdateãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãcheckUpdateイベントãŒh() { + val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + + actor.checkUpdate() + + assertContainsEvent(actor, ActorEvent.checkUpdate.eventName) + } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt index 220b4056..217fb36b 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt @@ -54,8 +54,8 @@ object TestActor2Factory { keyId = ActorKeyId(keyId), followersEndpoint = followersEndpoint, followingEndpoint = followingEndpoint, - InstanceId(instanceId), - locked, + instance = InstanceId(instanceId), + locked = locked, followersCount = ActorRelationshipCount(followersCount), followingCount = ActorRelationshipCount(followingCount), postsCount = ActorPostsCount(postCount), diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt new file mode 100644 index 00000000..b14d9bbe --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -0,0 +1,10 @@ +package dev.usbharu.hideout.core.domain.model.post + +import org.junit.jupiter.api.Test + +class PostTest { + @Test + fun deletedãŒtrueã®ã¨ãghostã®idãŒè¿”ã•ã‚Œã‚‹() { + + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/utils/AssertDomainEvent.kt b/hideout-core/src/test/kotlin/utils/AssertDomainEvent.kt new file mode 100644 index 00000000..d1d2c049 --- /dev/null +++ b/hideout-core/src/test/kotlin/utils/AssertDomainEvent.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable + +object AssertDomainEvent { + fun assertContainsEvent(domainEventStorable: DomainEventStorable, eventName: String) { + val find = domainEventStorable.getDomainEvents().find { it.name == eventName } + + if (find == null) { + throw AssertionError("Domain Event not found: $eventName") + } + } + + fun assertEmpty(domainEventStorable: DomainEventStorable) { + if (domainEventStorable.getDomainEvents().isNotEmpty()) { + throw AssertionError("Domain Event found") + } + } +} \ No newline at end of file From 71eeb47169d0ec67fd55a8de49ccaf56a23104ad Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 2 Jun 2024 23:18:45 +0900 Subject: [PATCH 16/54] wip --- .../intTest/kotlin/mastodon/apps/AppTest.kt | 2 +- .../hideout/application/config/AwsConfig.kt | 38 -- .../application/config/CaptchaConfig.kt | 9 - .../application/config/HttpClientConfig.kt | 44 -- .../application/config/HttpSignatureConfig.kt | 36 -- .../application/config/MdcXrequestIdFilter.kt | 45 -- .../hideout/application/config/MediaConfig.kt | 24 -- .../application/config/MvcConfigurer.kt | 46 -- .../hideout/application/config/OwlConfig.kt | 89 ---- .../application/config/SecurityConfig.kt | 319 -------------- .../application/config/SpringConfig.kt | 105 ----- .../application/external/OwlProducerRunner.kt | 42 -- .../exposed/ExposedPaginationExtension.kt | 35 -- .../infrastructure/exposed/Page.kt | 63 --- .../infrastructure/exposed/PaginationList.kt | 38 -- ...oleHierarchyAuthorizationManagerFactory.kt | 32 -- .../RegisterLocalActorApplicationService.kt | 2 +- .../config/HtmlSanitizeConfig.kt | 2 +- .../post/DefaultPostContentFormatter.kt} | 14 +- .../service/post/PostContentFormatter.kt} | 12 +- .../domain/shared}/id/IdGenerateService.kt | 5 +- .../exposed/ActorQueryMapper.kt | 2 - .../exposed/ActorResultRowMapper.kt | 1 - .../exposed/ExposedTransaction.kt | 17 +- .../infrastructure/exposed/QueryMapper.kt | 2 +- .../infrastructure/exposed/ResultRowMapper.kt | 2 +- .../ExposedActorRepository.kt | 2 +- .../factory/ActorFactoryImpl.kt | 2 +- .../factory/PostContentFactoryImpl.kt | 2 +- .../infrastructure/factory/PostFactoryImpl.kt | 2 +- .../httpsignature/HttpRequestMixIn.kt | 45 -- .../other}/SnowflakeIdGenerateService.kt | 5 +- .../TwitterSnowflakeIdGenerateService.kt | 2 +- ...xposedOAuth2AuthorizationConsentService.kt | 97 ----- .../ExposedOAuth2AuthorizationService.kt | 393 ------------------ .../oauth2/RegisteredClientRepositoryImpl.kt | 206 --------- .../springframework/oauth2/UserDetailsImpl.kt | 120 ------ .../OAuth2JwtLoginUserContextHolder.kt | 39 -- .../hideout/generate/JsonOrFormBind.kt | 22 - .../JsonOrFormModelMethodProcessor.kt | 78 ---- .../domain/model/actor/TestActor2Factory.kt | 2 +- hideout-mastodon/src/main/kotlin/Main.kt | 5 - 42 files changed, 33 insertions(+), 2015 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpClientConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MdcXrequestIdFilter.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MediaConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MvcConfigurer.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/OwlConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/springframework/RoleHierarchyAuthorizationManagerFactory.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application => core}/config/HtmlSanitizeConfig.kt (96%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{service/post/PostContentFormatter.kt => domain/service/post/DefaultPostContentFormatter.kt} (94%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{infrastructure/springframework/security/LoginUserContextHolder.kt => domain/service/post/PostContentFormatter.kt} (73%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application/service => core/domain/shared}/id/IdGenerateService.kt (86%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application => core}/infrastructure/exposed/ExposedTransaction.kt (78%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application => core}/infrastructure/exposed/QueryMapper.kt (91%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application => core}/infrastructure/exposed/ResultRowMapper.kt (91%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application/service/id => core/infrastructure/other}/SnowflakeIdGenerateService.kt (92%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/{application/service/id => core/infrastructure/other}/TwitterSnowflakeIdGenerateService.kt (94%) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/RegisteredClientRepositoryImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/OAuth2JwtLoginUserContextHolder.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt delete mode 100644 hideout-mastodon/src/main/kotlin/Main.kt diff --git a/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt b/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt index 8ce170eb..8b8ff123 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt @@ -17,7 +17,6 @@ package mastodon.apps import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient import dev.usbharu.owl.producer.api.OwlProducer import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat @@ -30,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.MediaType +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient import org.springframework.security.test.context.support.WithAnonymousUser import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers import org.springframework.test.web.servlet.MockMvc diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt deleted file mode 100644 index 2356b3d4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/AwsConfig.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.s3.S3Client -import java.net.URI - -@Configuration -class AwsConfig { - @Bean - @ConditionalOnProperty("hideout.storage.type", havingValue = "s3") - fun s3Client(awsConfig: S3StorageConfig): S3Client { - return S3Client.builder() - .endpointOverride(URI.create(awsConfig.endpoint)) - .region(Region.of(awsConfig.region)) - .credentialsProvider { AwsBasicCredentials.create(awsConfig.accessKey, awsConfig.secretKey) } - .build() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt deleted file mode 100644 index ac8237f6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/CaptchaConfig.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.usbharu.hideout.application.config - -import org.springframework.boot.context.properties.ConfigurationProperties - -@ConfigurationProperties("hideout.security") -data class CaptchaConfig( - val reCaptchaSiteKey: String?, - val reCaptchaSecretKey: String? -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpClientConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpClientConfig.kt deleted file mode 100644 index 2f783282..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpClientConfig.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import io.ktor.client.* -import io.ktor.client.engine.cio.* -import io.ktor.client.plugins.* -import io.ktor.client.plugins.cache.* -import io.ktor.client.plugins.logging.* -import org.springframework.boot.info.BuildProperties -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class HttpClientConfig { - @Bean - fun httpClient(buildProperties: BuildProperties, applicationConfig: ApplicationConfig): HttpClient = - HttpClient(CIO).config { - install(Logging) { - logger = Logger.DEFAULT - level = LogLevel.ALL - } - install(HttpCache) { - } - expectSuccess = true - install(UserAgent) { - agent = "Hideout/${buildProperties.version} (${applicationConfig.url})" - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt deleted file mode 100644 index 9935c53b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HttpSignatureConfig.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import dev.usbharu.httpsignature.sign.RsaSha256HttpSignatureSigner -import dev.usbharu.httpsignature.verify.DefaultSignatureHeaderParser -import dev.usbharu.httpsignature.verify.RsaSha256HttpSignatureVerifier -import dev.usbharu.httpsignature.verify.SignatureHeaderParser -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class HttpSignatureConfig { - @Bean - fun defaultSignatureHeaderParser(): DefaultSignatureHeaderParser = DefaultSignatureHeaderParser() - - @Bean - fun rsaSha256HttpSignatureVerifier( - signatureHeaderParser: SignatureHeaderParser, - signatureSigner: RsaSha256HttpSignatureSigner - ): RsaSha256HttpSignatureVerifier = RsaSha256HttpSignatureVerifier(signatureHeaderParser, signatureSigner) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MdcXrequestIdFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MdcXrequestIdFilter.kt deleted file mode 100644 index 188251de..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MdcXrequestIdFilter.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import jakarta.servlet.Filter -import jakarta.servlet.FilterChain -import jakarta.servlet.ServletRequest -import jakarta.servlet.ServletResponse -import org.slf4j.MDC -import org.springframework.boot.autoconfigure.security.SecurityProperties -import org.springframework.core.annotation.Order -import org.springframework.stereotype.Component -import java.util.* - -@Component -@Order(SecurityProperties.DEFAULT_FILTER_ORDER - 1) -class MdcXrequestIdFilter : Filter { - override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain) { - val uuid = UUID.randomUUID() - try { - MDC.put(KEY, uuid.toString()) - chain.doFilter(request, response) - } finally { - MDC.remove(KEY) - } - } - - companion object { - private const val KEY = "x-request-id" - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MediaConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MediaConfig.kt deleted file mode 100644 index 43d7cbf9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MediaConfig.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import org.springframework.boot.context.properties.ConfigurationProperties - -@ConfigurationProperties("hideout.media") -data class MediaConfig( - val remoteMediaFileSizeLimit: Long = 3000000L -) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MvcConfigurer.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MvcConfigurer.kt deleted file mode 100644 index 05c1898c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/MvcConfigurer.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.http.converter.HttpMessageConverter -import org.springframework.web.method.support.HandlerMethodArgumentResolver -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer -import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor -import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor - -@Configuration -class MvcConfigurer(private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor) : WebMvcConfigurer { - override fun addArgumentResolvers(resolvers: MutableList) { - resolvers.add(jsonOrFormModelMethodProcessor) - } -} - -@Configuration -class JsonOrFormModelMethodProcessorConfig { - @Bean - fun jsonOrFormModelMethodProcessor(converter: List>): JsonOrFormModelMethodProcessor { - return JsonOrFormModelMethodProcessor( - ServletModelAttributeMethodProcessor(true), - RequestResponseBodyMethodProcessor( - converter - ) - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/OwlConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/OwlConfig.kt deleted file mode 100644 index e5454e23..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/OwlConfig.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.owl.broker.ModuleContext -import dev.usbharu.owl.common.property.* -import dev.usbharu.owl.common.retry.RetryPolicyFactory -import dev.usbharu.owl.producer.api.OWL -import dev.usbharu.owl.producer.api.OwlProducer -import dev.usbharu.owl.producer.defaultimpl.DEFAULT -import dev.usbharu.owl.producer.embedded.EMBEDDED -import dev.usbharu.owl.producer.embedded.EMBEDDED_GRPC -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.context.properties.ConfigurationProperties -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import java.util.* - -@Configuration -class OwlConfig(private val producerConfig: ProducerConfig) { - @Bean - fun producer( - @Autowired(required = false) retryPolicyFactory: RetryPolicyFactory? = null, - @Qualifier("activitypub") objectMapper: ObjectMapper, - ): OwlProducer { - return when (producerConfig.mode) { - ProducerMode.EMBEDDED -> { - OWL(EMBEDDED) { - if (retryPolicyFactory != null) { - this.retryPolicyFactory = retryPolicyFactory - } - if (producerConfig.port != null) { - this.port = producerConfig.port.toString() - } - val moduleContext = ServiceLoader.load(ModuleContext::class.java).firstOrNull() - if (moduleContext != null) { - this.moduleContext = moduleContext - } - this.propertySerializerFactory = CustomPropertySerializerFactory( - setOf( - IntegerPropertySerializer(), - StringPropertyValueSerializer(), - DoublePropertySerializer(), - BooleanPropertySerializer(), - LongPropertySerializer(), - FloatPropertySerializer(), - ObjectPropertySerializer(objectMapper), - ) - ) - } - } - - ProducerMode.GRPC -> { - OWL(EMBEDDED_GRPC) { - } - } - - ProducerMode.EMBEDDED_GRPC -> { - OWL(DEFAULT) { - } - } - } - } -} - -@ConfigurationProperties("hideout.owl.producer") -data class ProducerConfig(val mode: ProducerMode = ProducerMode.EMBEDDED, val port: Int? = null) - -enum class ProducerMode { - GRPC, - EMBEDDED, - EMBEDDED_GRPC -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt deleted file mode 100644 index 0730dfb7..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import com.nimbusds.jose.jwk.JWKSet -import com.nimbusds.jose.jwk.RSAKey -import com.nimbusds.jose.jwk.source.ImmutableJWKSet -import com.nimbusds.jose.jwk.source.JWKSource -import com.nimbusds.jose.proc.SecurityContext -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.UserDetailsImpl -import dev.usbharu.hideout.util.RsaUtil -import jakarta.annotation.PostConstruct -import jakarta.servlet.* -import org.springframework.beans.factory.support.BeanDefinitionRegistry -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.boot.context.properties.ConfigurationProperties -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.core.annotation.Order -import org.springframework.http.HttpMethod.GET -import org.springframework.http.HttpMethod.POST -import org.springframework.http.HttpStatus -import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.authentication.dao.DaoAuthenticationProvider -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.annotation.web.invoke -import org.springframework.security.config.http.SessionCreationPolicy -import org.springframework.security.core.Authentication -import org.springframework.security.core.context.SecurityContextHolderStrategy -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder -import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.security.oauth2.core.AuthorizationGrantType -import org.springframework.security.oauth2.jwt.JwtDecoder -import org.springframework.security.oauth2.server.authorization.OAuth2TokenType -import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration -import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings -import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext -import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer -import org.springframework.security.web.FilterChainProxy -import org.springframework.security.web.SecurityFilterChain -import org.springframework.security.web.authentication.HttpStatusEntryPoint -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer -import org.springframework.security.web.debug.DebugFilter -import org.springframework.security.web.firewall.HttpFirewall -import org.springframework.security.web.firewall.RequestRejectedHandler -import org.springframework.security.web.util.matcher.AnyRequestMatcher -import org.springframework.web.filter.CompositeFilter -import java.io.IOException -import java.security.KeyPairGenerator -import java.security.interfaces.RSAPrivateKey -import java.security.interfaces.RSAPublicKey -import java.util.* - -@EnableWebSecurity(debug = false) -@Configuration -@Suppress("FunctionMaxLength", "TooManyFunctions", "LongMethod") -class SecurityConfig { - - @Bean - fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager? = - authenticationConfiguration.authenticationManager - - @Bean - @Order(1) - fun httpSignatureFilterChain( - http: HttpSecurity, - ): SecurityFilterChain { - http { - securityMatcher("/users/*/posts/*") - authorizeHttpRequests { - authorize(anyRequest, permitAll) - } - exceptionHandling { - authenticationEntryPoint = HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED) - defaultAuthenticationEntryPointFor( - HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - AnyRequestMatcher.INSTANCE - ) - } - sessionManagement { - sessionCreationPolicy = SessionCreationPolicy.STATELESS - } - } - return http.build() - } - - - @Bean - @Order(2) - fun oauth2SecurityFilterChain(http: HttpSecurity): SecurityFilterChain { - OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http) - http { - exceptionHandling { - authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/login") - } - oauth2ResourceServer { - jwt { - } - } - } - return http.build() - } - - @Bean - @Order(5) - fun defaultSecurityFilterChain( - http: HttpSecurity, - ): SecurityFilterChain { - http { - authorizeHttpRequests { - authorize("/error", permitAll) - authorize("/login", permitAll) - authorize(GET, "/.well-known/**", permitAll) - authorize(GET, "/nodeinfo/2.0", permitAll) - - authorize(POST, "/inbox", permitAll) - authorize(POST, "/users/*/inbox", permitAll) - authorize(GET, "/users/*", permitAll) - authorize(GET, "/users/*/posts/*", permitAll) - - authorize("/dev/usbharu/hideout/core/service/auth/sign_up", hasRole("ANONYMOUS")) - authorize(GET, "/files/*", permitAll) - authorize(GET, "/users/*/icon.jpg", permitAll) - authorize(GET, "/users/*/header.jpg", permitAll) - - authorize(anyRequest, authenticated) - } - - oauth2ResourceServer { - jwt { } - } - - formLogin { - } - - csrf { - ignoringRequestMatchers("/users/*/inbox", "/inbox", "/api/v1/apps") - } - - headers { - frameOptions { - sameOrigin = true - } - } - } - return http.build() - } - - @Bean - fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() - - @Bean - @ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "false", matchIfMissing = true) - fun genJwkSource(): JWKSource { - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val generateKeyPair = keyPairGenerator.generateKeyPair() - val rsaPublicKey = generateKeyPair.public as RSAPublicKey - val rsaPrivateKey = generateKeyPair.private as RSAPrivateKey - val rsaKey = RSAKey.Builder(rsaPublicKey).privateKey(rsaPrivateKey).keyID(UUID.randomUUID().toString()).build() - - val jwkSet = JWKSet(rsaKey) - return ImmutableJWKSet(jwkSet) - } - - @Bean - @ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "") - fun loadJwkSource(jwkConfig: JwkConfig): JWKSource { - val rsaKey = RSAKey.Builder(RsaUtil.decodeRsaPublicKey(jwkConfig.publicKey)) - .privateKey(RsaUtil.decodeRsaPrivateKey(jwkConfig.privateKey)).keyID(jwkConfig.keyId).build() - return ImmutableJWKSet(JWKSet(rsaKey)) - } - - @Bean - fun jwtDecoder(jwkSource: JWKSource): JwtDecoder = - OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource) - - @Bean - fun authorizationServerSettings(): AuthorizationServerSettings { - return AuthorizationServerSettings.builder().authorizationEndpoint("/oauth/authorize") - .tokenEndpoint("/oauth/token").tokenRevocationEndpoint("/oauth/revoke").build() - } - - @Bean - fun jwtTokenCustomizer(): OAuth2TokenCustomizer { - return OAuth2TokenCustomizer { context: JwtEncodingContext -> - - if (OAuth2TokenType.ACCESS_TOKEN == context.tokenType && - context.authorization?.authorizationGrantType == AuthorizationGrantType.AUTHORIZATION_CODE - ) { - val userDetailsImpl = context.getPrincipal().principal as UserDetailsImpl - context.claims.claim("uid", userDetailsImpl.id.toString()) - } - } - } - - - // Spring Security 3.2.1 ã«å­˜åœ¨ã™ã‚‹ EnableWebSecurity(debug = true)ã«ã™ã‚‹ã¨ç™ºç”Ÿã™ã‚‹ã‚¨ãƒ©ãƒ¼ã«å¯¾å‡¦ã™ã‚‹ãŸã‚ã®ã‚³ãƒ¼ãƒ‰ - // trueã«ã—ãªã„ã¨ãã¯ã‚³ãƒ¡ãƒ³ãƒˆã‚¢ã‚¦ãƒˆ - - // @Bean - fun beanDefinitionRegistryPostProcessor(): BeanDefinitionRegistryPostProcessor { - return BeanDefinitionRegistryPostProcessor { registry: BeanDefinitionRegistry -> - registry.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME).beanClassName = - CompositeFilterChainProxy::class.java.name - } - } - - @Suppress("ExpressionBodySyntax") - internal class CompositeFilterChainProxy(filters: List) : FilterChainProxy() { - private val doFilterDelegate: Filter - - private val springSecurityFilterChain: FilterChainProxy - - init { - this.doFilterDelegate = createDoFilterDelegate(filters) - this.springSecurityFilterChain = findFilterChainProxy(filters) - } - - override fun afterPropertiesSet() { - springSecurityFilterChain.afterPropertiesSet() - } - - @Throws(IOException::class, ServletException::class) - override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { - doFilterDelegate.doFilter(request, response, chain) - } - - override fun getFilters(url: String): List { - return springSecurityFilterChain.getFilters(url) - } - - override fun getFilterChains(): List { - return springSecurityFilterChain.filterChains - } - - override fun setSecurityContextHolderStrategy(securityContextHolderStrategy: SecurityContextHolderStrategy) { - springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy) - } - - override fun setFilterChainValidator(filterChainValidator: FilterChainValidator) { - springSecurityFilterChain.setFilterChainValidator(filterChainValidator) - } - - override fun setFilterChainDecorator(filterChainDecorator: FilterChainDecorator) { - springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator) - } - - override fun setFirewall(firewall: HttpFirewall) { - springSecurityFilterChain.setFirewall(firewall) - } - - override fun setRequestRejectedHandler(requestRejectedHandler: RequestRejectedHandler) { - springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler) - } - - companion object { - private fun createDoFilterDelegate(filters: List): Filter { - val delegate: CompositeFilter = CompositeFilter() - delegate.setFilters(filters) - return delegate - } - - private fun findFilterChainProxy(filters: List): FilterChainProxy { - for (filter in filters) { - if (filter is FilterChainProxy) { - return filter - } - if (filter is DebugFilter) { - return filter.filterChainProxy - } - } - throw IllegalStateException("Couldn't find FilterChainProxy in $filters") - } - } - } -} - -@ConfigurationProperties("hideout.security.jwt") -@ConditionalOnProperty(name = ["hideout.security.jwt.generate"], havingValue = "") -data class JwkConfig( - val keyId: String, - val publicKey: String, - val privateKey: String, -) - -@Configuration -class PostSecurityConfig( - val auth: AuthenticationManagerBuilder, - val daoAuthenticationProvider: DaoAuthenticationProvider, - val httpSignatureAuthenticationProvider: PreAuthenticatedAuthenticationProvider, -) { - - @PostConstruct - fun config() { - auth.authenticationProvider(daoAuthenticationProvider) - auth.authenticationProvider(httpSignatureAuthenticationProvider) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt deleted file mode 100644 index 8f1b97ce..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/SpringConfig.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.config - -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty -import org.springframework.boot.context.properties.ConfigurationProperties -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.web.filter.CommonsRequestLoggingFilter -import java.net.URL - -@Configuration -class SpringConfig { - - @Autowired - lateinit var config: ApplicationConfig - - @Bean - fun requestLoggingFilter(): CommonsRequestLoggingFilter { - val loggingFilter = CommonsRequestLoggingFilter() - loggingFilter.setIncludeHeaders(true) - loggingFilter.setIncludeClientInfo(true) - loggingFilter.setIncludeQueryString(true) - loggingFilter.setIncludePayload(true) - loggingFilter.setMaxPayloadLength(64000) - return loggingFilter - } -} - -@ConfigurationProperties("hideout") -data class ApplicationConfig( - val url: URL, - val private: Boolean = true, -) - -@ConfigurationProperties("hideout.storage.s3") -@ConditionalOnProperty("hideout.storage.type", havingValue = "s3") -data class S3StorageConfig( - val endpoint: String, - val publicUrl: String, - val bucket: String, - val region: String, - val accessKey: String, - val secretKey: String -) - -/** - * メディアã®ä¿å­˜ã«ãƒ­ãƒ¼ã‚«ãƒ«ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’使用ã™ã‚‹éš›ã®ã‚³ãƒ³ãƒ•ã‚£ã‚° - * - * @property path フォゾンã™ã‚‹å ´æ‰€ã¸ã®ãƒ‘ス。 /ã‹ã‚‰å§‹ã‚ã‚‹ã¨çµ¶å¯¾ãƒ‘スã¨ãªã‚Šã¾ã™ã€‚ - * @property publicUrl 公開用URL çœç•¥å¯èƒ½ 指定ã™ã‚‹ã¨HideoutãŒãƒ•ã‚¡ã‚¤ãƒ«ã‚’é…ä¿¡ã—ãªããªã‚Šã¾ã™ã€‚ - */ -@ConfigurationProperties("hideout.storage.local") -@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) -data class LocalStorageConfig( - val path: String = "files", - val publicUrl: String? -) - -@ConfigurationProperties("hideout.character-limit") -data class CharacterLimit( - val general: General = General(), - val post: Post = Post(), - val account: Account = Account(), - val instance: Instance = Instance() -) { - - data class General( - val url: Int = 1000, - val domain: Int = 1000, - val publicKey: Int = 10000, - val privateKey: Int = 10000 - ) - - data class Post( - val text: Int = 3000, - val overview: Int = 3000 - ) - - data class Account( - val id: Int = 300, - val name: Int = 300, - val description: Int = 10000 - ) - - data class Instance( - val name: Int = 600, - val description: Int = 10000 - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt deleted file mode 100644 index 5df20246..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/external/OwlProducerRunner.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.external - -import dev.usbharu.owl.common.task.TaskDefinition -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.springframework.beans.factory.DisposableBean -import org.springframework.boot.ApplicationArguments -import org.springframework.boot.ApplicationRunner -import org.springframework.stereotype.Component - -@Component -class OwlProducerRunner(private val owlProducer: OwlProducer, private val taskDefinitions: List>) : - ApplicationRunner, DisposableBean { - override fun run(args: ApplicationArguments?) { - runBlocking { - owlProducer.start() - taskDefinitions.forEach { taskDefinition -> owlProducer.registerTask(taskDefinition) } - } - } - - override fun destroy() { - runBlocking { - owlProducer.stop() - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt deleted file mode 100644 index 1f8dad8a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.exposed - -import org.jetbrains.exposed.sql.* - -fun Query.withPagination(page: Page, exp: ExpressionWithColumnType): PaginationList { - page.limit?.let { limit(it) } - val resultRows = if (page.minId != null) { - page.maxId?.let { it: Long -> andWhere { exp.less(it) } } - andWhere { exp.greater(page.minId!!) } - reversed() - } else { - page.maxId?.let { andWhere { exp.less(it) } } - page.sinceId?.let { andWhere { exp.greater(it) } } - orderBy(exp, SortOrder.DESC) - toList() - } - - return PaginationList(resultRows, resultRows.firstOrNull()?.getOrNull(exp), resultRows.lastOrNull()?.getOrNull(exp)) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt deleted file mode 100644 index c6178261..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.exposed - -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 { - @Suppress("FunctionMinLength") - 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 - ) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt deleted file mode 100644 index d796e48c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.exposed - -class PaginationList(list: List, val next: ID?, val prev: ID?) : List by list - -fun PaginationList.toHttpHeader( - nextBlock: (string: String) -> String, - prevBlock: (string: String) -> String -): String? { - val mutableListOf = mutableListOf() - if (next != null) { - mutableListOf.add("<${nextBlock(this.next.toString())}>; rel=\"next\"") - } - if (prev != null) { - mutableListOf.add("<${prevBlock(this.prev.toString())}>; rel=\"prev\"") - } - - if (mutableListOf.isEmpty()) { - return null - } - - return mutableListOf.joinToString(", ") -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/springframework/RoleHierarchyAuthorizationManagerFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/springframework/RoleHierarchyAuthorizationManagerFactory.kt deleted file mode 100644 index 99976547..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/springframework/RoleHierarchyAuthorizationManagerFactory.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.application.infrastructure.springframework - -import org.springframework.security.access.hierarchicalroles.RoleHierarchy -import org.springframework.security.authorization.AuthorityAuthorizationManager -import org.springframework.security.authorization.AuthorizationManager -import org.springframework.security.web.access.intercept.RequestAuthorizationContext -import org.springframework.stereotype.Component - -@Component -class RoleHierarchyAuthorizationManagerFactory(private val roleHierarchy: RoleHierarchy) { - fun hasScope(role: String): AuthorizationManager { - val hasAuthority = AuthorityAuthorizationManager.hasAuthority("SCOPE_$role") - hasAuthority.setRoleHierarchy(roleHierarchy) - return hasAuthority - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index f924330e..e52af918 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.core.application.actor import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository @@ -26,6 +25,7 @@ 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.actor.local.LocalActorDomainService import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HtmlSanitizeConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/HtmlSanitizeConfig.kt similarity index 96% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HtmlSanitizeConfig.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/HtmlSanitizeConfig.kt index 10d5b076..e55bc8fe 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/config/HtmlSanitizeConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/HtmlSanitizeConfig.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.config +package dev.usbharu.hideout.core.config import org.owasp.html.HtmlPolicyBuilder import org.owasp.html.PolicyFactory diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt similarity index 94% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt index 5819c47b..9054f12b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/service/post/PostContentFormatter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.service.post +package dev.usbharu.hideout.core.domain.service.post import org.jsoup.Jsoup import org.jsoup.nodes.Document @@ -24,11 +24,6 @@ import org.jsoup.select.Elements import org.owasp.html.PolicyFactory import org.springframework.stereotype.Service - -interface PostContentFormatter { - fun format(content: String): FormattedPostContent -} - @Service class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : PostContentFormatter { override fun format(content: String): FormattedPostContent { @@ -101,9 +96,4 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po } } } -} - -data class FormattedPostContent( - val html: String, - val content: String, -) \ No newline at end of file +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/LoginUserContextHolder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt similarity index 73% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/LoginUserContextHolder.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt index fc6a3c42..a0004b39 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/LoginUserContextHolder.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt @@ -14,10 +14,14 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.infrastructure.springframework.security +package dev.usbharu.hideout.core.domain.service.post -interface LoginUserContextHolder { - fun getLoginUserId(): Long - fun getLoginUserIdOrNull(): Long? +interface PostContentFormatter { + fun format(content: String): FormattedPostContent } + +data class FormattedPostContent( + val html: String, + val content: String, +) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/IdGenerateService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/id/IdGenerateService.kt similarity index 86% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/IdGenerateService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/id/IdGenerateService.kt index ef2686e8..91991d71 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/IdGenerateService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/id/IdGenerateService.kt @@ -14,11 +14,8 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.service.id +package dev.usbharu.hideout.core.domain.shared.id -import org.springframework.stereotype.Service - -@Service interface IdGenerateService { suspend fun generateId(): Long } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt index f1c99847..8b8e46e8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorQueryMapper.kt @@ -16,8 +16,6 @@ package dev.usbharu.hideout.core.infrastructure.exposed -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt index a75c148d..639bd216 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt @@ -16,7 +16,6 @@ package dev.usbharu.hideout.core.infrastructure.exposed -import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper 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 diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ExposedTransaction.kt similarity index 78% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ExposedTransaction.kt index 8e380cb6..7dc419f3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedTransaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ExposedTransaction.kt @@ -14,28 +14,27 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.infrastructure.exposed +package dev.usbharu.hideout.core.infrastructure.exposed import dev.usbharu.hideout.core.application.shared.Transaction -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.slf4j.MDCContext import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger import org.jetbrains.exposed.sql.addLogger import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction -import org.jetbrains.exposed.sql.transactions.transaction -import org.springframework.stereotype.Service +import org.springframework.stereotype.Component import java.sql.Connection -@Service +@Component class ExposedTransaction : Transaction { override suspend fun transaction(block: suspend () -> T): T { - return transaction(transactionIsolation = Connection.TRANSACTION_READ_COMMITTED) { + return newSuspendedTransaction( + transactionIsolation = Connection.TRANSACTION_READ_COMMITTED, + context = MDCContext() + ) { debug = true warnLongQueriesDuration = 1000 addLogger(Slf4jSqlDebugLogger) - runBlocking(MDCContext()) { - block() - } + block() } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/QueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/QueryMapper.kt similarity index 91% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/QueryMapper.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/QueryMapper.kt index 078fdd65..a817b9b2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/QueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/QueryMapper.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.infrastructure.exposed +package dev.usbharu.hideout.core.infrastructure.exposed import org.jetbrains.exposed.sql.Query diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ResultRowMapper.kt similarity index 91% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ResultRowMapper.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ResultRowMapper.kt index cc7737ff..7f4b3261 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ResultRowMapper.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.infrastructure.exposed +package dev.usbharu.hideout.core.infrastructure.exposed import org.jetbrains.exposed.sql.ResultRow diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index 466cf138..9b16788f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -1,10 +1,10 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository -import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper import dev.usbharu.hideout.core.domain.model.actor.* import dev.usbharu.hideout.core.domain.model.shared.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 import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.javatime.timestamp diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index 1273b68c..3e610e53 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -17,10 +17,10 @@ package dev.usbharu.hideout.core.infrastructure.factory import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.service.id.IdGenerateService 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.shared.id.IdGenerateService import org.springframework.stereotype.Component import java.net.URI import java.time.Instant diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt index b901f6a6..7a18fa29 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.infrastructure.factory import dev.usbharu.hideout.core.domain.model.post.PostContent -import dev.usbharu.hideout.core.service.post.PostContentFormatter +import dev.usbharu.hideout.core.domain.service.post.PostContentFormatter import org.springframework.stereotype.Component @Component diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index f28dd0d1..822035d9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -17,7 +17,6 @@ package dev.usbharu.hideout.core.infrastructure.factory import dev.usbharu.hideout.application.config.ApplicationConfig -import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorName import dev.usbharu.hideout.core.domain.model.media.MediaId @@ -25,6 +24,7 @@ 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.PostOverview import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import org.springframework.stereotype.Component import java.net.URI import java.time.Instant diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt deleted file mode 100644 index 5219c983..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/httpsignature/HttpRequestMixIn.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.httpsignature - -import com.fasterxml.jackson.annotation.JsonSubTypes -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import java.net.URL - -@JsonDeserialize(using = HttpRequestDeserializer::class) -@JsonSubTypes -@Suppress("UnnecessaryAbstractClass") -abstract class HttpRequestMixIn - -class HttpRequestDeserializer : JsonDeserializer() { - override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): HttpRequest { - val readTree: JsonNode = p.codec.readTree(p) - - return HttpRequest( - URL(readTree["url"].textValue()), - HttpHeaders(emptyMap()), - HttpMethod.valueOf(readTree["method"].textValue()) - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/SnowflakeIdGenerateService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/SnowflakeIdGenerateService.kt similarity index 92% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/SnowflakeIdGenerateService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/SnowflakeIdGenerateService.kt index d0748acb..3b82b1cc 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/SnowflakeIdGenerateService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/SnowflakeIdGenerateService.kt @@ -14,15 +14,16 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.service.id +package dev.usbharu.hideout.core.infrastructure.other +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import kotlinx.coroutines.delay import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import java.time.Instant @Suppress("MagicNumber") -class SnowflakeIdGenerateService(private val baseTime: Long) : IdGenerateService { +open class SnowflakeIdGenerateService(private val baseTime: Long) : IdGenerateService { var lastTimeStamp: Long = -1 var sequenceId: Int = 0 val mutex = Mutex() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateService.kt similarity index 94% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateService.kt index a23d92d5..dcefc995 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/application/service/id/TwitterSnowflakeIdGenerateService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateService.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.application.service.id +package dev.usbharu.hideout.core.infrastructure.other import org.springframework.context.annotation.Primary import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt deleted file mode 100644 index 7c5126e9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationConsentService.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import dev.usbharu.hideout.core.application.shared.Transaction -import kotlinx.coroutines.runBlocking -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository -import org.springframework.stereotype.Service -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent as AuthorizationConsent - -@Service -class ExposedOAuth2AuthorizationConsentService( - private val registeredClientRepository: RegisteredClientRepository, - private val transaction: Transaction, -) : - OAuth2AuthorizationConsentService { - - override fun save(authorizationConsent: AuthorizationConsent?): Unit = runBlocking { - requireNotNull(authorizationConsent) - transaction.transaction { - val singleOrNull = - OAuth2AuthorizationConsent.selectAll().where { - OAuth2AuthorizationConsent.registeredClientId - .eq(authorizationConsent.registeredClientId) - .and(OAuth2AuthorizationConsent.principalName.eq(authorizationConsent.principalName)) - } - .singleOrNull() - if (singleOrNull == null) { - OAuth2AuthorizationConsent.insert { - it[registeredClientId] = authorizationConsent.registeredClientId - it[principalName] = authorizationConsent.principalName - it[authorities] = authorizationConsent.authorities.joinToString(",") - } - } - } - } - - override fun remove(authorizationConsent: AuthorizationConsent?) { - if (authorizationConsent == null) { - return - } - OAuth2AuthorizationConsent.deleteWhere { - registeredClientId eq authorizationConsent.registeredClientId and (principalName eq principalName) - } - } - - override fun findById(registeredClientId: String?, principalName: String?): AuthorizationConsent? = runBlocking { - requireNotNull(registeredClientId) - requireNotNull(principalName) - transaction.transaction { - OAuth2AuthorizationConsent.selectAll().where { - (OAuth2AuthorizationConsent.registeredClientId eq registeredClientId) - .and(OAuth2AuthorizationConsent.principalName eq principalName) - } - .singleOrNull()?.toAuthorizationConsent() - } - } - - fun ResultRow.toAuthorizationConsent(): AuthorizationConsent { - val registeredClientId = this[OAuth2AuthorizationConsent.registeredClientId] - registeredClientRepository.findById(registeredClientId) - - val principalName = this[OAuth2AuthorizationConsent.principalName] - val builder = AuthorizationConsent.withId(registeredClientId, principalName) - - this[OAuth2AuthorizationConsent.authorities].split(",").forEach { - builder.authority(SimpleGrantedAuthority(it)) - } - - return builder.build() - } -} - -object OAuth2AuthorizationConsent : Table("oauth2_authorization_consent") { - val registeredClientId: Column = varchar("registered_client_id", 100) - val principalName: Column = varchar("principal_name", 200) - val authorities: Column = varchar("authorities", 1000) - override val primaryKey: PrimaryKey = PrimaryKey(registeredClientId, principalName) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt deleted file mode 100644 index b6689829..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.core.application.shared.Transaction -import kotlinx.coroutines.runBlocking -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.timestamp -import org.springframework.security.jackson2.CoreJackson2Module -import org.springframework.security.jackson2.SecurityJackson2Modules -import org.springframework.security.oauth2.core.* -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames -import org.springframework.security.oauth2.core.oidc.OidcIdToken -import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames -import org.springframework.security.oauth2.server.authorization.OAuth2Authorization -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService -import org.springframework.security.oauth2.server.authorization.OAuth2TokenType -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository -import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class ExposedOAuth2AuthorizationService( - private val registeredClientRepository: RegisteredClientRepository, - private val transaction: Transaction, -) : - OAuth2AuthorizationService { - - @Suppress("LongMethod", "CyclomaticComplexMethod") - override fun save(authorization: OAuth2Authorization?): Unit = runBlocking { - requireNotNull(authorization) - transaction.transaction { - val singleOrNull = Authorization.selectAll().where { Authorization.id eq authorization.id }.singleOrNull() - if (singleOrNull == null) { - val authorizationCodeToken = authorization.getToken(OAuth2AuthorizationCode::class.java) - val accessToken = authorization.getToken(OAuth2AccessToken::class.java) - val refreshToken = authorization.getToken(OAuth2RefreshToken::class.java) - val oidcIdToken = authorization.getToken(OidcIdToken::class.java) - val userCode = authorization.getToken(OAuth2UserCode::class.java) - val deviceCode = authorization.getToken(OAuth2DeviceCode::class.java) - Authorization.insert { - it[id] = authorization.id - it[registeredClientId] = authorization.registeredClientId - it[principalName] = authorization.principalName - it[authorizationGrantType] = authorization.authorizationGrantType.value - it[authorizedScopes] = - authorization.authorizedScopes.joinToString(",").takeIf { s -> s.isNotEmpty() } - it[attributes] = mapToJson(authorization.attributes) - it[state] = authorization.getAttribute(OAuth2ParameterNames.STATE) - it[authorizationCodeValue] = authorizationCodeToken?.token?.tokenValue - it[authorizationCodeIssuedAt] = authorizationCodeToken?.token?.issuedAt - it[authorizationCodeExpiresAt] = authorizationCodeToken?.token?.expiresAt - it[authorizationCodeMetadata] = - authorizationCodeToken?.metadata?.let { it1 -> mapToJson(it1) } - it[accessTokenValue] = accessToken?.token?.tokenValue - it[accessTokenIssuedAt] = accessToken?.token?.issuedAt - it[accessTokenExpiresAt] = accessToken?.token?.expiresAt - it[accessTokenMetadata] = accessToken?.metadata?.let { it1 -> mapToJson(it1) } - it[accessTokenType] = accessToken?.token?.tokenType?.value - it[accessTokenScopes] = - accessToken?.run { token.scopes.joinToString(",").takeIf { s -> s.isNotEmpty() } } - it[refreshTokenValue] = refreshToken?.token?.tokenValue - it[refreshTokenIssuedAt] = refreshToken?.token?.issuedAt - it[refreshTokenExpiresAt] = refreshToken?.token?.expiresAt - it[refreshTokenMetadata] = refreshToken?.metadata?.let { it1 -> mapToJson(it1) } - it[oidcIdTokenValue] = oidcIdToken?.token?.tokenValue - it[oidcIdTokenIssuedAt] = oidcIdToken?.token?.issuedAt - it[oidcIdTokenExpiresAt] = oidcIdToken?.token?.expiresAt - it[oidcIdTokenMetadata] = oidcIdToken?.metadata?.let { it1 -> mapToJson(it1) } - it[userCodeValue] = userCode?.token?.tokenValue - it[userCodeIssuedAt] = userCode?.token?.issuedAt - it[userCodeExpiresAt] = userCode?.token?.expiresAt - it[userCodeMetadata] = userCode?.metadata?.let { it1 -> mapToJson(it1) } - it[deviceCodeValue] = deviceCode?.token?.tokenValue - it[deviceCodeIssuedAt] = deviceCode?.token?.issuedAt - it[deviceCodeExpiresAt] = deviceCode?.token?.expiresAt - it[deviceCodeMetadata] = deviceCode?.metadata?.let { it1 -> mapToJson(it1) } - } - } else { - val authorizationCodeToken = authorization.getToken(OAuth2AuthorizationCode::class.java) - val accessToken = authorization.getToken(OAuth2AccessToken::class.java) - val refreshToken = authorization.getToken(OAuth2RefreshToken::class.java) - val oidcIdToken = authorization.getToken(OidcIdToken::class.java) - val userCode = authorization.getToken(OAuth2UserCode::class.java) - val deviceCode = authorization.getToken(OAuth2DeviceCode::class.java) - Authorization.update({ Authorization.id eq authorization.id }) { - it[registeredClientId] = authorization.registeredClientId - it[principalName] = authorization.principalName - it[authorizationGrantType] = authorization.authorizationGrantType.value - it[authorizedScopes] = - authorization.authorizedScopes.joinToString(",").takeIf { s -> s.isNotEmpty() } - it[attributes] = mapToJson(authorization.attributes) - it[state] = authorization.getAttribute(OAuth2ParameterNames.STATE) - it[authorizationCodeValue] = authorizationCodeToken?.token?.tokenValue - it[authorizationCodeIssuedAt] = authorizationCodeToken?.token?.issuedAt - it[authorizationCodeExpiresAt] = authorizationCodeToken?.token?.expiresAt - it[authorizationCodeMetadata] = - authorizationCodeToken?.metadata?.let { it1 -> mapToJson(it1) } - it[accessTokenValue] = accessToken?.token?.tokenValue - it[accessTokenIssuedAt] = accessToken?.token?.issuedAt - it[accessTokenExpiresAt] = accessToken?.token?.expiresAt - it[accessTokenMetadata] = accessToken?.metadata?.let { it1 -> mapToJson(it1) } - it[accessTokenType] = accessToken?.run { token.tokenType.value } - it[accessTokenScopes] = - accessToken?.run { token.scopes.joinToString(",").takeIf { s -> s.isNotEmpty() } } - it[refreshTokenValue] = refreshToken?.token?.tokenValue - it[refreshTokenIssuedAt] = refreshToken?.token?.issuedAt - it[refreshTokenExpiresAt] = refreshToken?.token?.expiresAt - it[refreshTokenMetadata] = refreshToken?.metadata?.let { it1 -> mapToJson(it1) } - it[oidcIdTokenValue] = oidcIdToken?.token?.tokenValue - it[oidcIdTokenIssuedAt] = oidcIdToken?.token?.issuedAt - it[oidcIdTokenExpiresAt] = oidcIdToken?.token?.expiresAt - it[oidcIdTokenMetadata] = oidcIdToken?.metadata?.let { it1 -> mapToJson(it1) } - it[userCodeValue] = userCode?.token?.tokenValue - it[userCodeIssuedAt] = userCode?.token?.issuedAt - it[userCodeExpiresAt] = userCode?.token?.expiresAt - it[userCodeMetadata] = userCode?.metadata?.let { it1 -> mapToJson(it1) } - it[deviceCodeValue] = deviceCode?.token?.tokenValue - it[deviceCodeIssuedAt] = deviceCode?.token?.issuedAt - it[deviceCodeExpiresAt] = deviceCode?.token?.expiresAt - it[deviceCodeMetadata] = deviceCode?.metadata?.let { it1 -> mapToJson(it1) } - } - } - } - } - - override fun remove(authorization: OAuth2Authorization?) { - if (authorization == null) { - return - } - Authorization.deleteWhere { id eq authorization.id } - } - - override fun findById(id: String?): OAuth2Authorization? { - if (id == null) { - return null - } - return Authorization.selectAll().where { Authorization.id eq id }.singleOrNull()?.toAuthorization() - } - - override fun findByToken(token: String?, tokenType: OAuth2TokenType?): OAuth2Authorization? = runBlocking { - requireNotNull(token) - transaction.transaction { - when (tokenType?.value) { - null -> { - Authorization.selectAll().where { Authorization.authorizationCodeValue eq token }.orWhere { - Authorization.accessTokenValue eq token - }.orWhere { - Authorization.oidcIdTokenValue eq token - }.orWhere { - Authorization.refreshTokenValue eq token - }.orWhere { - Authorization.userCodeValue eq token - }.orWhere { - Authorization.deviceCodeValue eq token - } - } - - OAuth2ParameterNames.STATE -> { - Authorization.selectAll().where { Authorization.state eq token } - } - - OAuth2ParameterNames.CODE -> { - Authorization.selectAll().where { Authorization.authorizationCodeValue eq token } - } - - OAuth2ParameterNames.ACCESS_TOKEN -> { - Authorization.selectAll().where { Authorization.accessTokenValue eq token } - } - - OidcParameterNames.ID_TOKEN -> { - Authorization.selectAll().where { Authorization.oidcIdTokenValue eq token } - } - - OAuth2ParameterNames.REFRESH_TOKEN -> { - Authorization.selectAll().where { Authorization.refreshTokenValue eq token } - } - - OAuth2ParameterNames.USER_CODE -> { - Authorization.selectAll().where { Authorization.userCodeValue eq token } - } - - OAuth2ParameterNames.DEVICE_CODE -> { - Authorization.selectAll().where { Authorization.deviceCodeValue eq token } - } - - else -> { - null - } - }?.singleOrNull()?.toAuthorization() - } - } - - @Suppress("LongMethod", "CyclomaticComplexMethod", "CastToNullableType", "UNCHECKED_CAST") - fun ResultRow.toAuthorization(): OAuth2Authorization { - val registeredClientId = this[Authorization.registeredClientId] - - val registeredClient = registeredClientRepository.findById(registeredClientId) - - val builder = OAuth2Authorization.withRegisteredClient(registeredClient) - val id = this[Authorization.id] - val principalName = this[Authorization.principalName] - val authorizationGrantType = this[Authorization.authorizationGrantType] - val authorizedScopes = this[Authorization.authorizedScopes]?.split(",").orEmpty().toSet() - val attributes = this[Authorization.attributes]?.let { jsonToMap(it) }.orEmpty() - - builder.id(id).principalName(principalName) - .authorizationGrantType(AuthorizationGrantType(authorizationGrantType)).authorizedScopes(authorizedScopes) - .attributes { it.putAll(attributes) } - - val state = this[Authorization.state].orEmpty() - if (state.isNotBlank()) { - builder.attribute(OAuth2ParameterNames.STATE, state) - } - - val authorizationCodeValue = this[Authorization.authorizationCodeValue].orEmpty() - if (authorizationCodeValue.isNotBlank()) { - val authorizationCodeIssuedAt = this[Authorization.authorizationCodeIssuedAt] - val authorizationCodeExpiresAt = this[Authorization.authorizationCodeExpiresAt] - val authorizationCodeMetadata = this[Authorization.authorizationCodeMetadata]?.let { - jsonToMap( - it - ) - }.orEmpty() - val oAuth2AuthorizationCode = - OAuth2AuthorizationCode(authorizationCodeValue, authorizationCodeIssuedAt, authorizationCodeExpiresAt) - builder.token(oAuth2AuthorizationCode) { - it.putAll(authorizationCodeMetadata) - } - } - - val accessTokenValue = this[Authorization.accessTokenValue].orEmpty() - if (accessTokenValue.isNotBlank()) { - val accessTokenIssuedAt = this[Authorization.accessTokenIssuedAt] - val accessTokenExpiresAt = this[Authorization.accessTokenExpiresAt] - val accessTokenMetadata = - this[Authorization.accessTokenMetadata]?.let { jsonToMap(it) }.orEmpty() - val accessTokenType = - if (this[Authorization.accessTokenType].equals(OAuth2AccessToken.TokenType.BEARER.value, true)) { - OAuth2AccessToken.TokenType.BEARER - } else { - null - } - - val accessTokenScope = this[Authorization.accessTokenScopes]?.split(",").orEmpty().toSet() - - val oAuth2AccessToken = OAuth2AccessToken( - accessTokenType, - accessTokenValue, - accessTokenIssuedAt, - accessTokenExpiresAt, - accessTokenScope - ) - - builder.token(oAuth2AccessToken) { it.putAll(accessTokenMetadata) } - } - - val oidcIdTokenValue = this[Authorization.oidcIdTokenValue].orEmpty() - if (oidcIdTokenValue.isNotBlank()) { - val oidcTokenIssuedAt = this[Authorization.oidcIdTokenIssuedAt] - val oidcTokenExpiresAt = this[Authorization.oidcIdTokenExpiresAt] - val oidcTokenMetadata = - this[Authorization.oidcIdTokenMetadata]?.let { jsonToMap(it) }.orEmpty() - - val oidcIdToken = OidcIdToken( - oidcIdTokenValue, - oidcTokenIssuedAt, - oidcTokenExpiresAt, - oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) - as MutableMap? - ) - - builder.token(oidcIdToken) { it.putAll(oidcTokenMetadata) } - } - - val refreshTokenValue = this[Authorization.refreshTokenValue].orEmpty() - if (refreshTokenValue.isNotBlank()) { - val refreshTokenIssuedAt = this[Authorization.refreshTokenIssuedAt] - val refreshTokenExpiresAt = this[Authorization.refreshTokenExpiresAt] - val refreshTokenMetadata = - this[Authorization.refreshTokenMetadata]?.let { jsonToMap(it) }.orEmpty() - - val oAuth2RefreshToken = OAuth2RefreshToken(refreshTokenValue, refreshTokenIssuedAt, refreshTokenExpiresAt) - - builder.token(oAuth2RefreshToken) { it.putAll(refreshTokenMetadata) } - } - - val userCodeValue = this[Authorization.userCodeValue].orEmpty() - if (userCodeValue.isNotBlank()) { - val userCodeIssuedAt = this[Authorization.userCodeIssuedAt] - val userCodeExpiresAt = this[Authorization.userCodeExpiresAt] - val userCodeMetadata = - this[Authorization.userCodeMetadata]?.let { jsonToMap(it) }.orEmpty() - val oAuth2UserCode = OAuth2UserCode(userCodeValue, userCodeIssuedAt, userCodeExpiresAt) - builder.token(oAuth2UserCode) { it.putAll(userCodeMetadata) } - } - - val deviceCodeValue = this[Authorization.deviceCodeValue].orEmpty() - if (deviceCodeValue.isNotBlank()) { - val deviceCodeIssuedAt = this[Authorization.deviceCodeIssuedAt] - val deviceCodeExpiresAt = this[Authorization.deviceCodeExpiresAt] - val deviceCodeMetadata = - this[Authorization.deviceCodeMetadata]?.let { jsonToMap(it) }.orEmpty() - - val oAuth2DeviceCode = OAuth2DeviceCode(deviceCodeValue, deviceCodeIssuedAt, deviceCodeExpiresAt) - builder.token(oAuth2DeviceCode) { it.putAll(deviceCodeMetadata) } - } - - return builder.build() - } - - private fun mapToJson(map: Map<*, *>): String = objectMapper.writeValueAsString(map) - - private fun jsonToMap(json: String): Map = objectMapper.readValue(json) - - companion object { - val objectMapper: ObjectMapper = ObjectMapper() - - init { - - val classLoader = ExposedOAuth2AuthorizationService::class.java.classLoader - val modules = SecurityJackson2Modules.getModules(classLoader) - objectMapper.registerModules(JavaTimeModule()) - objectMapper.registerModules(modules) - objectMapper.registerModules(OAuth2AuthorizationServerJackson2Module()) - objectMapper.registerModules(CoreJackson2Module()) - objectMapper.addMixIn(UserDetailsImpl::class.java, UserDetailsMixin::class.java) - } - } -} - -object Authorization : Table("application_authorization") { - val id: Column = varchar("id", 255) - val registeredClientId: Column = varchar("registered_client_id", 255) - val principalName: Column = varchar("principal_name", 255) - val authorizationGrantType: Column = varchar("authorization_grant_type", 255) - val authorizedScopes: Column = varchar("authorized_scopes", 1000).nullable().default(null) - val attributes: Column = varchar("attributes", 4000).nullable().default(null) - val state: Column = varchar("state", 500).nullable().default(null) - val authorizationCodeValue: Column = varchar("authorization_code_value", 4000).nullable().default(null) - val authorizationCodeIssuedAt: Column = timestamp("authorization_code_issued_at").nullable().default(null) - val authorizationCodeExpiresAt: Column = timestamp("authorization_code_expires_at").nullable().default( - null - ) - val authorizationCodeMetadata: Column = varchar("authorization_code_metadata", 2000).nullable().default( - null - ) - val accessTokenValue: Column = varchar("access_token_value", 4000).nullable().default(null) - val accessTokenIssuedAt: Column = timestamp("access_token_issued_at").nullable().default(null) - val accessTokenExpiresAt: Column = timestamp("access_token_expires_at").nullable().default(null) - val accessTokenMetadata: Column = varchar("access_token_metadata", 2000).nullable().default(null) - val accessTokenType: Column = varchar("access_token_type", 255).nullable().default(null) - val accessTokenScopes: Column = varchar("access_token_scopes", 1000).nullable().default(null) - val refreshTokenValue: Column = varchar("refresh_token_value", 4000).nullable().default(null) - val refreshTokenIssuedAt: Column = timestamp("refresh_token_issued_at").nullable().default(null) - val refreshTokenExpiresAt: Column = timestamp("refresh_token_expires_at").nullable().default(null) - val refreshTokenMetadata: Column = varchar("refresh_token_metadata", 2000).nullable().default(null) - val oidcIdTokenValue: Column = varchar("oidc_id_token_value", 4000).nullable().default(null) - val oidcIdTokenIssuedAt: Column = timestamp("oidc_id_token_issued_at").nullable().default(null) - val oidcIdTokenExpiresAt: Column = timestamp("oidc_id_token_expires_at").nullable().default(null) - val oidcIdTokenMetadata: Column = varchar("oidc_id_token_metadata", 2000).nullable().default(null) - val oidcIdTokenClaims: Column = varchar("oidc_id_token_claims", 2000).nullable().default(null) - val userCodeValue: Column = varchar("user_code_value", 4000).nullable().default(null) - val userCodeIssuedAt: Column = timestamp("user_code_issued_at").nullable().default(null) - val userCodeExpiresAt: Column = timestamp("user_code_expires_at").nullable().default(null) - val userCodeMetadata: Column = varchar("user_code_metadata", 2000).nullable().default(null) - val deviceCodeValue: Column = varchar("device_code_value", 4000).nullable().default(null) - val deviceCodeIssuedAt: Column = timestamp("device_code_issued_at").nullable().default(null) - val deviceCodeExpiresAt: Column = timestamp("device_code_expires_at").nullable().default(null) - val deviceCodeMetadata: Column = varchar("device_code_metadata", 2000).nullable().default(null) - - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/RegisteredClientRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/RegisteredClientRepositoryImpl.kt deleted file mode 100644 index 68a3177d..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/RegisteredClientRepositoryImpl.kt +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient.clientId -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient.clientSettings -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.RegisteredClient.tokenSettings -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.javatime.CurrentTimestamp -import org.jetbrains.exposed.sql.javatime.timestamp -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.security.jackson2.SecurityJackson2Modules -import org.springframework.security.oauth2.core.AuthorizationGrantType -import org.springframework.security.oauth2.core.ClientAuthenticationMethod -import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository -import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module -import org.springframework.security.oauth2.server.authorization.settings.ClientSettings -import org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames -import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat -import org.springframework.security.oauth2.server.authorization.settings.TokenSettings -import org.springframework.stereotype.Repository -import org.springframework.transaction.annotation.Transactional -import java.time.Instant -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient as SpringRegisteredClient - -@Repository -class RegisteredClientRepositoryImpl : RegisteredClientRepository { - - override fun save(registeredClient: SpringRegisteredClient?) { - requireNotNull(registeredClient) - val singleOrNull = - RegisteredClient.selectAll().where { RegisteredClient.id eq registeredClient.id }.singleOrNull() - if (singleOrNull == null) { - RegisteredClient.insert { - it[id] = registeredClient.id - it[clientId] = registeredClient.clientId - it[clientIdIssuedAt] = registeredClient.clientIdIssuedAt ?: Instant.now() - it[clientSecret] = registeredClient.clientSecret - it[clientSecretExpiresAt] = registeredClient.clientSecretExpiresAt - it[clientName] = registeredClient.clientName - it[clientAuthenticationMethods] = - registeredClient.clientAuthenticationMethods.joinToString(",") { method -> method.value } - it[authorizationGrantTypes] = - registeredClient.authorizationGrantTypes.joinToString(",") { type -> type.value } - it[redirectUris] = registeredClient.redirectUris.joinToString(",") - it[postLogoutRedirectUris] = registeredClient.postLogoutRedirectUris.joinToString(",") - it[scopes] = registeredClient.scopes.joinToString(",") - it[clientSettings] = mapToJson(registeredClient.clientSettings.settings) - it[tokenSettings] = mapToJson(registeredClient.tokenSettings.settings) - } - } else { - RegisteredClient.update({ RegisteredClient.id eq registeredClient.id }) { - it[clientId] = registeredClient.clientId - it[clientIdIssuedAt] = registeredClient.clientIdIssuedAt ?: Instant.now() - it[clientSecret] = registeredClient.clientSecret - it[clientSecretExpiresAt] = registeredClient.clientSecretExpiresAt - it[clientName] = registeredClient.clientName - it[clientAuthenticationMethods] = registeredClient.clientAuthenticationMethods.joinToString(",") - it[authorizationGrantTypes] = registeredClient.authorizationGrantTypes.joinToString(",") - it[redirectUris] = registeredClient.redirectUris.joinToString(",") - it[postLogoutRedirectUris] = registeredClient.postLogoutRedirectUris.joinToString(",") - it[scopes] = registeredClient.scopes.joinToString(",") - it[clientSettings] = mapToJson(registeredClient.clientSettings.settings) - it[tokenSettings] = mapToJson(registeredClient.tokenSettings.settings) - } - } - } - - override fun findById(id: String?): SpringRegisteredClient? { - if (id == null) { - return null - } - return RegisteredClient.selectAll().where { RegisteredClient.id eq id }.singleOrNull()?.toRegisteredClient() - } - - @Transactional - override fun findByClientId(clientId: String?): SpringRegisteredClient? { - if (clientId == null) { - return null - } - val toRegisteredClient = - RegisteredClient.selectAll().where { RegisteredClient.clientId eq clientId }.singleOrNull() - ?.toRegisteredClient() - LOGGER.trace("findByClientId: {}", toRegisteredClient) - return toRegisteredClient - } - - private fun mapToJson(map: Map<*, *>): String = objectMapper.writeValueAsString(map) - - private fun jsonToMap(json: String): Map = objectMapper.readValue(json) - - @Suppress("CyclomaticComplexMethod") - fun ResultRow.toRegisteredClient(): SpringRegisteredClient { - fun resolveClientAuthenticationMethods(string: String): ClientAuthenticationMethod { - return when (string) { - ClientAuthenticationMethod.CLIENT_SECRET_BASIC.value -> ClientAuthenticationMethod.CLIENT_SECRET_BASIC - ClientAuthenticationMethod.CLIENT_SECRET_JWT.value -> ClientAuthenticationMethod.CLIENT_SECRET_JWT - ClientAuthenticationMethod.CLIENT_SECRET_POST.value -> ClientAuthenticationMethod.CLIENT_SECRET_POST - ClientAuthenticationMethod.NONE.value -> ClientAuthenticationMethod.NONE - else -> { - ClientAuthenticationMethod(string) - } - } - } - - fun resolveAuthorizationGrantType(string: String): AuthorizationGrantType { - return when (string) { - AuthorizationGrantType.AUTHORIZATION_CODE.value -> AuthorizationGrantType.AUTHORIZATION_CODE - AuthorizationGrantType.CLIENT_CREDENTIALS.value -> AuthorizationGrantType.CLIENT_CREDENTIALS - AuthorizationGrantType.REFRESH_TOKEN.value -> AuthorizationGrantType.REFRESH_TOKEN - else -> { - AuthorizationGrantType(string) - } - } - } - - val clientAuthenticationMethods = this[RegisteredClient.clientAuthenticationMethods].split(",").toSet() - val authorizationGrantTypes = this[RegisteredClient.authorizationGrantTypes].split(",").toSet() - val redirectUris = this[RegisteredClient.redirectUris]?.split(",").orEmpty().toSet() - val postLogoutRedirectUris = this[RegisteredClient.postLogoutRedirectUris]?.split(",").orEmpty().toSet() - val clientScopes = this[RegisteredClient.scopes].split(",").toSet() - - val builder = SpringRegisteredClient - .withId(this[RegisteredClient.id]) - .clientId(this[clientId]) - .clientIdIssuedAt(this[RegisteredClient.clientIdIssuedAt]) - .clientSecret(this[RegisteredClient.clientSecret]) - .clientSecretExpiresAt(this[RegisteredClient.clientSecretExpiresAt]) - .clientName(this[RegisteredClient.clientName]) - .clientAuthenticationMethods { - clientAuthenticationMethods.forEach { s -> - it.add(resolveClientAuthenticationMethods(s)) - } - } - .authorizationGrantTypes { - authorizationGrantTypes.forEach { s -> - it.add(resolveAuthorizationGrantType(s)) - } - } - .redirectUris { it.addAll(redirectUris) } - .postLogoutRedirectUris { it.addAll(postLogoutRedirectUris) } - .scopes { it.addAll(clientScopes) } - .clientSettings(ClientSettings.withSettings(jsonToMap(this[clientSettings])).build()) - - val tokenSettingsMap = jsonToMap(this[tokenSettings]) - val withSettings = TokenSettings.withSettings(tokenSettingsMap) - if (tokenSettingsMap.containsKey(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT)) { - withSettings.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED) - } - builder.tokenSettings(withSettings.build()) - - return builder.build() - } - - companion object { - val objectMapper: ObjectMapper = ObjectMapper() - val LOGGER: Logger = LoggerFactory.getLogger(RegisteredClientRepositoryImpl::class.java) - - init { - - val classLoader = ExposedOAuth2AuthorizationService::class.java.classLoader - val modules = SecurityJackson2Modules.getModules(classLoader) - objectMapper.registerModules(JavaTimeModule()) - objectMapper.registerModules(modules) - objectMapper.registerModules(OAuth2AuthorizationServerJackson2Module()) - } - } -} - -// org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql -object RegisteredClient : Table("registered_client") { - val id: Column = varchar("id", 100) - val clientId: Column = varchar("client_id", 100) - val clientIdIssuedAt: Column = timestamp("client_id_issued_at").defaultExpression(CurrentTimestamp) - val clientSecret: Column = varchar("client_secret", 200).nullable().default(null) - val clientSecretExpiresAt: Column = timestamp("client_secret_expires_at").nullable().default(null) - val clientName: Column = varchar("client_name", 200) - val clientAuthenticationMethods: Column = varchar("client_authentication_methods", 1000) - val authorizationGrantTypes: Column = varchar("authorization_grant_types", 1000) - val redirectUris: Column = varchar("redirect_uris", 1000).nullable().default(null) - val postLogoutRedirectUris: Column = varchar("post_logout_redirect_uris", 1000).nullable().default(null) - val scopes: Column = varchar("scopes", 1000) - val clientSettings: Column = varchar("client_settings", 2000) - val tokenSettings: Column = varchar("token_settings", 2000) - - override val primaryKey: PrimaryKey = PrimaryKey(id) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt deleted file mode 100644 index abb9846e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import com.fasterxml.jackson.annotation.JsonAutoDetect -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import com.fasterxml.jackson.annotation.JsonSubTypes -import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonDeserializer -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import org.springframework.security.core.GrantedAuthority -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.core.userdetails.User -import java.io.Serial - -class UserDetailsImpl( - val id: Long, - username: String?, - password: String?, - enabled: Boolean, - accountNonExpired: Boolean, - credentialsNonExpired: Boolean, - accountNonLocked: Boolean, - authorities: MutableCollection? -) : User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities) { - override fun toString(): String { - return "UserDetailsImpl(" + - "id=$id" + - ")" + - " ${super.toString()}" - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - if (!super.equals(other)) return false - - other as UserDetailsImpl - - return id == other.id - } - - override fun hashCode(): Int { - var result = super.hashCode() - result = 31 * result + id.hashCode() - return result - } - - companion object { - @Serial - private const val serialVersionUID: Long = -899168205656607781L - } -} - -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) -@JsonDeserialize(using = UserDetailsDeserializer::class) -@JsonAutoDetect( - fieldVisibility = JsonAutoDetect.Visibility.ANY, - getterVisibility = JsonAutoDetect.Visibility.NONE, - isGetterVisibility = JsonAutoDetect.Visibility.NONE, - creatorVisibility = JsonAutoDetect.Visibility.NONE -) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonSubTypes -@Suppress("UnnecessaryAbstractClass") -abstract class UserDetailsMixin - -class UserDetailsDeserializer : JsonDeserializer() { - - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UserDetailsImpl { - val mapper = p.codec as ObjectMapper - val jsonNode: JsonNode = mapper.readTree(p) - val authorities: Set = mapper.convertValue( - jsonNode["authorities"], - SIMPLE_GRANTED_AUTHORITY_SET - ) - - val password = jsonNode.readText("password") - return UserDetailsImpl( - id = jsonNode["id"].longValue(), - username = jsonNode.readText("username"), - password = password, - enabled = true, - accountNonExpired = true, - credentialsNonExpired = true, - accountNonLocked = true, - authorities = authorities.toMutableList(), - ) - } - - fun JsonNode.readText(field: String, defaultValue: String = ""): String { - return when { - has(field) -> get(field).asText(defaultValue) - else -> defaultValue - } - } - - companion object { - private val SIMPLE_GRANTED_AUTHORITY_SET = object : TypeReference>() {} - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/OAuth2JwtLoginUserContextHolder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/OAuth2JwtLoginUserContextHolder.kt deleted file mode 100644 index 29746d90..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/security/OAuth2JwtLoginUserContextHolder.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.security - -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.stereotype.Component - -@Component -class OAuth2JwtLoginUserContextHolder : LoginUserContextHolder { - override fun getLoginUserId(): Long { - val principal = SecurityContextHolder.getContext().authentication.principal as Jwt - - return principal.getClaim("uid").toLong() - } - - override fun getLoginUserIdOrNull(): Long? { - val principal = SecurityContextHolder.getContext()?.authentication?.principal - if (principal !is Jwt) { - return null - } - - return principal.getClaim("uid").toLongOrNull() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt deleted file mode 100644 index 4a74319e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.generate - -@MustBeDocumented -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.VALUE_PARAMETER) -annotation class JsonOrFormBind diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt deleted file mode 100644 index 98febb98..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.generate - -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.core.MethodParameter -import org.springframework.validation.BindException -import org.springframework.web.bind.support.WebDataBinderFactory -import org.springframework.web.context.request.NativeWebRequest -import org.springframework.web.method.annotation.ModelAttributeMethodProcessor -import org.springframework.web.method.support.HandlerMethodArgumentResolver -import org.springframework.web.method.support.ModelAndViewContainer -import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - -@Suppress("TooGenericExceptionCaught") -class JsonOrFormModelMethodProcessor( - private val modelAttributeMethodProcessor: ModelAttributeMethodProcessor, - private val requestResponseBodyMethodProcessor: RequestResponseBodyMethodProcessor -) : HandlerMethodArgumentResolver { - private val isJsonRegex = Regex("application/((\\w*)\\+)?json") - - override fun supportsParameter(parameter: MethodParameter): Boolean = - parameter.hasParameterAnnotation(JsonOrFormBind::class.java) - - override fun resolveArgument( - parameter: MethodParameter, - mavContainer: ModelAndViewContainer?, - webRequest: NativeWebRequest, - binderFactory: WebDataBinderFactory? - ): Any? { - val contentType = webRequest.getHeader("Content-Type").orEmpty() - logger.trace("ContentType is {}", contentType) - if (contentType.contains(isJsonRegex)) { - logger.trace("Determine content type as json.") - return requestResponseBodyMethodProcessor.resolveArgument( - parameter, - mavContainer, - webRequest, - binderFactory - ) - } - - return try { - modelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory) - } catch (e: BindException) { - throw e - } catch (exception: Exception) { - try { - requestResponseBodyMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory) - } catch (e: BindException) { - throw e - } catch (e: Exception) { - logger.warn("Failed to bind request (1)", exception) - logger.warn("Failed to bind request (2)", e) - throw IllegalArgumentException("Failed to bind request.") - } - } - } - - companion object { - val logger: Logger = LoggerFactory.getLogger(JsonOrFormModelMethodProcessor::class.java) - } -} diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt index 217fb36b..fa0662c9 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt @@ -1,9 +1,9 @@ package dev.usbharu.hideout.core.domain.model.actor -import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService 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.infrastructure.other.TwitterSnowflakeIdGenerateService import kotlinx.coroutines.runBlocking import java.net.URI import java.time.Instant diff --git a/hideout-mastodon/src/main/kotlin/Main.kt b/hideout-mastodon/src/main/kotlin/Main.kt deleted file mode 100644 index 27f6ee1a..00000000 --- a/hideout-mastodon/src/main/kotlin/Main.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.usbharu - -fun main() { - println("Hello World!") -} \ No newline at end of file From 23507f1812111875506ecf7607c0dff233b8e3c5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 3 Jun 2024 00:44:43 +0900 Subject: [PATCH 17/54] =?UTF-8?q?chore:=20core=E3=81=8B=E3=82=89mastodon?= =?UTF-8?q?=E3=81=AB=E3=83=86=E3=83=B3=E3=83=97=E3=83=AC=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=82=92=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 43 +++++++++- hideout-core/build.gradle.kts | 22 ------ .../RegisterLocalActorApplicationService.kt | 2 +- .../hideout/core/config/ApplicationConfig.kt | 26 +++++++ .../actor/RemoteActorCheckDomainService.kt | 2 +- .../factory/ActorFactoryImpl.kt | 2 +- .../infrastructure/factory/PostFactoryImpl.kt | 2 +- .../hideout/generate/JsonOrFormBind.kt | 22 ++++++ .../JsonOrFormModelMethodProcessor.kt | 78 +++++++++++++++++++ .../dev/usbharu/hideout/util/RsaUtil.kt | 8 -- hideout-mastodon/build.gradle.kts | 53 ++++++++++++- hideout-mastodon/settings.gradle.kts | 13 ++++ .../src/main/resources/openapi/mastodon.yaml | 0 .../templates/api.mustache | 0 .../templates/apiController.mustache | 0 .../templates/apiDelegate.mustache | 0 .../templates/apiInterface.mustache | 0 .../templates/apiUtil.mustache | 0 .../templates/api_test.mustache | 0 .../templates/beanValidation.mustache | 0 .../templates/beanValidationModel.mustache | 0 .../templates/beanValidationPath.mustache | 0 .../beanValidationPathParams.mustache | 0 .../beanValidationQueryParams.mustache | 0 .../templates/bodyParams.mustache | 0 .../templates/dataClass.mustache | 0 .../templates/dataClassOptVar.mustache | 0 .../templates/dataClassReqVar.mustache | 0 .../templates/enumClass.mustache | 0 .../templates/exceptions.mustache | 0 .../templates/formParams.mustache | 0 .../templates/generatedAnnotation.mustache | 0 .../templates/headerParams.mustache | 0 .../templates/homeController.mustache | 0 .../templates/interfaceOptVar.mustache | 0 .../templates/interfaceReqVar.mustache | 0 .../libraries/spring-boot/README.mustache | 0 .../spring-boot/application.mustache | 0 .../spring-boot/buildGradle-sb3-Kts.mustache | 0 .../spring-boot/buildGradleKts.mustache | 0 .../spring-boot/defaultBasePath.mustache | 0 .../libraries/spring-boot/pom-sb3.mustache | 0 .../libraries/spring-boot/pom.mustache | 0 .../spring-boot/settingsGradle.mustache | 0 .../springBootApplication.mustache | 0 .../libraries/spring-boot/swagger-ui.mustache | 0 .../libraries/spring-cloud/README.mustache | 0 .../libraries/spring-cloud/apiClient.mustache | 0 .../apiKeyRequestInterceptor.mustache | 0 .../spring-cloud/buildGradle-sb3-Kts.mustache | 0 .../spring-cloud/buildGradleKts.mustache | 0 .../spring-cloud/clientConfiguration.mustache | 0 .../libraries/spring-cloud/pom-sb3.mustache | 0 .../libraries/spring-cloud/pom.mustache | 0 .../spring-cloud/settingsGradle.mustache | 0 .../templates/methodBody.mustache | 0 .../templates/model.mustache | 0 .../templates/modelMutable.mustache | 0 .../templates/openapi.mustache | 0 .../templates/optionalDataType.mustache | 0 .../templates/pathParams.mustache | 0 .../templates/queryParams.mustache | 0 .../templates/returnTypes.mustache | 0 .../templates/returnValue.mustache | 0 .../templates/service.mustache | 0 .../templates/serviceImpl.mustache | 0 .../springdocDocumentationConfig.mustache | 0 .../springfoxDocumentationConfig.mustache | 0 .../templates/typeInfoAnnotation.mustache | 0 settings.gradle.kts | 1 + 70 files changed, 236 insertions(+), 38 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt rename {hideout-core => hideout-mastodon}/src/main/resources/openapi/mastodon.yaml (100%) rename {hideout-core => hideout-mastodon}/templates/api.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/apiController.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/apiDelegate.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/apiInterface.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/apiUtil.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/api_test.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/beanValidation.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/beanValidationModel.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/beanValidationPath.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/beanValidationPathParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/beanValidationQueryParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/bodyParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/dataClass.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/dataClassOptVar.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/dataClassReqVar.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/enumClass.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/exceptions.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/formParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/generatedAnnotation.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/headerParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/homeController.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/interfaceOptVar.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/interfaceReqVar.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/README.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/application.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/buildGradleKts.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/defaultBasePath.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/pom-sb3.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/pom.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/settingsGradle.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/springBootApplication.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-boot/swagger-ui.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/README.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/apiClient.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/buildGradleKts.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/clientConfiguration.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/pom-sb3.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/pom.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/libraries/spring-cloud/settingsGradle.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/methodBody.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/model.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/modelMutable.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/openapi.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/optionalDataType.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/pathParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/queryParams.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/returnTypes.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/returnValue.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/service.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/serviceImpl.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/springdocDocumentationConfig.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/springfoxDocumentationConfig.mustache (100%) rename {hideout-core => hideout-mastodon}/templates/typeInfoAnnotation.mustache (100%) diff --git a/build.gradle.kts b/build.gradle.kts index c0e7f565..3ad2888b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,13 +16,54 @@ plugins { alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.spring.boot) + alias(libs.plugins.kotlin.spring) +} + +apply { + plugin("io.spring.dependency-management") +} + +repositories { + mavenCentral() + maven { + url = uri("https://git.usbharu.dev/api/packages/usbharu/maven") + } + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/usbharu/http-signature") + credentials { + + username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME") + password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN") + } + } + maven { + name = "GitHubPackages2" + url = uri("https://maven.pkg.github.com/multim-dev/emoji-kt") + credentials { + + username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME") + password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN") + } + } +} +configurations { + all { + exclude("org.springframework.boot", "spring-boot-starter-logging") + exclude("ch.qos.logback", "logback-classic") + } } dependencies { implementation("dev.usbharu:hideout-core:0.0.1") - implementation("dev.usbharu:hideout-worker:0.0.1") + implementation("dev.usbharu:hideout-mastodon:1.0-SNAPSHOT") } tasks.register("run") { dependsOn(gradle.includedBuild("hideout-core").task(":run")) +} + +springBoot { + mainClass = "dev.usbharu.hideout.SpringApplicationKt" } \ No newline at end of file diff --git a/hideout-core/build.gradle.kts b/hideout-core/build.gradle.kts index 52db4593..5920eb91 100644 --- a/hideout-core/build.gradle.kts +++ b/hideout-core/build.gradle.kts @@ -4,14 +4,12 @@ import com.github.jk1.license.importer.DependencyDataImporter import com.github.jk1.license.importer.XmlReportImporter import com.github.jk1.license.render.* import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.openapitools.generator.gradle.plugin.tasks.GenerateTask plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.detekt) alias(libs.plugins.spring.boot) alias(libs.plugins.kotlin.spring) - alias(libs.plugins.openapi.generator) alias(libs.plugins.kover) alias(libs.plugins.license.report) @@ -102,26 +100,6 @@ tasks.clean { delete += listOf("$rootDir/src/main/resources/static") } -tasks.create("openApiGenerateMastodonCompatibleApi", GenerateTask::class) { - generatorName.set("kotlin-spring") - inputSpec.set("$rootDir/src/main/resources/openapi/mastodon.yaml") - outputDir.set("$buildDir/generated/sources/mastodon") - apiPackage.set("dev.usbharu.hideout.controller.mastodon.generated") - modelPackage.set("dev.usbharu.hideout.domain.mastodon.model.generated") - configOptions.put("interfaceOnly", "true") - configOptions.put("useSpringBoot3", "true") - configOptions.put("reactive", "true") - additionalProperties.put("useTags", "true") - - importMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile") - typeMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile") - schemaMappings.put( - "StatusesRequest", - "dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest" - ) - templateDir.set("$rootDir/templates") -} - repositories { mavenCentral() maven { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index e52af918..6608a0e2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -16,8 +16,8 @@ package dev.usbharu.hideout.core.application.actor -import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt new file mode 100644 index 00000000..b4b0e391 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.config + +import org.springframework.boot.context.properties.ConfigurationProperties +import java.net.URL + +@ConfigurationProperties("hideout") +data class ApplicationConfig( + val url: URL, + val private: Boolean = true, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt index d7bf7ba8..a2f9b6fd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainService.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.domain.service.actor -import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.actor.Actor import org.springframework.stereotype.Service diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index 3e610e53..d9e56e09 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.infrastructure.factory -import dev.usbharu.hideout.application.config.ApplicationConfig +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 diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index 822035d9..ab31daf1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -16,7 +16,7 @@ package dev.usbharu.hideout.core.infrastructure.factory -import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorName import dev.usbharu.hideout.core.domain.model.media.MediaId diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt new file mode 100644 index 00000000..4a74319e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormBind.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.generate + +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class JsonOrFormBind diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt new file mode 100644 index 00000000..d7a97736 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.generate + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.core.MethodParameter +import org.springframework.validation.BindException +import org.springframework.web.bind.support.WebDataBinderFactory +import org.springframework.web.context.request.NativeWebRequest +import org.springframework.web.method.annotation.ModelAttributeMethodProcessor +import org.springframework.web.method.support.HandlerMethodArgumentResolver +import org.springframework.web.method.support.ModelAndViewContainer +import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor + +@Suppress("TooGenericExceptionCaught") +class JsonOrFormModelMethodProcessor( + private val modelAttributeMethodProcessor: ModelAttributeMethodProcessor, + private val requestResponseBodyMethodProcessor: RequestResponseBodyMethodProcessor, +) : HandlerMethodArgumentResolver { + private val isJsonRegex = Regex("application/((\\w*)\\+)?json") + + override fun supportsParameter(parameter: MethodParameter): Boolean = + parameter.hasParameterAnnotation(JsonOrFormBind::class.java) + + override fun resolveArgument( + parameter: MethodParameter, + mavContainer: ModelAndViewContainer?, + webRequest: NativeWebRequest, + binderFactory: WebDataBinderFactory?, + ): Any? { + val contentType = webRequest.getHeader("Content-Type").orEmpty() + logger.trace("ContentType is {}", contentType) + if (contentType.contains(isJsonRegex)) { + logger.trace("Determine content type as json.") + return requestResponseBodyMethodProcessor.resolveArgument( + parameter, + mavContainer, + webRequest, + binderFactory + ) + } + + return try { + modelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory) + } catch (e: BindException) { + throw e + } catch (exception: Exception) { + try { + requestResponseBodyMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory) + } catch (e: BindException) { + throw e + } catch (e: Exception) { + logger.warn("Failed to bind request (1)", exception) + logger.warn("Failed to bind request (2)", e) + throw IllegalArgumentException("Failed to bind request.") + } + } + } + + companion object { + val logger: Logger = LoggerFactory.getLogger(JsonOrFormModelMethodProcessor::class.java) + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt index 56b20ac3..3460a515 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt @@ -17,9 +17,7 @@ package dev.usbharu.hideout.util import java.security.KeyFactory -import java.security.interfaces.RSAPrivateKey import java.security.interfaces.RSAPublicKey -import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec object RsaUtil { @@ -38,10 +36,4 @@ object RsaUtil { return decodeRsaPublicKey(replace) } - fun decodeRsaPrivateKey(byteArray: ByteArray): RSAPrivateKey { - val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(byteArray) - return KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey - } - - fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded)) } diff --git a/hideout-mastodon/build.gradle.kts b/hideout-mastodon/build.gradle.kts index 45be2756..2907614d 100644 --- a/hideout-mastodon/build.gradle.kts +++ b/hideout-mastodon/build.gradle.kts @@ -1,5 +1,15 @@ +import org.openapitools.generator.gradle.plugin.tasks.GenerateTask + plugins { - kotlin("jvm") version "1.9.23" + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.openapi.generator) + alias(libs.plugins.spring.boot) + alias(libs.plugins.kotlin.spring) +} + + +apply { + plugin("io.spring.dependency-management") } group = "dev.usbharu" @@ -10,7 +20,19 @@ repositories { } dependencies { - testImplementation(kotlin("test")) + 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("dev.usbharu:hideout-core:0.0.1") + + implementation(libs.jackson.databind) + implementation(libs.jackson.module.kotlin) + implementation(libs.jakarta.annotation) + implementation(libs.jakarta.validation) + + implementation(libs.bundles.openapi) + implementation(libs.bundles.coroutines) } tasks.test { @@ -18,4 +40,29 @@ tasks.test { } kotlin { jvmToolchain(21) -} \ No newline at end of file +} + +tasks.create("openApiGenerateMastodonCompatibleApi", GenerateTask::class) { + generatorName.set("kotlin-spring") + inputSpec.set("$rootDir/src/main/resources/openapi/mastodon.yaml") + outputDir.set("$buildDir/generated/sources/mastodon") + apiPackage.set("dev.usbharu.hideout.mastodon.interfaces.api.generated") + modelPackage.set("dev.usbharu.hideout.mastodon.interfaces.api.generated.model") + configOptions.put("interfaceOnly", "true") + configOptions.put("useSpringBoot3", "true") + configOptions.put("reactive", "true") + configOptions.put("gradleBuildFile", "false") + configOptions.put("useSwaggerUI", "false") + configOptions.put("enumPropertyNaming", "UPPERCASE") + additionalProperties.put("useTags", "true") + + importMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile") + typeMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile") + templateDir.set("$rootDir/templates") +} + +sourceSets.main { + kotlin.srcDirs( + "$buildDir/generated/sources/mastodon/src/main/kotlin" + ) +} diff --git a/hideout-mastodon/settings.gradle.kts b/hideout-mastodon/settings.gradle.kts index c023f31a..9334d94c 100644 --- a/hideout-mastodon/settings.gradle.kts +++ b/hideout-mastodon/settings.gradle.kts @@ -3,3 +3,16 @@ plugins { } rootProject.name = "hideout-mastodon" +includeBuild("../hideout-core") + +dependencyResolutionManagement { + repositories { + mavenCentral() + } + + versionCatalogs { + create("libs") { + from(files("../libs.versions.toml")) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml similarity index 100% rename from hideout-core/src/main/resources/openapi/mastodon.yaml rename to hideout-mastodon/src/main/resources/openapi/mastodon.yaml diff --git a/hideout-core/templates/api.mustache b/hideout-mastodon/templates/api.mustache similarity index 100% rename from hideout-core/templates/api.mustache rename to hideout-mastodon/templates/api.mustache diff --git a/hideout-core/templates/apiController.mustache b/hideout-mastodon/templates/apiController.mustache similarity index 100% rename from hideout-core/templates/apiController.mustache rename to hideout-mastodon/templates/apiController.mustache diff --git a/hideout-core/templates/apiDelegate.mustache b/hideout-mastodon/templates/apiDelegate.mustache similarity index 100% rename from hideout-core/templates/apiDelegate.mustache rename to hideout-mastodon/templates/apiDelegate.mustache diff --git a/hideout-core/templates/apiInterface.mustache b/hideout-mastodon/templates/apiInterface.mustache similarity index 100% rename from hideout-core/templates/apiInterface.mustache rename to hideout-mastodon/templates/apiInterface.mustache diff --git a/hideout-core/templates/apiUtil.mustache b/hideout-mastodon/templates/apiUtil.mustache similarity index 100% rename from hideout-core/templates/apiUtil.mustache rename to hideout-mastodon/templates/apiUtil.mustache diff --git a/hideout-core/templates/api_test.mustache b/hideout-mastodon/templates/api_test.mustache similarity index 100% rename from hideout-core/templates/api_test.mustache rename to hideout-mastodon/templates/api_test.mustache diff --git a/hideout-core/templates/beanValidation.mustache b/hideout-mastodon/templates/beanValidation.mustache similarity index 100% rename from hideout-core/templates/beanValidation.mustache rename to hideout-mastodon/templates/beanValidation.mustache diff --git a/hideout-core/templates/beanValidationModel.mustache b/hideout-mastodon/templates/beanValidationModel.mustache similarity index 100% rename from hideout-core/templates/beanValidationModel.mustache rename to hideout-mastodon/templates/beanValidationModel.mustache diff --git a/hideout-core/templates/beanValidationPath.mustache b/hideout-mastodon/templates/beanValidationPath.mustache similarity index 100% rename from hideout-core/templates/beanValidationPath.mustache rename to hideout-mastodon/templates/beanValidationPath.mustache diff --git a/hideout-core/templates/beanValidationPathParams.mustache b/hideout-mastodon/templates/beanValidationPathParams.mustache similarity index 100% rename from hideout-core/templates/beanValidationPathParams.mustache rename to hideout-mastodon/templates/beanValidationPathParams.mustache diff --git a/hideout-core/templates/beanValidationQueryParams.mustache b/hideout-mastodon/templates/beanValidationQueryParams.mustache similarity index 100% rename from hideout-core/templates/beanValidationQueryParams.mustache rename to hideout-mastodon/templates/beanValidationQueryParams.mustache diff --git a/hideout-core/templates/bodyParams.mustache b/hideout-mastodon/templates/bodyParams.mustache similarity index 100% rename from hideout-core/templates/bodyParams.mustache rename to hideout-mastodon/templates/bodyParams.mustache diff --git a/hideout-core/templates/dataClass.mustache b/hideout-mastodon/templates/dataClass.mustache similarity index 100% rename from hideout-core/templates/dataClass.mustache rename to hideout-mastodon/templates/dataClass.mustache diff --git a/hideout-core/templates/dataClassOptVar.mustache b/hideout-mastodon/templates/dataClassOptVar.mustache similarity index 100% rename from hideout-core/templates/dataClassOptVar.mustache rename to hideout-mastodon/templates/dataClassOptVar.mustache diff --git a/hideout-core/templates/dataClassReqVar.mustache b/hideout-mastodon/templates/dataClassReqVar.mustache similarity index 100% rename from hideout-core/templates/dataClassReqVar.mustache rename to hideout-mastodon/templates/dataClassReqVar.mustache diff --git a/hideout-core/templates/enumClass.mustache b/hideout-mastodon/templates/enumClass.mustache similarity index 100% rename from hideout-core/templates/enumClass.mustache rename to hideout-mastodon/templates/enumClass.mustache diff --git a/hideout-core/templates/exceptions.mustache b/hideout-mastodon/templates/exceptions.mustache similarity index 100% rename from hideout-core/templates/exceptions.mustache rename to hideout-mastodon/templates/exceptions.mustache diff --git a/hideout-core/templates/formParams.mustache b/hideout-mastodon/templates/formParams.mustache similarity index 100% rename from hideout-core/templates/formParams.mustache rename to hideout-mastodon/templates/formParams.mustache diff --git a/hideout-core/templates/generatedAnnotation.mustache b/hideout-mastodon/templates/generatedAnnotation.mustache similarity index 100% rename from hideout-core/templates/generatedAnnotation.mustache rename to hideout-mastodon/templates/generatedAnnotation.mustache diff --git a/hideout-core/templates/headerParams.mustache b/hideout-mastodon/templates/headerParams.mustache similarity index 100% rename from hideout-core/templates/headerParams.mustache rename to hideout-mastodon/templates/headerParams.mustache diff --git a/hideout-core/templates/homeController.mustache b/hideout-mastodon/templates/homeController.mustache similarity index 100% rename from hideout-core/templates/homeController.mustache rename to hideout-mastodon/templates/homeController.mustache diff --git a/hideout-core/templates/interfaceOptVar.mustache b/hideout-mastodon/templates/interfaceOptVar.mustache similarity index 100% rename from hideout-core/templates/interfaceOptVar.mustache rename to hideout-mastodon/templates/interfaceOptVar.mustache diff --git a/hideout-core/templates/interfaceReqVar.mustache b/hideout-mastodon/templates/interfaceReqVar.mustache similarity index 100% rename from hideout-core/templates/interfaceReqVar.mustache rename to hideout-mastodon/templates/interfaceReqVar.mustache diff --git a/hideout-core/templates/libraries/spring-boot/README.mustache b/hideout-mastodon/templates/libraries/spring-boot/README.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/README.mustache rename to hideout-mastodon/templates/libraries/spring-boot/README.mustache diff --git a/hideout-core/templates/libraries/spring-boot/application.mustache b/hideout-mastodon/templates/libraries/spring-boot/application.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/application.mustache rename to hideout-mastodon/templates/libraries/spring-boot/application.mustache diff --git a/hideout-core/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache b/hideout-mastodon/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache rename to hideout-mastodon/templates/libraries/spring-boot/buildGradle-sb3-Kts.mustache diff --git a/hideout-core/templates/libraries/spring-boot/buildGradleKts.mustache b/hideout-mastodon/templates/libraries/spring-boot/buildGradleKts.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/buildGradleKts.mustache rename to hideout-mastodon/templates/libraries/spring-boot/buildGradleKts.mustache diff --git a/hideout-core/templates/libraries/spring-boot/defaultBasePath.mustache b/hideout-mastodon/templates/libraries/spring-boot/defaultBasePath.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/defaultBasePath.mustache rename to hideout-mastodon/templates/libraries/spring-boot/defaultBasePath.mustache diff --git a/hideout-core/templates/libraries/spring-boot/pom-sb3.mustache b/hideout-mastodon/templates/libraries/spring-boot/pom-sb3.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/pom-sb3.mustache rename to hideout-mastodon/templates/libraries/spring-boot/pom-sb3.mustache diff --git a/hideout-core/templates/libraries/spring-boot/pom.mustache b/hideout-mastodon/templates/libraries/spring-boot/pom.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/pom.mustache rename to hideout-mastodon/templates/libraries/spring-boot/pom.mustache diff --git a/hideout-core/templates/libraries/spring-boot/settingsGradle.mustache b/hideout-mastodon/templates/libraries/spring-boot/settingsGradle.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/settingsGradle.mustache rename to hideout-mastodon/templates/libraries/spring-boot/settingsGradle.mustache diff --git a/hideout-core/templates/libraries/spring-boot/springBootApplication.mustache b/hideout-mastodon/templates/libraries/spring-boot/springBootApplication.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/springBootApplication.mustache rename to hideout-mastodon/templates/libraries/spring-boot/springBootApplication.mustache diff --git a/hideout-core/templates/libraries/spring-boot/swagger-ui.mustache b/hideout-mastodon/templates/libraries/spring-boot/swagger-ui.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-boot/swagger-ui.mustache rename to hideout-mastodon/templates/libraries/spring-boot/swagger-ui.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/README.mustache b/hideout-mastodon/templates/libraries/spring-cloud/README.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/README.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/README.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/apiClient.mustache b/hideout-mastodon/templates/libraries/spring-cloud/apiClient.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/apiClient.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/apiClient.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache b/hideout-mastodon/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/apiKeyRequestInterceptor.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache b/hideout-mastodon/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/buildGradle-sb3-Kts.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/buildGradleKts.mustache b/hideout-mastodon/templates/libraries/spring-cloud/buildGradleKts.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/buildGradleKts.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/buildGradleKts.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/clientConfiguration.mustache b/hideout-mastodon/templates/libraries/spring-cloud/clientConfiguration.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/clientConfiguration.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/clientConfiguration.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/pom-sb3.mustache b/hideout-mastodon/templates/libraries/spring-cloud/pom-sb3.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/pom-sb3.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/pom-sb3.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/pom.mustache b/hideout-mastodon/templates/libraries/spring-cloud/pom.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/pom.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/pom.mustache diff --git a/hideout-core/templates/libraries/spring-cloud/settingsGradle.mustache b/hideout-mastodon/templates/libraries/spring-cloud/settingsGradle.mustache similarity index 100% rename from hideout-core/templates/libraries/spring-cloud/settingsGradle.mustache rename to hideout-mastodon/templates/libraries/spring-cloud/settingsGradle.mustache diff --git a/hideout-core/templates/methodBody.mustache b/hideout-mastodon/templates/methodBody.mustache similarity index 100% rename from hideout-core/templates/methodBody.mustache rename to hideout-mastodon/templates/methodBody.mustache diff --git a/hideout-core/templates/model.mustache b/hideout-mastodon/templates/model.mustache similarity index 100% rename from hideout-core/templates/model.mustache rename to hideout-mastodon/templates/model.mustache diff --git a/hideout-core/templates/modelMutable.mustache b/hideout-mastodon/templates/modelMutable.mustache similarity index 100% rename from hideout-core/templates/modelMutable.mustache rename to hideout-mastodon/templates/modelMutable.mustache diff --git a/hideout-core/templates/openapi.mustache b/hideout-mastodon/templates/openapi.mustache similarity index 100% rename from hideout-core/templates/openapi.mustache rename to hideout-mastodon/templates/openapi.mustache diff --git a/hideout-core/templates/optionalDataType.mustache b/hideout-mastodon/templates/optionalDataType.mustache similarity index 100% rename from hideout-core/templates/optionalDataType.mustache rename to hideout-mastodon/templates/optionalDataType.mustache diff --git a/hideout-core/templates/pathParams.mustache b/hideout-mastodon/templates/pathParams.mustache similarity index 100% rename from hideout-core/templates/pathParams.mustache rename to hideout-mastodon/templates/pathParams.mustache diff --git a/hideout-core/templates/queryParams.mustache b/hideout-mastodon/templates/queryParams.mustache similarity index 100% rename from hideout-core/templates/queryParams.mustache rename to hideout-mastodon/templates/queryParams.mustache diff --git a/hideout-core/templates/returnTypes.mustache b/hideout-mastodon/templates/returnTypes.mustache similarity index 100% rename from hideout-core/templates/returnTypes.mustache rename to hideout-mastodon/templates/returnTypes.mustache diff --git a/hideout-core/templates/returnValue.mustache b/hideout-mastodon/templates/returnValue.mustache similarity index 100% rename from hideout-core/templates/returnValue.mustache rename to hideout-mastodon/templates/returnValue.mustache diff --git a/hideout-core/templates/service.mustache b/hideout-mastodon/templates/service.mustache similarity index 100% rename from hideout-core/templates/service.mustache rename to hideout-mastodon/templates/service.mustache diff --git a/hideout-core/templates/serviceImpl.mustache b/hideout-mastodon/templates/serviceImpl.mustache similarity index 100% rename from hideout-core/templates/serviceImpl.mustache rename to hideout-mastodon/templates/serviceImpl.mustache diff --git a/hideout-core/templates/springdocDocumentationConfig.mustache b/hideout-mastodon/templates/springdocDocumentationConfig.mustache similarity index 100% rename from hideout-core/templates/springdocDocumentationConfig.mustache rename to hideout-mastodon/templates/springdocDocumentationConfig.mustache diff --git a/hideout-core/templates/springfoxDocumentationConfig.mustache b/hideout-mastodon/templates/springfoxDocumentationConfig.mustache similarity index 100% rename from hideout-core/templates/springfoxDocumentationConfig.mustache rename to hideout-mastodon/templates/springfoxDocumentationConfig.mustache diff --git a/hideout-core/templates/typeInfoAnnotation.mustache b/hideout-mastodon/templates/typeInfoAnnotation.mustache similarity index 100% rename from hideout-core/templates/typeInfoAnnotation.mustache rename to hideout-mastodon/templates/typeInfoAnnotation.mustache diff --git a/settings.gradle.kts b/settings.gradle.kts index c2d9aa85..65982597 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,7 @@ rootProject.name = "hideout" includeBuild("hideout-core") includeBuild("hideout-worker") +includeBuild("hideout-mastodon") dependencyResolutionManagement { repositories { From 21d0d7af69d3e8fcf04350aa2a8480132e3cdd2c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:08:14 +0900 Subject: [PATCH 18/54] wip --- .../domain/service/actor/RemoteActorCheckDomainServiceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt index 7903a999..a1185837 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt @@ -1,6 +1,6 @@ package dev.usbharu.hideout.core.domain.service.actor -import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey import dev.usbharu.hideout.core.domain.model.actor.TestActor2Factory import org.junit.jupiter.api.Test From bd747718d48e203a8ca58d8581cbc0e0d72dd36a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:46:48 +0900 Subject: [PATCH 19/54] =?UTF-8?q?QueryMapper=E3=81=A8ResultRowMapper?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/post/PostContent.kt | 11 +--- .../infrastructure/exposed/PostQueryMapper.kt | 65 +++++++++++++++++++ .../exposed/PostResultRowMapper.kt | 48 ++++++++++++++ .../ExposedPostRepository.kt | 6 +- .../factory/PostContentFactoryImpl.kt | 4 +- 5 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index 2471ba2a..38e4052d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -18,7 +18,7 @@ package dev.usbharu.hideout.core.domain.model.post import dev.usbharu.hideout.core.domain.model.emoji.EmojiId -class PostContent private constructor(val text: String, val content: String, val emojiIds: List) { +data class PostContent(val text: String, val content: String, val emojiIds: List) { companion object { val empty = PostContent("", "", emptyList()) @@ -26,13 +26,4 @@ class PostContent private constructor(val text: String, val content: String, val val textLength = 3000 } - abstract class PostContentFactory { - protected suspend fun create(text: String, content: String, emojiIds: List): PostContent { - return PostContent( - text, - content, - emojiIds - ) - } - } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt new file mode 100644 index 00000000..c0b600a4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposed + +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.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts +import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsEmojis +import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia +import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsVisibleActors +import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component + +@Component +class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : QueryMapper { + override fun map(query: Query): List { + return query + .groupBy { it[Posts.id] } + .map { it.value } + .map { + it + .first() + .let(postResultRowMapper::map) + .apply { + addMediaIds( + it.mapNotNull { resultRow: ResultRow -> + resultRow + .getOrNull(PostsMedia.mediaId) + ?.let { mediaId -> MediaId(mediaId) } + } + ) + content = content.copy(emojiIds = it + .mapNotNull { resultRow: ResultRow -> + resultRow + .getOrNull(PostsEmojis.emojiId) + ?.let { emojiId -> EmojiId(emojiId) } + } + ) + visibleActors = it.mapNotNull { resultRow: ResultRow -> + resultRow + .getOrNull(PostsVisibleActors.actorId) + ?.let { actorId -> ActorId(actorId) } + } + clearDomainEvents() + } + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt new file mode 100644 index 00000000..f2a5667a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposed + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.post.* +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component +import java.net.URI + +@Component +class PostResultRowMapper : ResultRowMapper { + override fun map(resultRow: ResultRow): Post { + return Post( + id = PostId(resultRow[Posts.id]), + actorId = ActorId(resultRow[Posts.actorId]), + overview = resultRow[Posts.overview]?.let { PostOverview(it) }, + content = PostContent(resultRow[Posts.text], resultRow[Posts.content], emptyList()), + createdAt = resultRow[Posts.createdAt], + visibility = Visibility.valueOf(resultRow[Posts.visibility]), + url = URI.create(resultRow[Posts.url]), + repostId = resultRow[Posts.repostId]?.let { PostId(it) }, + replyId = resultRow[Posts.replyId]?.let { PostId(it) }, + sensitive = resultRow[Posts.sensitive], + apId = URI.create(resultRow[Posts.apId]), + deleted = resultRow[Posts.deleted], + mediaIds = emptyList(), + visibleActors = emptyList(), + hide = resultRow[Posts.hide], + moveTo = resultRow[Posts.moveTo]?.let { PostId(it) } + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 8115fc25..922a605f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -145,7 +145,11 @@ class ExposedPostRepository(override val domainEventPublisher: DomainEventPublis } override suspend fun findById(id: PostId): Post? { - TODO("Not yet implemented") + query { + Posts.selectAll().where { + Posts.id eq id.id + } + } } override suspend fun findByActorId(id: ActorId): List { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt index 7a18fa29..a59af46c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostContentFactoryImpl.kt @@ -23,10 +23,10 @@ import org.springframework.stereotype.Component @Component class PostContentFactoryImpl( private val postContentFormatter: PostContentFormatter, -) : PostContent.PostContentFactory() { +) { suspend fun create(content: String): PostContent { val format = postContentFormatter.format(content) - return super.create( + return PostContent( format.content, format.html, emptyList() From 5e7b538d1d815811fa47a9ef969477e18b33b93a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 4 Jun 2024 23:46:09 +0900 Subject: [PATCH 20/54] =?UTF-8?q?=E5=AE=9F=E9=9A=9B=E3=81=AB=E8=B5=B7?= =?UTF-8?q?=E5=8B=95=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hideout-core/build.gradle.kts | 2 + .../hideout/core/config/ApplicationConfig.kt | 1 + .../hideout/core/config/SecurityConfig.kt | 30 +++++++++++++ .../domain/model/actor/ActorPrivateKey.kt | 19 +++++++- .../core/domain/model/actor/ActorPublicKey.kt | 19 +++++++- .../domain/model/actor/ActorRepository.kt | 1 + .../local/LocalActorDomainServiceImpl.kt | 41 ++++++++++++++++++ ...calActorMigrationCheckDomainServiceImpl.kt | 43 +++++++++++++++++++ .../ExposedActorRepository.kt | 15 ++++++- .../ExposedPostRepository.kt | 25 ++++++++--- .../SpringSecurityPasswordEncoder.kt | 28 ++++++++++++ .../src/main/resources/application.yml | 2 +- .../resources/db/migration/V1__Init_DB.sql | 21 ++++----- libs.versions.toml | 1 + 14 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt diff --git a/hideout-core/build.gradle.kts b/hideout-core/build.gradle.kts index 5920eb91..ca9a8cc9 100644 --- a/hideout-core/build.gradle.kts +++ b/hideout-core/build.gradle.kts @@ -165,6 +165,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-log4j2") implementation("org.springframework.boot:spring-boot-starter-validation") + implementation(libs.blurhash) implementation(libs.aws.s3) implementation(libs.jsoup) @@ -173,6 +174,7 @@ dependencies { implementation(libs.imageio.webp) implementation(libs.thumbnailator) implementation(libs.flyway.core) + runtimeOnly(libs.flyway.postgresql) implementation("dev.usbharu:owl-common-serialize-jackson:0.0.1") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt index b4b0e391..5cb5941c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ApplicationConfig.kt @@ -23,4 +23,5 @@ import java.net.URL data class ApplicationConfig( val url: URL, val private: Boolean = true, + val keySize: Int = 2048, ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt new file mode 100644 index 00000000..f56f796b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder + +@Configuration +class SecurityConfig { + @Bean + fun passwordEncoder(): PasswordEncoder { + return BCryptPasswordEncoder() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt index 2777c3e5..bb507885 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPrivateKey.kt @@ -16,5 +16,22 @@ package dev.usbharu.hideout.core.domain.model.actor +import java.security.PrivateKey +import java.util.* + @JvmInline -value class ActorPrivateKey(val privateKey: String) +value class ActorPrivateKey(val privateKey: String) { + companion object { + fun create(privateKey: PrivateKey): ActorPrivateKey { + return ActorPrivateKey( + "-----BEGIN PRIVATE KEY-----\n" + + Base64 + .getEncoder() + .encodeToString(privateKey.encoded) + .chunked(64) + .joinToString("\n") + + "\n-----END PRIVATE KEY-----" + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt index ac8cc06f..5dd982f1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorPublicKey.kt @@ -16,5 +16,22 @@ package dev.usbharu.hideout.core.domain.model.actor +import java.security.PublicKey +import java.util.* + @JvmInline -value class ActorPublicKey(val publicKey: String) +value class ActorPublicKey(val publicKey: String) { + companion object { + fun create(publicKey: PublicKey): ActorPublicKey { + return ActorPublicKey( + "-----BEGIN PUBLIC KEY-----\n" + + Base64 + .getEncoder() + .encodeToString(publicKey.encoded) + .chunked(64) + .joinToString("\n") + + "\n-----END PUBLIC KEY-----" + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt index 0438660b..266e1d47 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorRepository.kt @@ -20,4 +20,5 @@ interface ActorRepository { suspend fun save(actor: Actor): Actor suspend fun delete(actor: Actor) suspend fun findById(id: ActorId): Actor? + suspend fun findByNameAndDomain(name: String, domain: String): Actor? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt new file mode 100644 index 00000000..f00406e5 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorPrivateKey +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import org.springframework.stereotype.Service +import java.security.KeyPairGenerator + +@Service +class LocalActorDomainServiceImpl( + private val actorRepository: ActorRepository, + private val applicationConfig: ApplicationConfig, +) : LocalActorDomainService { + override suspend fun usernameAlreadyUse(name: String): Boolean = + actorRepository.findByNameAndDomain(name, applicationConfig.url.host) == null + + override suspend fun generateKeyPair(): Pair { + val keyPairGenerator = KeyPairGenerator.getInstance("RSA") + keyPairGenerator.initialize(applicationConfig.keySize) + val generateKeyPair = keyPairGenerator.generateKeyPair() + + return ActorPublicKey.create(generateKeyPair.public) to ActorPrivateKey.create(generateKeyPair.private) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt new file mode 100644 index 00000000..e2b2f621 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.domain.model.actor.Actor +import org.springframework.stereotype.Service + +@Service +class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService { + override suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck { + if (to == from) { + return AccountMigrationCheck.SelfReferences() + } + + if (to.moveTo != null) { + return AccountMigrationCheck.AlreadyMoved("${to.name}@${to.domain} was move to ${to.moveTo}") + } + + if (from.moveTo != null) { + return AccountMigrationCheck.AlreadyMoved("${from.name}@${from.domain} was move to ${from.moveTo}") + } + + if (to.alsoKnownAs.contains(to.id).not()) { + return AccountMigrationCheck.AlsoKnownAsNotFound("${to.id} has ${to.alsoKnownAs}") + } + + return AccountMigrationCheck.CanAccountMigration() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index 9b16788f..14d17c3f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -83,7 +83,20 @@ class ExposedActorRepository( Actors.id eq id.id } .let(actorQueryMapper::map) - .first() + .firstOrNull() + } + } + + override suspend fun findByNameAndDomain(name: String, domain: String): Actor? { + return query { + Actors + .leftJoin(ActorsAlsoKnownAs, onColumn = { id }, otherColumn = { actorId }) + .selectAll() + .where { + Actors.name eq name and (Actors.domain eq domain) + } + .let(actorQueryMapper::map) + .firstOrNull() } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 922a605f..574ea476 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -20,6 +20,7 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.post.* 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 import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.actorId import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.apId import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.content @@ -44,7 +45,10 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedPostRepository(override val domainEventPublisher: DomainEventPublisher) : +class ExposedPostRepository( + private val postQueryMapper: QueryMapper, + override val domainEventPublisher: DomainEventPublisher, +) : PostRepository, AbstractRepository(), DomainEventPublishableRepository { @@ -144,16 +148,23 @@ class ExposedPostRepository(override val domainEventPublisher: DomainEventPublis return posts } - override suspend fun findById(id: PostId): Post? { - query { - Posts.selectAll().where { + override suspend fun findById(id: PostId): Post? = query { + Posts + .selectAll() + .where { Posts.id eq id.id } - } + .let(postQueryMapper::map) + .first() } - override suspend fun findByActorId(id: ActorId): List { - TODO("Not yet implemented") + override suspend fun findByActorId(id: ActorId): List = query { + Posts + .selectAll() + .where { + actorId eq id.id + } + .let(postQueryMapper::map) } override suspend fun delete(post: Post) { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt new file mode 100644 index 00000000..1f8ae461 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework + +import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder +import org.springframework.stereotype.Component + +@Component +class SpringSecurityPasswordEncoder(private val passwordEncoder: org.springframework.security.crypto.password.PasswordEncoder) : + PasswordEncoder { + override suspend fun encode(input: String): String { + return passwordEncoder.encode(input) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/resources/application.yml b/hideout-core/src/main/resources/application.yml index 52bc6eba..e5c023ec 100644 --- a/hideout-core/src/main/resources/application.yml +++ b/hideout-core/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: default-property-inclusion: always datasource: driver-class-name: org.postgresql.Driver - url: "jdbc:postgresql:hideout2" + url: "jdbc:postgresql:hideout3" username: "postgres" password: "" data: diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 0e81fca9..699a3357 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -41,20 +41,20 @@ create table if not exists actors url varchar(1000) not null unique, public_key varchar(10000) not null, private_key varchar(10000) null, - created_at bigint not null, + created_at timestamp not null, key_id varchar(1000) not null, "following" varchar(1000) null, followers varchar(1000) null, - "instance" bigint not null, + "instance" bigint not null, locked boolean not null, - following_count int not null, - followers_count int not null, + following_count int null, + followers_count int null, posts_count int not null, last_post_at timestamp null default null, - last_update_at timestamp not null, - suspend boolean not null, - move_to bigint null default null, - emojis varchar(3000) not null default '', + last_update_at timestamp not null, + suspend boolean not null, + move_to bigint null default null, + emojis varchar(3000) not null default '', 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 @@ -250,8 +250,9 @@ values (0, 'system', '', '', '', null, '', '', false, false, '', current_timesta 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) -values (0, 'ghost', '', '', '', '', '', '', '', null, 0, '', '', '', 0, true, 0, 0, 0, null); + 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, ''); create table if not exists deleted_actors ( diff --git a/libs.versions.toml b/libs.versions.toml index 4280a193..e8b1fed1 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -82,6 +82,7 @@ imageio-webp = { module = "com.twelvemonkeys.imageio:imageio-webp", version = "3 thumbnailator = { module = "net.coobird:thumbnailator", version = "0.4.20" } flyway-core = { module = "org.flywaydb:flyway-core" } +flyway-postgresql = { module = "org.flywaydb:flyway-database-postgresql", version = "10.14.0" } h2db = { module = "com.h2database:h2", version = "2.2.224" } From 5de217fecc425a4f7669a30576e7ced05ce16fea Mon Sep 17 00:00:00 2001 From: usbharu Date: Wed, 5 Jun 2024 10:45:10 +0900 Subject: [PATCH 21/54] =?UTF-8?q?test:=20post=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/domain/model/post/Post.kt | 1 + .../core/domain/model/actor/ActorsTest.kt | 24 ++-- ...stActor2Factory.kt => TestActorFactory.kt} | 2 +- .../core/domain/model/post/PostTest.kt | 135 ++++++++++++++++++ .../core/domain/model/post/TestPostFactory.kt | 54 +++++++ .../RemoteActorCheckDomainServiceTest.kt | 6 +- 6 files changed, 206 insertions(+), 16 deletions(-) rename hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/{TestActor2Factory.kt => TestActorFactory.kt} (99%) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index f1846382..90cbcb2c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -54,6 +54,7 @@ class Post( var visibility = visibility set(value) { + require(visibility != Visibility.DIRECT) require(value != Visibility.DIRECT) require(field.ordinal >= value.ordinal) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt index d35fb0f8..a0127a35 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt @@ -12,7 +12,7 @@ import kotlin.test.assertNull class ActorsTest { @Test fun suspendãŒtrueã®ã¨ãactorSuspendイベントãŒç™ºç”Ÿã™ã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) actor.suspend = true @@ -21,7 +21,7 @@ class ActorsTest { @Test fun suspendãŒfalseã«ãªã£ãŸã¨ãactorUnsuspendイベントãŒç™ºç”Ÿã™ã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""), suspend = true) + val actor = TestActorFactory.create(publicKey = ActorPublicKey(""), suspend = true) actor.suspend = false @@ -30,7 +30,7 @@ class ActorsTest { @Test fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œãªã„å ´åˆæ›´æ–°ã•ã‚Œã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) val actorIds = setOf(ActorId(100), ActorId(200)) actor.alsoKnownAs = actorIds @@ -40,7 +40,7 @@ class ActorsTest { @Test fun moveToã«è‡ªåˆ†è‡ªèº«ãŒè¨­å®šã•ã‚ŒãŸå ´åˆmoveイベントãŒç™ºç”Ÿã—æ›´æ–°ã•ã‚Œã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) actor.moveTo = ActorId(100) @@ -50,7 +50,7 @@ class ActorsTest { @Test fun alsoKnownAsã«è‡ªåˆ†è‡ªèº«ãŒå«ã¾ã‚Œã¦ã¯ã„ã‘ãªã„() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) assertThrows { actor.alsoKnownAs = setOf(actor.id) @@ -59,7 +59,7 @@ class ActorsTest { @Test fun moveToã«è‡ªåˆ†è‡ªèº«ãŒè¨­å®šã•ã‚Œã¦ã¯ã„ã‘ãªã„() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) assertThrows { actor.moveTo = actor.id @@ -68,7 +68,7 @@ class ActorsTest { @Test fun descriptionãŒæ›´æ–°ã•ã‚ŒãŸã¨ãupdateイベントãŒç™ºç”Ÿã™ã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) actor.description = ActorDescription("hoge fuga") @@ -77,7 +77,7 @@ class ActorsTest { @Test fun screenNameãŒæ›´æ–°ã•ã‚ŒãŸã¨ãupdateイベントãŒç™ºç”Ÿã™ã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) actor.screenName = ActorScreenName("fuga hoge") @@ -86,7 +86,7 @@ class ActorsTest { @Test fun deleteãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãã™ã§ã«deletedãŒtrueãªã‚‰ä½•ã‚‚ã—ãªã„() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""), deleted = true) + val actor = TestActorFactory.create(publicKey = ActorPublicKey(""), deleted = true) actor.delete() @@ -95,7 +95,7 @@ class ActorsTest { @Test fun deleteãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãdeletedãŒfalseãªã‚‰deleteイベントãŒç™ºç”Ÿã™ã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) actor.delete() @@ -111,7 +111,7 @@ class ActorsTest { @Test fun restoreãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãcheckUpdateイベントãŒç™ºç”Ÿã™ã‚‹() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""), deleted = true) + val actor = TestActorFactory.create(publicKey = ActorPublicKey(""), deleted = true) actor.restore() @@ -121,7 +121,7 @@ class ActorsTest { @Test fun checkUpdateãŒå®Ÿè¡Œã•ã‚ŒãŸã¨ãcheckUpdateイベントãŒh() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) actor.checkUpdate() diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt similarity index 99% rename from hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt rename to hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt index fa0662c9..c0919feb 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActor2Factory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.runBlocking import java.net.URI import java.time.Instant -object TestActor2Factory { +object TestActorFactory { private val idGenerateService = TwitterSnowflakeIdGenerateService fun create( diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index b14d9bbe..83c632ad 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -1,10 +1,145 @@ package dev.usbharu.hideout.core.domain.model.post +import dev.usbharu.hideout.core.domain.event.post.PostEvent +import dev.usbharu.hideout.core.domain.model.actor.ActorId import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows +import utils.AssertDomainEvent.assertContainsEvent +import utils.AssertDomainEvent.assertEmpty +import kotlin.test.assertEquals class PostTest { @Test fun deletedãŒtrueã®ã¨ãghostã®idãŒè¿”ã•ã‚Œã‚‹() { + val post = TestPostFactory.create(deleted = true) + + assertEquals(ActorId.ghost, post.actorId) + } + + @Test + fun deletedãŒfalseã®æ™‚actorã®IDãŒè¿”ã•ã‚Œã‚‹() { + val post = TestPostFactory.create(deleted = false, actorId = 100) + + assertEquals(ActorId(100), post.actorId) + } + + @Test + fun visibilityãŒDIRECTã®ã¨ã変更ã§ããªã„() { + val post = TestPostFactory.create(visibility = Visibility.DIRECT) + + assertThrows { + post.visibility = Visibility.PUBLIC + } + assertThrows { + post.visibility = Visibility.UNLISTED + } + assertThrows { + post.visibility = Visibility.FOLLOWERS + } + } + + @Test + fun visibilityã‚’å°ã•ãã™ã‚‹ã“ã¨ã¯ã§ããªã„PUBLIC() { + val post = TestPostFactory.create(visibility = Visibility.PUBLIC) + + assertThrows { + post.visibility = Visibility.DIRECT + } + assertThrows { + post.visibility = Visibility.UNLISTED + } + assertThrows { + post.visibility = Visibility.FOLLOWERS + } + } + + @Test + fun visibilityã‚’å°ã•ãã™ã‚‹ã“ã¨ã¯ã§ããªã„UNLISTED() { + val post = TestPostFactory.create(visibility = Visibility.UNLISTED) + + assertThrows { + post.visibility = Visibility.DIRECT + } + assertThrows { + post.visibility = Visibility.FOLLOWERS + } + } + + @Test + fun visibilityã‚’å°ã•ãã™ã‚‹ã“ã¨ã¯ã§ããªã„FOLLOWERS() { + val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + + assertThrows { + post.visibility = Visibility.DIRECT + } + } + + @Test + fun visibilityã‚’DIRECTã«ã‚ã¨ã‹ã‚‰ã™ã‚‹ã“ã¨ã¯ã§ããªã„() { + val post = TestPostFactory.create(visibility = Visibility.DIRECT) + + assertThrows { + post.visibility = Visibility.DIRECT + } + } + + @Test + fun visibilityを大ããã™ã‚‹ã“ã¨ãŒã§ãã‚‹FOLLOWERS() { + val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + + assertDoesNotThrow { + post.visibility = Visibility.UNLISTED + } + + val post2 = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + + assertDoesNotThrow { + post2.visibility = Visibility.PUBLIC + } + } + + @Test + fun visibilityを大ããã™ã‚‹ã“ã¨ãŒã§ãã‚‹UNLISTED() { + val post = TestPostFactory.create(visibility = Visibility.UNLISTED) + + assertDoesNotThrow { + post.visibility = Visibility.PUBLIC + } + } + + @Test + fun deletedãŒtrueã®ã¨ãvisibilityを変更ã§ããªã„() { + val post = TestPostFactory.create(visibility = Visibility.UNLISTED, deleted = true) + + assertThrows { + post.visibility = Visibility.PUBLIC + } + } + + @Test + fun visibilityãŒå¤‰æ›´ã•ã‚Œãªã„é™ã‚Šãƒ‰ãƒ¡ã‚¤ãƒ³ã‚¤ãƒ™ãƒ³ãƒˆã¯ç™ºç”Ÿã—ãªã„() { + val post = TestPostFactory.create(visibility = Visibility.UNLISTED) + + post.visibility = Visibility.UNLISTED + assertEmpty(post) } + + @Test + fun visibilityãŒå¤‰æ›´ã•ã‚Œã‚‹ã¨updateイベントãŒç™ºç”Ÿã™ã‚‹() { + val post = TestPostFactory.create(visibility = Visibility.UNLISTED) + post.visibility = Visibility.PUBLIC + + assertContainsEvent(post, PostEvent.update.eventName) + } + + @Test + fun deletedãŒtrueã®ã¨ãvisibleActorsを変更ã§ããªã„() { + val post = TestPostFactory.create(deleted = true) + + assertThrows { + post.visibleActors = listOf(ActorId(100)) + } + } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt new file mode 100644 index 00000000..e9dcc61e --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt @@ -0,0 +1,54 @@ +package dev.usbharu.hideout.core.domain.model.post + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.media.MediaId +import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService +import kotlinx.coroutines.runBlocking +import java.net.URI +import java.time.Instant + +object TestPostFactory { + private val idGenerateService = TwitterSnowflakeIdGenerateService + + fun create( + id: Long = generateId(), + actorId: Long = 1, + overview: String? = null, + content: String = "This is test content", + createdAt: Instant = Instant.now(), + visibility: Visibility = Visibility.PUBLIC, + url: URI = URI.create("https://example.com/$actorId/posts/$id"), + repostId: Long? = null, + replyId: Long? = null, + sensitive: Boolean = false, + apId: URI = URI.create("https://example.com/$actorId/posts/$id"), + deleted: Boolean = false, + mediaIds: List = emptyList(), + visibleActors: List = emptyList(), + hide: Boolean = false, + moveTo: Long? = null, + ): Post { + return Post( + PostId(id), + ActorId(actorId), + 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) }, + sensitive = sensitive, + apId = apId, + deleted = deleted, + mediaIds.map { MediaId(it) }, + visibleActors.map { ActorId(it) }, + hide = hide, + moveTo?.let { PostId(it) } + ) + } + + private fun generateId(): Long = runBlocking { + idGenerateService.generateId() + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt index a1185837..0b7acd6d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/RemoteActorCheckDomainServiceTest.kt @@ -2,7 +2,7 @@ package dev.usbharu.hideout.core.domain.service.actor import dev.usbharu.hideout.core.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey -import dev.usbharu.hideout.core.domain.model.actor.TestActor2Factory +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory import org.junit.jupiter.api.Test import java.net.URI import kotlin.test.assertFalse @@ -11,7 +11,7 @@ import kotlin.test.assertTrue class RemoteActorCheckDomainServiceTest { @Test fun リモートã®ãƒ‰ãƒ¡ã‚¤ãƒ³ãªã‚‰trueã‚’è¿”ã™() { - val actor = TestActor2Factory.create(publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(publicKey = ActorPublicKey("")) val remoteActor = RemoteActorCheckDomainService( ApplicationConfig( @@ -26,7 +26,7 @@ class RemoteActorCheckDomainServiceTest { @Test fun ローカルã®Actorãªã‚‰falseã‚’è¿”ã™() { - val actor = TestActor2Factory.create(domain = "local.example.com", publicKey = ActorPublicKey("")) + val actor = TestActorFactory.create(domain = "local.example.com", publicKey = ActorPublicKey("")) val localActor = RemoteActorCheckDomainService( ApplicationConfig( From 2c298d2d2651612aea3fbcc7fdd549560985be33 Mon Sep 17 00:00:00 2001 From: usbharu Date: Wed, 5 Jun 2024 11:47:19 +0900 Subject: [PATCH 22/54] =?UTF-8?q?test:=20post=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/domain/model/post/Post.kt | 25 ++++++--- .../infrastructure/exposed/PostQueryMapper.kt | 2 +- .../exposed/PostResultRowMapper.kt | 2 +- .../core/domain/model/post/PostTest.kt | 51 ++++++++++++++++++- .../core/domain/model/post/TestPostFactory.kt | 2 +- 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index 90cbcb2c..80099fc8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -19,6 +19,7 @@ package dev.usbharu.hideout.core.domain.model.post import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory import dev.usbharu.hideout.core.domain.event.post.PostEvent 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.media.MediaId import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI @@ -38,7 +39,7 @@ class Post( val apId: URI, deleted: Boolean, mediaIds: List, - visibleActors: List = emptyList(), + visibleActors: Set = emptySet(), hide: Boolean = false, moveTo: PostId? = null, ) : DomainEventStorable() { @@ -71,7 +72,7 @@ class Post( require(deleted.not()) if (visibility == Visibility.DIRECT) { addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) - field = field.plus(value).distinct() + field = field.plus(value) } } @@ -114,11 +115,21 @@ class Post( field = value } - val text - get() = content.text + val text: String + get() { + if (hide) { + return PostContent.empty.text + } + return content.text + } - val emojiIds - get() = content.emojiIds + val emojiIds: List + get() { + if (hide) { + return PostContent.empty.emojiIds + } + return content.emojiIds + } var mediaIds = mediaIds get() { @@ -207,7 +218,7 @@ class Post( apId: URI, deleted: Boolean, mediaIds: List, - visibleActors: List = emptyList(), + visibleActors: Set = emptySet(), hide: Boolean = false, moveTo: PostId? = null, ): Post { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt index c0b600a4..6a338544 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt @@ -57,7 +57,7 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : resultRow .getOrNull(PostsVisibleActors.actorId) ?.let { actorId -> ActorId(actorId) } - } + }.toSet() clearDomainEvents() } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt index f2a5667a..1076477c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt @@ -40,7 +40,7 @@ class PostResultRowMapper : ResultRowMapper { apId = URI.create(resultRow[Posts.apId]), deleted = resultRow[Posts.deleted], mediaIds = emptyList(), - visibleActors = emptyList(), + visibleActors = emptySet(), hide = resultRow[Posts.hide], moveTo = resultRow[Posts.moveTo]?.let { PostId(it) } ) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index 83c632ad..9ec4934d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -139,7 +139,56 @@ class PostTest { val post = TestPostFactory.create(deleted = true) assertThrows { - post.visibleActors = listOf(ActorId(100)) + post.visibleActors = setOf(ActorId(100)) } } + + @Test + fun visibilityãŒDIRECTã®æ™‚visibleActorsを変更ã§ãã‚‹() { + val post = TestPostFactory.create(visibility = Visibility.DIRECT) + + post.visibleActors = setOf(ActorId(100)) + assertEquals(setOf(ActorId(100)), post.visibleActors) + } + + @Test + fun visibleActorsã‹ã‚‰å‰Šé™¤ã•ã‚Œã‚‹ã“ã¨ã¯ãªã„() { + val post = TestPostFactory.create(visibility = Visibility.DIRECT, visibleActors = listOf(100)) + + post.visibleActors = setOf(ActorId(200)) + assertEquals(setOf(ActorId(100), ActorId(200)), post.visibleActors) + } + + @Test + fun visibleActorsã«è¿½åŠ ã•ã‚ŒãŸæ™‚updateイベントãŒç™ºç”Ÿã™ã‚‹() { + val post = TestPostFactory.create(visibility = Visibility.DIRECT) + + post.visibleActors = setOf(ActorId(100)) + + assertContainsEvent(post, PostEvent.update.eventName) + } + + @Test + fun hideãŒtrueã®ã¨ãcontetnãŒemptyã‚’è¿”ã™() { + val post = TestPostFactory.create(hide = true) + + assertEquals(PostContent.empty, post.content) + } + + @Test + fun deletedãŒtrueã®æ™‚contentをセットã§ããªã„() { + val post = TestPostFactory.create(deleted = true) + + assertThrows { + post.content = PostContent("test", "test", emptyList()) + } + } + + @Test + fun contentã®å†…容ãŒå¤‰æ›´ã•ã‚ŒãŸã‚‰updateイベントãŒç™ºç”Ÿã™ã‚‹() { + val post = TestPostFactory.create() + + post.content = PostContent("test", "test", emptyList()) + assertContainsEvent(post, PostEvent.update.eventName) + } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt index e9dcc61e..2bf9c5ae 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/TestPostFactory.kt @@ -42,7 +42,7 @@ object TestPostFactory { apId = apId, deleted = deleted, mediaIds.map { MediaId(it) }, - visibleActors.map { ActorId(it) }, + visibleActors.map { ActorId(it) }.toSet(), hide = hide, moveTo?.let { PostId(it) } ) From f794095ecfd90b9d0184e69e0a1a33a107191c6d Mon Sep 17 00:00:00 2001 From: usbharu Date: Wed, 5 Jun 2024 12:07:58 +0900 Subject: [PATCH 23/54] =?UTF-8?q?test:=20post=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/post/PostTest.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index 9ec4934d..83d826a9 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -143,6 +143,24 @@ class PostTest { } } + @Test + fun ã‚”visibilityãŒDIRECT以外ã®æ™‚visibleActorsを変更ã§ããªã„() { + val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + + post.visibleActors = setOf(ActorId(100)) + assertEmpty(post) + + val post2 = TestPostFactory.create(visibility = Visibility.UNLISTED) + + post2.visibleActors = setOf(ActorId(100)) + assertEmpty(post2) + + val post3 = TestPostFactory.create(visibility = Visibility.PUBLIC) + + post3.visibleActors = setOf(ActorId(100)) + assertEmpty(post3) + } + @Test fun visibilityãŒDIRECTã®æ™‚visibleActorsを変更ã§ãã‚‹() { val post = TestPostFactory.create(visibility = Visibility.DIRECT) @@ -191,4 +209,6 @@ class PostTest { post.content = PostContent("test", "test", emptyList()) assertContainsEvent(post, PostEvent.update.eventName) } + + } \ No newline at end of file From 6739bb0da2d686ae75d9d91cf2ce168bae868719 Mon Sep 17 00:00:00 2001 From: usbharu Date: Thu, 6 Jun 2024 14:15:59 +0900 Subject: [PATCH 24/54] =?UTF-8?q?test:=20post=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/post/PostTest.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index 83d826a9..f0e4cf1f 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.api.assertThrows import utils.AssertDomainEvent.assertContainsEvent import utils.AssertDomainEvent.assertEmpty import kotlin.test.assertEquals +import kotlin.test.assertNull class PostTest { @Test @@ -210,5 +211,50 @@ class PostTest { assertContainsEvent(post, PostEvent.update.eventName) } + @Test + fun hideãŒtrueã®æ™‚nullã‚’è¿”ã™() { + val post = TestPostFactory.create(hide = true, overview = "aaaa") + + assertNull(post.overview) + } + + @Test + fun hideãŒfalseã®æ™‚overviewã‚’è¿”ã™() { + val post = TestPostFactory.create(hide = false, overview = "aaaa") + + assertEquals(PostOverview("aaaa"), post.overview) + } + + @Test + fun deletedãŒtrueã®ã¨ãセットã§ããªã„() { + val post = TestPostFactory.create(deleted = true) + + assertThrows { + post.overview = PostOverview("aaaa") + } + } + + @Test + fun deletedãŒfalseã®ã¨ãセットã§ãã‚‹() { + val post = TestPostFactory.create(deleted = false) + + val overview = PostOverview("aaaa") + assertDoesNotThrow { + post.overview = overview + } + assertEquals(overview, post.overview) + + assertContainsEvent(post, PostEvent.update.eventName) + } + + @Test + fun overviewã®å†…容ãŒæ›´æ–°ã•ã‚Œãªã‹ã£ãŸæ™‚イベントãŒç™ºç”Ÿã—ãªã„() { + val post = TestPostFactory.create(overview = "aaaa") + post.overview = PostOverview("aaaa") + assertEmpty(post) + } + + + } \ No newline at end of file From 9c271b8cc84cc1e57a55a282a9a7cd116b2729a6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:08:59 +0900 Subject: [PATCH 25/54] =?UTF-8?q?feat:=20api=E3=81=AE=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=83=AD=E3=83=BC=E3=83=A9=E3=83=BC=E3=82=92=E5=BE=A9?= =?UTF-8?q?=E6=B4=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mastodon/config/MastodonSecurityConfig.kt | 173 ++++++++++++++++++ ...oleHierarchyAuthorizationManagerFactory.kt | 32 ++++ .../interfaces/api/SpringAccountApi.kt | 82 +++++++++ .../mastodon/interfaces/api/SpringAppApi.kt | 30 +++ .../interfaces/api/SpringFilterApi.kt | 112 ++++++++++++ .../interfaces/api/SpringInstanceApi.kt | 39 ++++ .../mastodon/interfaces/api/SpringMediaApi.kt | 35 ++++ .../interfaces/api/SpringNotificationApi.kt | 38 ++++ .../interfaces/api/SpringStatusApi.kt | 42 +++++ .../interfaces/api/SpringTimelineApi.kt | 28 +++ 10 files changed, 611 insertions(+) create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/external/RoleHierarchyAuthorizationManagerFactory.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringInstanceApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringNotificationApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt new file mode 100644 index 00000000..2819b80a --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.config + +import dev.usbharu.hideout.mastodon.external.RoleHierarchyAuthorizationManagerFactory +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.annotation.Order +import org.springframework.http.HttpMethod.* +import org.springframework.security.access.hierarchicalroles.RoleHierarchy +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.invoke +import org.springframework.security.web.SecurityFilterChain + +@Configuration +class MastodonSecurityConfig { + @Bean + @Order(4) + @Suppress("LongMethod") + fun mastodonApiSecurityFilterChain( + http: HttpSecurity, + rf: RoleHierarchyAuthorizationManagerFactory, + ): SecurityFilterChain { + http { + securityMatcher("/api/v1/**", "/api/v2/**") + authorizeHttpRequests { + authorize(POST, "/api/v1/apps", permitAll) + authorize(GET, "/api/v1/instance/**", permitAll) + authorize(POST, "/api/v1/accounts", authenticated) + + authorize(GET, "/api/v1/accounts/verify_credentials", rf.hasScope("read:accounts")) + authorize(GET, "/api/v1/accounts/relationships", rf.hasScope("read:follows")) + authorize(GET, "/api/v1/accounts/*", permitAll) + authorize(GET, "/api/v1/accounts/*/statuses", permitAll) + authorize(POST, "/api/v1/accounts/*/follow", rf.hasScope("write:follows")) + authorize(POST, "/api/v1/accounts/*/unfollow", rf.hasScope("write:follows")) + authorize(POST, "/api/v1/accounts/*/block", rf.hasScope("write:blocks")) + authorize(POST, "/api/v1/accounts/*/unblock", rf.hasScope("write:blocks")) + authorize(POST, "/api/v1/accounts/*/mute", rf.hasScope("write:mutes")) + authorize(POST, "/api/v1/accounts/*/unmute", rf.hasScope("write:mutes")) + authorize(GET, "/api/v1/mutes", rf.hasScope("read:mutes")) + + authorize(POST, "/api/v1/media", rf.hasScope("write:media")) + authorize(POST, "/api/v1/statuses", rf.hasScope("write:statuses")) + authorize(GET, "/api/v1/statuses/*", permitAll) + authorize(POST, "/api/v1/statuses/*/favourite", rf.hasScope("write:favourites")) + authorize(POST, "/api/v1/statuses/*/unfavourite", rf.hasScope("write:favourites")) + authorize(PUT, "/api/v1/statuses/*/emoji_reactions/*", rf.hasScope("write:favourites")) + + authorize(GET, "/api/v1/timelines/public", permitAll) + authorize(GET, "/api/v1/timelines/home", rf.hasScope("read:statuses")) + + authorize(GET, "/api/v2/filters", rf.hasScope("read:filters")) + authorize(POST, "/api/v2/filters", rf.hasScope("write:filters")) + + authorize(GET, "/api/v2/filters/*", rf.hasScope("read:filters")) + authorize(PUT, "/api/v2/filters/*", rf.hasScope("write:filters")) + authorize(DELETE, "/api/v2/filters/*", rf.hasScope("write:filters")) + + authorize(GET, "/api/v2/filters/*/keywords", rf.hasScope("read:filters")) + authorize(POST, "/api/v2/filters/*/keywords", rf.hasScope("write:filters")) + + authorize(GET, "/api/v2/filters/keywords/*", rf.hasScope("read:filters")) + authorize(PUT, "/api/v2/filters/keywords/*", rf.hasScope("write:filters")) + authorize(DELETE, "/api/v2/filters/keywords/*", rf.hasScope("write:filters")) + + authorize(GET, "/api/v2/filters/*/statuses", rf.hasScope("read:filters")) + authorize(POST, "/api/v2/filters/*/statuses", rf.hasScope("write:filters")) + + authorize(GET, "/api/v2/filters/statuses/*", rf.hasScope("read:filters")) + authorize(DELETE, "/api/v2/filters/statuses/*", rf.hasScope("write:filters")) + + authorize(GET, "/api/v1/filters", rf.hasScope("read:filters")) + authorize(POST, "/api/v1/filters", rf.hasScope("write:filters")) + + authorize(GET, "/api/v1/filters/*", rf.hasScope("read:filters")) + authorize(POST, "/api/v1/filters/*", rf.hasScope("write:filters")) + authorize(DELETE, "/api/v1/filters/*", rf.hasScope("write:filters")) + + authorize(GET, "/api/v1/notifications", rf.hasScope("read:notifications")) + authorize(GET, "/api/v1/notifications/*", rf.hasScope("read:notifications")) + authorize(POST, "/api/v1/notifications/clear", rf.hasScope("write:notifications")) + authorize(POST, "/api/v1/notifications/*/dismiss", rf.hasScope("write:notifications")) + + authorize(anyRequest, authenticated) + } + + oauth2ResourceServer { + jwt { } + } + + csrf { + ignoringRequestMatchers("/api/v1/apps") + } + } + + return http.build() + } + + @Bean + fun roleHierarchy(): RoleHierarchy { + val roleHierarchyImpl = RoleHierarchyImpl() + + roleHierarchyImpl.setHierarchy( + """ + SCOPE_read > SCOPE_read:accounts + SCOPE_read > SCOPE_read:accounts + SCOPE_read > SCOPE_read:blocks + SCOPE_read > SCOPE_read:bookmarks + SCOPE_read > SCOPE_read:favourites + SCOPE_read > SCOPE_read:filters + SCOPE_read > SCOPE_read:follows + SCOPE_read > SCOPE_read:lists + SCOPE_read > SCOPE_read:mutes + SCOPE_read > SCOPE_read:notifications + SCOPE_read > SCOPE_read:search + SCOPE_read > SCOPE_read:statuses + SCOPE_write > SCOPE_write:accounts + SCOPE_write > SCOPE_write:blocks + SCOPE_write > SCOPE_write:bookmarks + SCOPE_write > SCOPE_write:conversations + SCOPE_write > SCOPE_write:favourites + SCOPE_write > SCOPE_write:filters + SCOPE_write > SCOPE_write:follows + SCOPE_write > SCOPE_write:lists + SCOPE_write > SCOPE_write:media + SCOPE_write > SCOPE_write:mutes + SCOPE_write > SCOPE_write:notifications + SCOPE_write > SCOPE_write:reports + SCOPE_write > SCOPE_write:statuses + SCOPE_follow > SCOPE_write:blocks + SCOPE_follow > SCOPE_write:follows + SCOPE_follow > SCOPE_write:mutes + SCOPE_follow > SCOPE_read:blocks + SCOPE_follow > SCOPE_read:follows + SCOPE_follow > SCOPE_read:mutes + SCOPE_admin > SCOPE_admin:read + SCOPE_admin > SCOPE_admin:write + SCOPE_admin:read > SCOPE_admin:read:accounts + SCOPE_admin:read > SCOPE_admin:read:reports + SCOPE_admin:read > SCOPE_admin:read:domain_allows + SCOPE_admin:read > SCOPE_admin:read:domain_blocks + SCOPE_admin:read > SCOPE_admin:read:ip_blocks + SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks + SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks + SCOPE_admin:write > SCOPE_admin:write:accounts + SCOPE_admin:write > SCOPE_admin:write:reports + SCOPE_admin:write > SCOPE_admin:write:domain_allows + SCOPE_admin:write > SCOPE_admin:write:domain_blocks + SCOPE_admin:write > SCOPE_admin:write:ip_blocks + SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks + SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks + """.trimIndent() + ) + + return roleHierarchyImpl + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/external/RoleHierarchyAuthorizationManagerFactory.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/external/RoleHierarchyAuthorizationManagerFactory.kt new file mode 100644 index 00000000..66395c20 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/external/RoleHierarchyAuthorizationManagerFactory.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.external + +import org.springframework.security.access.hierarchicalroles.RoleHierarchy +import org.springframework.security.authorization.AuthorityAuthorizationManager +import org.springframework.security.authorization.AuthorizationManager +import org.springframework.security.web.access.intercept.RequestAuthorizationContext +import org.springframework.stereotype.Component + +@Component +class RoleHierarchyAuthorizationManagerFactory(private val roleHierarchy: RoleHierarchy) { + fun hasScope(role: String): AuthorizationManager { + val hasAuthority = AuthorityAuthorizationManager.hasAuthority("SCOPE_$role") + hasAuthority.setRoleHierarchy(roleHierarchy) + return hasAuthority + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt new file mode 100644 index 00000000..43ec4488 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* +import kotlinx.coroutines.flow.Flow +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringAccountApi : AccountApi { + override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { + return super.apiV1AccountsIdBlockPost(id) + } + + override suspend fun apiV1AccountsIdFollowPost( + id: String, + followRequestBody: FollowRequestBody?, + ): ResponseEntity { + return super.apiV1AccountsIdFollowPost(id, followRequestBody) + } + + override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity { + return super.apiV1AccountsIdGet(id) + } + + override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { + return super.apiV1AccountsIdMutePost(id) + } + + override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { + return super.apiV1AccountsIdRemoveFromFollowersPost(id) + } + + override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { + return super.apiV1AccountsIdUnblockPost(id) + } + + override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { + return super.apiV1AccountsIdUnfollowPost(id) + } + + override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity { + return super.apiV1AccountsIdUnmutePost(id) + } + + override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity { + return super.apiV1AccountsPost(accountsCreateRequest) + } + + override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): ResponseEntity { + return super.apiV1AccountsUpdateCredentialsPatch(updateCredentials) + } + + override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity { + return super.apiV1AccountsVerifyCredentialsGet() + } + + override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { + return super.apiV1FollowRequestsAccountIdAuthorizePost(accountId) + } + + override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity { + return super.apiV1FollowRequestsAccountIdRejectPost(accountId) + } + +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt new file mode 100644 index 00000000..95a03bdc --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.AppApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Application +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.AppsRequest +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringAppApi : AppApi { + override suspend fun apiV1AppsPost(appsRequest: AppsRequest): ResponseEntity { + return super.apiV1AppsPost(appsRequest) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt new file mode 100644 index 00000000..9a4e7a05 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.FilterApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* +import kotlinx.coroutines.flow.Flow +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringFilterApi : FilterApi { + + override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity { + return super.apiV1FiltersIdDelete(id) + } + + override suspend fun apiV1FiltersIdGet(id: String): ResponseEntity { + return super.apiV1FiltersIdGet(id) + } + + override suspend fun apiV1FiltersIdPut( + id: String, + phrase: String?, + context: List?, + irreversible: Boolean?, + wholeWord: Boolean?, + expiresIn: Int?, + ): ResponseEntity { + return super.apiV1FiltersIdPut(id, phrase, context, irreversible, wholeWord, expiresIn) + } + + override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity { + return super.apiV1FiltersPost(v1FilterPostRequest) + } + + override suspend fun apiV2FiltersFilterIdKeywordsPost( + filterId: String, + filterKeywordsPostRequest: FilterKeywordsPostRequest, + ): ResponseEntity { + return super.apiV2FiltersFilterIdKeywordsPost(filterId, filterKeywordsPostRequest) + } + + override suspend fun apiV2FiltersFilterIdStatusesPost( + filterId: String, + filterStatusRequest: FilterStatusRequest, + ): ResponseEntity { + return super.apiV2FiltersFilterIdStatusesPost(filterId, filterStatusRequest) + } + + override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity { + return super.apiV2FiltersIdDelete(id) + } + + override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity { + return super.apiV2FiltersIdGet(id) + } + + override suspend fun apiV2FiltersIdPut( + id: String, + title: String?, + context: List?, + filterAction: String?, + expiresIn: Int?, + keywordsAttributes: List?, + ): ResponseEntity { + return super.apiV2FiltersIdPut(id, title, context, filterAction, expiresIn, keywordsAttributes) + } + + override suspend fun apiV2FiltersKeywordsIdDelete(id: String): ResponseEntity { + return super.apiV2FiltersKeywordsIdDelete(id) + } + + override suspend fun apiV2FiltersKeywordsIdGet(id: String): ResponseEntity { + return super.apiV2FiltersKeywordsIdGet(id) + } + + override suspend fun apiV2FiltersKeywordsIdPut( + id: String, + keyword: String?, + wholeWord: Boolean?, + regex: Boolean?, + ): ResponseEntity { + return super.apiV2FiltersKeywordsIdPut(id, keyword, wholeWord, regex) + } + + override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity { + return super.apiV2FiltersPost(filterPostRequest) + } + + override suspend fun apiV2FiltersStatusesIdDelete(id: String): ResponseEntity { + return super.apiV2FiltersStatusesIdDelete(id) + } + + override suspend fun apiV2FiltersStatusesIdGet(id: String): ResponseEntity { + return super.apiV2FiltersStatusesIdGet(id) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringInstanceApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringInstanceApi.kt new file mode 100644 index 00000000..b4aa3cf8 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringInstanceApi.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.InstanceApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* +import kotlinx.coroutines.flow.Flow +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringInstanceApi : InstanceApi { + + override suspend fun apiV1InstanceExtendedDescriptionGet(): ResponseEntity { + return super.apiV1InstanceExtendedDescriptionGet() + } + + override suspend fun apiV1InstanceGet(): ResponseEntity { + return super.apiV1InstanceGet() + } + + override suspend fun apiV2InstanceGet(): ResponseEntity { + return super.apiV2InstanceGet() + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt new file mode 100644 index 00000000..087bcb1c --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.MediaApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller +import org.springframework.web.multipart.MultipartFile + +@Controller +class SpringMediaApi : MediaApi { + override suspend fun apiV1MediaPost( + file: MultipartFile, + thumbnail: MultipartFile?, + description: String?, + focus: String?, + ): ResponseEntity { + return super.apiV1MediaPost(file, thumbnail, description, focus) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringNotificationApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringNotificationApi.kt new file mode 100644 index 00000000..cd196658 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringNotificationApi.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.NotificationsApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Notification +import kotlinx.coroutines.flow.Flow +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringNotificationApi : NotificationsApi { + override suspend fun apiV1NotificationsClearPost(): ResponseEntity { + return super.apiV1NotificationsClearPost() + } + + override suspend fun apiV1NotificationsIdDismissPost(id: String): ResponseEntity { + return super.apiV1NotificationsIdDismissPost(id) + } + + override suspend fun apiV1NotificationsIdGet(id: String): ResponseEntity { + return super.apiV1NotificationsIdGet(id) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt new file mode 100644 index 00000000..3783b068 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequest +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringStatusApi : StatusApi { + override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity { + return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji) + } + + override suspend fun apiV1StatusesIdEmojiReactionsEmojiPut(id: String, emoji: String): ResponseEntity { + return super.apiV1StatusesIdEmojiReactionsEmojiPut(id, emoji) + } + + override suspend fun apiV1StatusesIdGet(id: String): ResponseEntity { + return super.apiV1StatusesIdGet(id) + } + + override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity { + return super.apiV1StatusesPost(statusesRequest) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt new file mode 100644 index 00000000..7220865f --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.interfaces.api + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.TimelineApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status +import kotlinx.coroutines.flow.Flow +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller + +@Controller +class SpringTimelineApi : TimelineApi { + +} \ No newline at end of file From cf48ae651b80427a4dfa9953e6af614da5a36c51 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 6 Jun 2024 23:26:20 +0900 Subject: [PATCH 26/54] =?UTF-8?q?feat:=20=E3=83=AD=E3=83=BC=E3=82=AB?= =?UTF-8?q?=E3=83=AB=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RegisterLocalActorApplicationService.kt | 6 +- .../application/RegisterApplication.kt | 26 +++ .../RegisterApplicationApplicationService.kt | 93 ++++++++++ .../application/RegisteredApplication.kt | 27 +++ .../InitLocalInstanceApplicationService.kt | 59 +++++++ .../hideout/core/config/SecurityConfig.kt | 111 ++++++++++++ .../hideout/core/config/SpringMvcConfig.kt | 46 +++++ .../domain/model/application/Application.kt | 22 +++ .../domain/model/application/ApplicationId.kt | 20 +++ .../model/application/ApplicationName.kt | 20 +++ .../application/ApplicationRepository.kt | 22 +++ .../local/LocalActorDomainServiceImpl.kt | 2 +- .../ExposedActorRepository.kt | 4 +- .../ExposedApplicationRepository.kt | 57 ++++++ .../factory/ActorFactoryImpl.kt | 2 +- .../oauth2/HideoutUserDetails.kt | 39 +++++ .../oauth2/UserDetailsServiceImpl.kt | 54 ++++++ .../interfaces/api/auth/AuthController.kt | 19 +- .../core/interfaces/api/auth/SignUpForm.kt | 2 +- .../db/migration/V1707799249__Filter.sql | 18 -- .../resources/db/migration/V1__Init_DB.sql | 162 +++--------------- hideout-core/src/main/resources/log4j2.xml | 3 +- .../src/main/resources/templates/sign_up.html | 13 +- .../mastodon/config/MastodonSecurityConfig.kt | 63 +------ .../mastodon/interfaces/api/SpringAppApi.kt | 24 ++- 25 files changed, 674 insertions(+), 240 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt delete mode 100644 hideout-core/src/main/resources/db/migration/V1707799249__Filter.sql diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index 6608a0e2..567bccde 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -28,6 +28,7 @@ import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainServic import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl import org.springframework.stereotype.Service +import java.net.URI @Service class RegisterLocalActorApplicationService( @@ -41,8 +42,8 @@ class RegisterLocalActorApplicationService( private val userDetailRepository: UserDetailRepository, private val idGenerateService: IdGenerateService, ) { - suspend fun register(registerLocalActor: RegisterLocalActor) { - transaction.transaction { + suspend fun register(registerLocalActor: RegisterLocalActor): URI { + return transaction.transaction { if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) { // todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ throw Exception("Username already exists") @@ -61,6 +62,7 @@ class RegisterLocalActorApplicationService( password = userDetailDomainService.hashPassword(registerLocalActor.password), ) userDetailRepository.save(userDetail) + actor.url } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt new file mode 100644 index 00000000..b1351109 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.application + +import java.net.URI + +data class RegisterApplication( + val name: String, + val redirectUris: Set, + val useRefreshToken: Boolean, + val scopes: Set, +) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt new file mode 100644 index 00000000..96f5867e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.application + +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.domain.model.application.Application +import dev.usbharu.hideout.core.domain.model.application.ApplicationId +import dev.usbharu.hideout.core.domain.model.application.ApplicationName +import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository +import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator +import org.springframework.security.oauth2.core.AuthorizationGrantType +import org.springframework.security.oauth2.core.ClientAuthenticationMethod +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository +import org.springframework.security.oauth2.server.authorization.settings.ClientSettings +import org.springframework.security.oauth2.server.authorization.settings.TokenSettings +import org.springframework.stereotype.Service +import java.time.Duration + +@Service +class RegisterApplicationApplicationService( + private val idGenerateService: IdGenerateService, + private val passwordEncoder: PasswordEncoder, + private val secureTokenGenerator: SecureTokenGenerator, + private val registeredClientRepository: RegisteredClientRepository, + private val transaction: Transaction, + private val applicationRepository: ApplicationRepository, +) { + suspend fun register(registerApplication: RegisterApplication): RegisteredApplication { + + return transaction.transaction { + + val id = idGenerateService.generateId() + val clientSecret = secureTokenGenerator.generate() + val registeredClient = RegisteredClient + .withId(id.toString()) + .clientId(id.toString()) + .clientSecret(passwordEncoder.encode(clientSecret)) + .clientName(registerApplication.name) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .apply { + if (registerApplication.useRefreshToken) { + authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + tokenSettings( + TokenSettings + .builder() + .accessTokenTimeToLive(Duration.ofSeconds(31536000000)) + .build() + ) + } + } + .redirectUris { set -> + set.addAll(registerApplication.redirectUris.map { it.toString() }) + } + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) + .scopes { it.addAll(registerApplication.scopes) } + .build() + registeredClientRepository.save(registeredClient) + + val application = Application(ApplicationId(id), ApplicationName(registerApplication.name)) + + applicationRepository.save(application) + RegisteredApplication( + id = id, + name = registerApplication.name, + clientSecret = clientSecret, + clientId = id, + redirectUris = registerApplication.redirectUris + ) + } + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt new file mode 100644 index 00000000..280afcf0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.application + +import java.net.URI + +data class RegisteredApplication( + val id: Long, + val name: String, + val redirectUris: Set, + val clientSecret: String, + val clientId: Long, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt new file mode 100644 index 00000000..d2123c5d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.instance + +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.instance.* +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService +import org.springframework.boot.context.event.ApplicationReadyEvent +import org.springframework.boot.info.BuildProperties +import org.springframework.context.event.EventListener +import org.springframework.stereotype.Service +import java.time.Instant + +@Service +class InitLocalInstanceApplicationService( + private val applicationConfig: ApplicationConfig, + private val instanceRepository: InstanceRepository, + private val idGenerateService: IdGenerateService, + private val buildProperties: BuildProperties, + private val transaction: Transaction, +) { + @EventListener(ApplicationReadyEvent::class) + suspend fun init() = transaction.transaction { + val findByUrl = instanceRepository.findByUrl(applicationConfig.url.toURI()) + + if (findByUrl == null) { + val instance = Instance( + InstanceId(idGenerateService.generateId()), + InstanceName(applicationConfig.url.host), + InstanceDescription(""), + applicationConfig.url.toURI(), + applicationConfig.url.toURI(), + null, + InstanceSoftware("hideout"), + InstanceVersion(buildProperties.version), + false, + false, + InstanceModerationNote(""), + Instant.now(), + ) + instanceRepository.save(instance) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt index f56f796b..9289f80b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -18,13 +18,124 @@ package dev.usbharu.hideout.core.config import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.core.annotation.Order +import org.springframework.http.HttpMethod.GET +import org.springframework.http.HttpMethod.POST +import org.springframework.jdbc.core.JdbcOperations +import org.springframework.security.access.hierarchicalroles.RoleHierarchy +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.annotation.web.invoke import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository +import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint @Configuration +@EnableWebSecurity(debug = false) class SecurityConfig { @Bean fun passwordEncoder(): PasswordEncoder { return BCryptPasswordEncoder() } + + @Bean + @Order(1) + fun oauth2Provider(http: HttpSecurity): SecurityFilterChain { + OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http) + http { + exceptionHandling { + authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/login") + } + } + return http.build() + } + + @Bean + @Order(3) + fun httpSecurityFilterChain(http: HttpSecurity): SecurityFilterChain { + http { + authorizeHttpRequests { + authorize("/error", permitAll) + authorize("/login", permitAll) + authorize(GET, "/.well-known/**", permitAll) + authorize(GET, "/nodeinfo/2.0", permitAll) + + authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS")) + authorize(POST, "/auth/sign_up", permitAll) + + authorize(anyRequest, authenticated) + } + formLogin { + + } + } + return http.build() + } + + @Bean + fun registeredClientRepository(jdbcOperations: JdbcOperations): RegisteredClientRepository { + return JdbcRegisteredClientRepository(jdbcOperations) + } + + @Bean + fun roleHierarchy(): RoleHierarchy { + val roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy( + """ + SCOPE_read > SCOPE_read:accounts + SCOPE_read > SCOPE_read:accounts + SCOPE_read > SCOPE_read:blocks + SCOPE_read > SCOPE_read:bookmarks + SCOPE_read > SCOPE_read:favourites + SCOPE_read > SCOPE_read:filters + SCOPE_read > SCOPE_read:follows + SCOPE_read > SCOPE_read:lists + SCOPE_read > SCOPE_read:mutes + SCOPE_read > SCOPE_read:notifications + SCOPE_read > SCOPE_read:search + SCOPE_read > SCOPE_read:statuses + SCOPE_write > SCOPE_write:accounts + SCOPE_write > SCOPE_write:blocks + SCOPE_write > SCOPE_write:bookmarks + SCOPE_write > SCOPE_write:conversations + SCOPE_write > SCOPE_write:favourites + SCOPE_write > SCOPE_write:filters + SCOPE_write > SCOPE_write:follows + SCOPE_write > SCOPE_write:lists + SCOPE_write > SCOPE_write:media + SCOPE_write > SCOPE_write:mutes + SCOPE_write > SCOPE_write:notifications + SCOPE_write > SCOPE_write:reports + SCOPE_write > SCOPE_write:statuses + SCOPE_follow > SCOPE_write:blocks + SCOPE_follow > SCOPE_write:follows + SCOPE_follow > SCOPE_write:mutes + SCOPE_follow > SCOPE_read:blocks + SCOPE_follow > SCOPE_read:follows + SCOPE_follow > SCOPE_read:mutes + SCOPE_admin > SCOPE_admin:read + SCOPE_admin > SCOPE_admin:write + SCOPE_admin:read > SCOPE_admin:read:accounts + SCOPE_admin:read > SCOPE_admin:read:reports + SCOPE_admin:read > SCOPE_admin:read:domain_allows + SCOPE_admin:read > SCOPE_admin:read:domain_blocks + SCOPE_admin:read > SCOPE_admin:read:ip_blocks + SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks + SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks + SCOPE_admin:write > SCOPE_admin:write:accounts + SCOPE_admin:write > SCOPE_admin:write:reports + SCOPE_admin:write > SCOPE_admin:write:domain_allows + SCOPE_admin:write > SCOPE_admin:write:domain_blocks + SCOPE_admin:write > SCOPE_admin:write:ip_blocks + SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks + SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks + """.trimIndent() + ) + + return roleHierarchyImpl + } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt new file mode 100644 index 00000000..00b6697d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.config + +import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.converter.HttpMessageConverter +import org.springframework.web.method.support.HandlerMethodArgumentResolver +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer +import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor +import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor + +@Configuration +class MvcConfigurer(private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor) : WebMvcConfigurer { + override fun addArgumentResolvers(resolvers: MutableList) { + resolvers.add(jsonOrFormModelMethodProcessor) + } +} + +@Configuration +class JsonOrFormModelMethodProcessorConfig { + @Bean + fun jsonOrFormModelMethodProcessor(converter: List>): JsonOrFormModelMethodProcessor { + return JsonOrFormModelMethodProcessor( + ServletModelAttributeMethodProcessor(true), + RequestResponseBodyMethodProcessor( + converter + ) + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt new file mode 100644 index 00000000..8cee8422 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.application + +class Application( + val applicationId: ApplicationId, + val name: ApplicationName, +) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationId.kt new file mode 100644 index 00000000..6daadcf0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationId.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.application + +@JvmInline +value class ApplicationId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationName.kt new file mode 100644 index 00000000..6b9f9a77 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationName.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.application + +@JvmInline +value class ApplicationName(val name: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt new file mode 100644 index 00000000..22d41d8f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.application + +interface ApplicationRepository { + suspend fun save(application: Application): Application + suspend fun delete(application: Application) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt index f00406e5..ef77d51b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt @@ -29,7 +29,7 @@ class LocalActorDomainServiceImpl( private val applicationConfig: ApplicationConfig, ) : LocalActorDomainService { override suspend fun usernameAlreadyUse(name: String): Boolean = - actorRepository.findByNameAndDomain(name, applicationConfig.url.host) == null + actorRepository.findByNameAndDomain(name, applicationConfig.url.host) != null override suspend fun generateKeyPair(): Pair { val keyPairGenerator = KeyPairGenerator.getInstance("RSA") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index 14d17c3f..d8f560c9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -135,10 +135,10 @@ object Actors : Table("actors") { } } -object ActorsAlsoKnownAs : Table("actor_alsoknwonas") { +object ActorsAlsoKnownAs : Table("actor_alsoknownas") { val actorId = long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) - val alsoKnownAs = long("alsoKnownAs").references(Actors.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + val alsoKnownAs = long("also_known_as").references(Actors.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) override val primaryKey: PrimaryKey = PrimaryKey(actorId, alsoKnownAs) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt new file mode 100644 index 00000000..7c04f9dc --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.model.application.Application +import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.upsert +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class ExposedApplicationRepository : ApplicationRepository, AbstractRepository() { + override suspend fun save(application: Application) = query { + Applications.upsert { + it[id] = application.applicationId.id + it[name] = application.name.name + } + application + } + + override suspend fun delete(application: Application): Unit = query { + Applications.deleteWhere { id eq application.applicationId.id } + } + + override val logger: Logger + get() = Companion.logger + + + companion object { + private val logger = LoggerFactory.getLogger(ExposedApplicationRepository::class.java) + } +} + + +object Applications : Table("applications") { + val id = long("id") + val name = varchar("name", 500) + override val primaryKey: PrimaryKey = PrimaryKey(id) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index d9e56e09..b03e208a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -45,7 +45,7 @@ class ActorFactoryImpl( description = ActorDescription(""), inbox = URI.create("$userUrl/inbox"), outbox = URI.create("$userUrl/outbox"), - url = applicationConfig.url.toURI(), + url = URI.create(userUrl), publicKey = keyPair.first, privateKey = keyPair.second, createdAt = Instant.now(), diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt new file mode 100644 index 00000000..2ba083e0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.userdetails.UserDetails + +class HideoutUserDetails( + private val authorities: MutableList, + private val password: String, + private val username: String, + val userDetailsId: Long, +) : UserDetails { + override fun getAuthorities(): MutableCollection { + return authorities + } + + override fun getPassword(): String { + return password + } + + override fun getUsername(): String { + return username + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt new file mode 100644 index 00000000..c7db5d46 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import kotlinx.coroutines.runBlocking +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.core.userdetails.UsernameNotFoundException +import org.springframework.stereotype.Component + +@Component +class UserDetailsServiceImpl( + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, + private val applicationConfig: ApplicationConfig, + private val transaction: Transaction, +) : UserDetailsService { + override fun loadUserByUsername(username: String?): UserDetails = runBlocking { + if (username == null) { + throw UsernameNotFoundException("Username not found") + } + transaction.transaction { + val actor = actorRepository.findByNameAndDomain(username, applicationConfig.url.host) + ?: throw UsernameNotFoundException("$username not found") + val userDetail = userDetailRepository.findByActorId(actor.id.id) + ?: throw UsernameNotFoundException("${actor.id.id} not found") + HideoutUserDetails( + authorities = mutableListOf(), + password = userDetail.password.password, + actor.name.name, + userDetailsId = userDetail.id.id + ) + } + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 7487f8b8..d9dc8331 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -16,16 +16,27 @@ package dev.usbharu.hideout.core.interfaces.api.auth -import org.springframework.ui.Model +import dev.usbharu.hideout.core.application.actor.RegisterLocalActor +import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService +import jakarta.servlet.http.HttpServletRequest +import org.springframework.stereotype.Controller import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.ModelAttribute import org.springframework.web.bind.annotation.PostMapping -interface AuthController { +@Controller +class AuthController(private val registerLocalActorApplicationService: RegisterLocalActorApplicationService) { @GetMapping("/auth/sign_up") - fun signUp(model: Model): String + fun signUp(): String { + return "sign_up" + } @PostMapping("/auth/sign_up") - suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String + suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String { + val registerLocalActor = RegisterLocalActor(signUpForm.username, signUpForm.password) + val uri = registerLocalActorApplicationService.register(registerLocalActor) + request.login(signUpForm.username, signUpForm.password) + return "redirect:$uri" + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt index 5c032c5a..d70eb9c2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/SignUpForm.kt @@ -3,5 +3,5 @@ package dev.usbharu.hideout.core.interfaces.api.auth data class SignUpForm( val username: String, val password: String, - val recaptchaResponse: String +// val recaptchaResponse: String ) diff --git a/hideout-core/src/main/resources/db/migration/V1707799249__Filter.sql b/hideout-core/src/main/resources/db/migration/V1707799249__Filter.sql deleted file mode 100644 index 3b05b856..00000000 --- a/hideout-core/src/main/resources/db/migration/V1707799249__Filter.sql +++ /dev/null @@ -1,18 +0,0 @@ -create table if not exists filters -( - id bigint primary key not null, - user_id bigint not null, - name varchar(255) not null, - context varchar(500) not null, - action varchar(255) not null, - constraint fk_filters_user_id__id foreign key (user_id) references actors (id) on delete cascade on update cascade -); - -create table if not exists filter_keywords -( - id bigint primary key not null, - filter_id bigint not null, - keyword varchar(1000) not null, - mode varchar(100) not null, - constraint fk_filter_keywords_filter_id__id foreign key (filter_id) references filters (id) on delete cascade on update cascade -); \ No newline at end of file diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 699a3357..03ce40a6 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -55,17 +55,27 @@ create table if not exists actors suspend boolean not null, move_to bigint null default null, emojis varchar(3000) not null default '', + deleted boolean not null default false, 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 ); +create table if not exists actor_alsoknownas +( + 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 +); + create table if not exists user_details ( id bigserial primary key, actor_id bigint not null unique, password varchar(255) not null, auto_accept_followee_follow_request boolean not null, + last_migration timestamp null default null, constraint fk_user_details_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict ); @@ -81,14 +91,6 @@ create table if not exists media mime_type varchar(255) not null, description varchar(4000) null ); -create table if not exists meta_info -( - id bigint primary key, - version varchar(1000) not null, - kid varchar(1000) not null, - jwt_private_key varchar(100000) not null, - jwt_public_key varchar(100000) not null -); create table if not exists posts ( id bigint primary key, @@ -134,100 +136,6 @@ alter table posts_emojis alter table posts_emojis add constraint fk_posts_emojis_emoji_id__id foreign key (emoji_id) references emojis (id) on delete cascade on update cascade; -create table if not exists reactions -( - id bigint primary key, - unicode_emoji varchar(255) null default null, - custom_emoji_id bigint null default null, - post_id bigint not null, - actor_id bigint not null, - unique (post_id, actor_id) -); -alter table reactions - add constraint fk_reactions_post_id__id foreign key (post_id) references posts (id) on delete restrict on update restrict; -alter table reactions - add constraint fk_reactions_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict; -alter table reactions - add constraint fk_reactions_custom_emoji_id__id foreign key (custom_emoji_id) references emojis (id) on delete cascade on update cascade; - -create table if not exists timelines -( - id bigint primary key, - user_id bigint not null, - timeline_id bigint not null, - post_id bigint not null, - post_actor_id bigint not null, - created_at bigint not null, - reply_id bigint null, - repost_id bigint null, - visibility int not null, - "sensitive" boolean not null, - is_local boolean not null, - is_pure_repost boolean not null, - media_ids varchar(255) not null, - emoji_ids varchar(255) not null -); - -create table if not exists application_authorization -( - id varchar(255) primary key, - registered_client_id varchar(255) not null, - principal_name varchar(255) not null, - authorization_grant_type varchar(255) not null, - authorized_scopes varchar(1000) default null null, - "attributes" varchar(4000) default null null, - "state" varchar(500) default null null, - authorization_code_value varchar(4000) default null null, - authorization_code_issued_at timestamp default null null, - authorization_code_expires_at timestamp default null null, - authorization_code_metadata varchar(2000) default null null, - access_token_value varchar(4000) default null null, - access_token_issued_at timestamp default null null, - access_token_expires_at timestamp default null null, - access_token_metadata varchar(2000) default null null, - access_token_type varchar(255) default null null, - access_token_scopes varchar(1000) default null null, - refresh_token_value varchar(4000) default null null, - refresh_token_issued_at timestamp default null null, - refresh_token_expires_at timestamp default null null, - refresh_token_metadata varchar(2000) default null null, - oidc_id_token_value varchar(4000) default null null, - oidc_id_token_issued_at timestamp default null null, - oidc_id_token_expires_at timestamp default null null, - oidc_id_token_metadata varchar(2000) default null null, - oidc_id_token_claims varchar(2000) default null null, - user_code_value varchar(4000) default null null, - user_code_issued_at timestamp default null null, - user_code_expires_at timestamp default null null, - user_code_metadata varchar(2000) default null null, - device_code_value varchar(4000) default null null, - device_code_issued_at timestamp default null null, - device_code_expires_at timestamp default null null, - device_code_metadata varchar(2000) default null null -); -create table if not exists oauth2_authorization_consent -( - registered_client_id varchar(100), - principal_name varchar(200), - authorities varchar(1000) not null, - constraint pk_oauth2_authorization_consent primary key (registered_client_id, principal_name) -); -create table if not exists registered_client -( - id varchar(100) primary key, - client_id varchar(100) not null, - client_id_issued_at timestamp default current_timestamp not null, - client_secret varchar(200) default null null, - client_secret_expires_at timestamp default null null, - client_name varchar(200) not null, - client_authentication_methods varchar(1000) not null, - authorization_grant_types varchar(1000) not null, - redirect_uris varchar(1000) default null null, - post_logout_redirect_uris varchar(1000) default null null, - scopes varchar(1000) not null, - client_settings varchar(2000) not null, - token_settings varchar(2000) not null -); create table if not exists relationships ( @@ -254,40 +162,26 @@ insert into actors (id, name, domain, screen_name, description, inbox, outbox, u values (0, '', '', '', '', '', '', '', '', null, current_timestamp, '', null, null, 0, true, null, null, 0, null, current_timestamp, false, null, ''); -create table if not exists deleted_actors +create table if not exists applications ( - id bigint primary key, - "name" varchar(300) not null, - domain varchar(255) not null, - public_key varchar(10000) not null, - deleted_at timestamp not null, - unique ("name", domain) + id bigint primary key, + name varchar(500) not null ); -create table if not exists notifications +create table if not exists oauth2_registered_client ( - id bigint primary key, - type varchar(100) not null, - user_id bigint not null, - source_actor_id bigint null, - post_id bigint null, - text varchar(3000) null, - reaction_id bigint null, - created_at timestamp not null, - constraint fk_notifications_user_id__id foreign key (user_id) references actors (id) on delete cascade on update cascade, - constraint fk_notifications_source_actor__id foreign key (source_actor_id) references actors (id) on delete cascade on update cascade, - constraint fk_notifications_post_id__id foreign key (post_id) references posts (id) on delete cascade on update cascade, - constraint fk_notifications_reaction_id__id foreign key (reaction_id) references reactions (id) on delete cascade on update cascade + id varchar(100) NOT NULL, + client_id varchar(100) NOT NULL, + client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, + client_secret varchar(200) DEFAULT NULL, + client_secret_expires_at timestamp DEFAULT NULL, + client_name varchar(200) NOT NULL, + client_authentication_methods varchar(1000) NOT NULL, + authorization_grant_types varchar(1000) NOT NULL, + redirect_uris varchar(1000) DEFAULT NULL, + post_logout_redirect_uris varchar(1000) DEFAULT NULL, + scopes varchar(1000) NOT NULL, + client_settings varchar(2000) NOT NULL, + token_settings varchar(2000) NOT NULL, + PRIMARY KEY (id) ); - -create table if not exists mastodon_notifications -( - id bigint primary key, - user_id bigint not null, - type varchar(100) not null, - created_at timestamp not null, - account_id bigint not null, - status_id bigint null, - report_id bigint null, - relationship_serverance_event_id bigint null -) \ No newline at end of file diff --git a/hideout-core/src/main/resources/log4j2.xml b/hideout-core/src/main/resources/log4j2.xml index 7c9b917d..6d467f2d 100644 --- a/hideout-core/src/main/resources/log4j2.xml +++ b/hideout-core/src/main/resources/log4j2.xml @@ -6,9 +6,10 @@ - + + \ No newline at end of file diff --git a/hideout-core/src/main/resources/templates/sign_up.html b/hideout-core/src/main/resources/templates/sign_up.html index 3408bb8a..63f57c5a 100644 --- a/hideout-core/src/main/resources/templates/sign_up.html +++ b/hideout-core/src/main/resources/templates/sign_up.html @@ -3,23 +3,12 @@ SignUp - - -
+ -
diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt index 2819b80a..513994b1 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/config/MastodonSecurityConfig.kt @@ -21,8 +21,6 @@ import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.annotation.Order import org.springframework.http.HttpMethod.* -import org.springframework.security.access.hierarchicalroles.RoleHierarchy -import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.invoke import org.springframework.security.web.SecurityFilterChain @@ -30,7 +28,7 @@ import org.springframework.security.web.SecurityFilterChain @Configuration class MastodonSecurityConfig { @Bean - @Order(4) + @Order(2) @Suppress("LongMethod") fun mastodonApiSecurityFilterChain( http: HttpSecurity, @@ -111,63 +109,4 @@ class MastodonSecurityConfig { return http.build() } - - @Bean - fun roleHierarchy(): RoleHierarchy { - val roleHierarchyImpl = RoleHierarchyImpl() - - roleHierarchyImpl.setHierarchy( - """ - SCOPE_read > SCOPE_read:accounts - SCOPE_read > SCOPE_read:accounts - SCOPE_read > SCOPE_read:blocks - SCOPE_read > SCOPE_read:bookmarks - SCOPE_read > SCOPE_read:favourites - SCOPE_read > SCOPE_read:filters - SCOPE_read > SCOPE_read:follows - SCOPE_read > SCOPE_read:lists - SCOPE_read > SCOPE_read:mutes - SCOPE_read > SCOPE_read:notifications - SCOPE_read > SCOPE_read:search - SCOPE_read > SCOPE_read:statuses - SCOPE_write > SCOPE_write:accounts - SCOPE_write > SCOPE_write:blocks - SCOPE_write > SCOPE_write:bookmarks - SCOPE_write > SCOPE_write:conversations - SCOPE_write > SCOPE_write:favourites - SCOPE_write > SCOPE_write:filters - SCOPE_write > SCOPE_write:follows - SCOPE_write > SCOPE_write:lists - SCOPE_write > SCOPE_write:media - SCOPE_write > SCOPE_write:mutes - SCOPE_write > SCOPE_write:notifications - SCOPE_write > SCOPE_write:reports - SCOPE_write > SCOPE_write:statuses - SCOPE_follow > SCOPE_write:blocks - SCOPE_follow > SCOPE_write:follows - SCOPE_follow > SCOPE_write:mutes - SCOPE_follow > SCOPE_read:blocks - SCOPE_follow > SCOPE_read:follows - SCOPE_follow > SCOPE_read:mutes - SCOPE_admin > SCOPE_admin:read - SCOPE_admin > SCOPE_admin:write - SCOPE_admin:read > SCOPE_admin:read:accounts - SCOPE_admin:read > SCOPE_admin:read:reports - SCOPE_admin:read > SCOPE_admin:read:domain_allows - SCOPE_admin:read > SCOPE_admin:read:domain_blocks - SCOPE_admin:read > SCOPE_admin:read:ip_blocks - SCOPE_admin:read > SCOPE_admin:read:email_domain_blocks - SCOPE_admin:read > SCOPE_admin:read:canonical_email_blocks - SCOPE_admin:write > SCOPE_admin:write:accounts - SCOPE_admin:write > SCOPE_admin:write:reports - SCOPE_admin:write > SCOPE_admin:write:domain_allows - SCOPE_admin:write > SCOPE_admin:write:domain_blocks - SCOPE_admin:write > SCOPE_admin:write:ip_blocks - SCOPE_admin:write > SCOPE_admin:write:email_domain_blocks - SCOPE_admin:write > SCOPE_admin:write:canonical_email_blocks - """.trimIndent() - ) - - return roleHierarchyImpl - } } \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt index 95a03bdc..22d0a4a6 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt @@ -16,15 +16,35 @@ package dev.usbharu.hideout.mastodon.interfaces.api +import dev.usbharu.hideout.core.application.application.RegisterApplication +import dev.usbharu.hideout.core.application.application.RegisterApplicationApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.AppApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Application import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.AppsRequest import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller +import java.net.URI @Controller -class SpringAppApi : AppApi { +class SpringAppApi(private val registerApplicationApplicationService: RegisterApplicationApplicationService) : AppApi { override suspend fun apiV1AppsPost(appsRequest: AppsRequest): ResponseEntity { - return super.apiV1AppsPost(appsRequest) + + val registerApplication = RegisterApplication( + appsRequest.clientName, + setOf(URI.create(appsRequest.redirectUris)), + false, + appsRequest.scopes?.split(" ").orEmpty().toSet() + ) + val registeredApplication = registerApplicationApplicationService.register(registerApplication) + return ResponseEntity.ok( + Application( + registeredApplication.name, + "invalid-vapid-key", + null, + registeredApplication.clientId.toString(), + registeredApplication.clientSecret, + appsRequest.redirectUris + ) + ) } } \ No newline at end of file From a13fe45d0d4ff1747afe59112488dde8f90063ad Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 00:37:34 +0900 Subject: [PATCH 27/54] =?UTF-8?q?feat:=20=E3=82=A2=E3=83=97=E3=83=AA?= =?UTF-8?q?=E3=82=B1=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=B5=E3=83=BC?= =?UTF-8?q?=E3=83=93=E3=82=B9=E3=81=AE=E6=93=8D=E4=BD=9C=E3=83=AD=E3=82=B0?= =?UTF-8?q?=E3=82=92=E6=AE=8B=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RegisterLocalActorApplicationService.kt | 50 +++++++++++-------- .../shared/AbstractApplicationService.kt | 45 +++++++++++++++++ .../application/shared/ApplicationService.kt | 21 ++++++++ .../application/shared/CommandExecutor.kt | 21 ++++++++ .../springframework/HttpCommandExecutor.kt | 29 +++++++++++ .../SpringMvcCommandExecutorFactory.kt | 32 ++++++++++++ .../interfaces/api/auth/AuthController.kt | 11 +++- 7 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index 567bccde..2e172e83 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -16,6 +16,8 @@ package dev.usbharu.hideout.core.application.actor +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.config.ApplicationConfig import dev.usbharu.hideout.core.domain.model.actor.ActorRepository @@ -27,12 +29,13 @@ import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainServi import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.net.URI @Service class RegisterLocalActorApplicationService( - private val transaction: Transaction, + transaction: Transaction, private val actorDomainService: LocalActorDomainService, private val actorRepository: ActorRepository, private val actorFactoryImpl: ActorFactoryImpl, @@ -41,28 +44,31 @@ class RegisterLocalActorApplicationService( private val userDetailDomainService: UserDetailDomainService, private val userDetailRepository: UserDetailRepository, private val idGenerateService: IdGenerateService, -) { - suspend fun register(registerLocalActor: RegisterLocalActor): URI { - return transaction.transaction { - if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) { - // todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ - throw Exception("Username already exists") - } - val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! +) : AbstractApplicationService(transaction, Companion.logger) { - val actor = actorFactoryImpl.createLocal( - registerLocalActor.name, - actorDomainService.generateKeyPair(), - instance.id - ) - actorRepository.save(actor) - val userDetail = UserDetail.create( - id = UserDetailId(idGenerateService.generateId()), - actorId = actor.id, - password = userDetailDomainService.hashPassword(registerLocalActor.password), - ) - userDetailRepository.save(userDetail) - actor.url + override suspend fun internalExecute(command: RegisterLocalActor, executor: CommandExecutor): URI { + if (actorDomainService.usernameAlreadyUse(command.name)) { + // todo é©åˆ‡ãªä¾‹å¤–を考ãˆã‚‹ + throw Exception("Username already exists") } + val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! + + val actor = actorFactoryImpl.createLocal( + command.name, + actorDomainService.generateKeyPair(), + instance.id + ) + actorRepository.save(actor) + val userDetail = UserDetail.create( + id = UserDetailId(idGenerateService.generateId()), + actorId = actor.id, + password = userDetailDomainService.hashPassword(command.password), + ) + userDetailRepository.save(userDetail) + return actor.url + } + + companion object { + val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt new file mode 100644 index 00000000..9e34cc91 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.shared + +import kotlinx.coroutines.CancellationException +import org.slf4j.Logger + +abstract class AbstractApplicationService( + protected val transaction: Transaction, + protected val logger: Logger, +) : ApplicationService { + override suspend fun execute(command: T, executor: CommandExecutor): R { + return try { + logger.debug("START ${command::class.simpleName} by $executor") + val response = transaction.transaction { + internalExecute(command, executor) + } + logger.info("SUCCESS ${command::class.simpleName} by ${executor.executor}") + response + } catch (e: CancellationException) { + logger.debug("Coroutine canceled", e) + throw e + } catch (e: Exception) { + logger.warn("Command execution error", e) + throw e + } + + } + + protected abstract suspend fun internalExecute(command: T, executor: CommandExecutor): R +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt new file mode 100644 index 00000000..86f66991 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.shared + +interface ApplicationService { + suspend fun execute(command: T, executor: CommandExecutor): R +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt new file mode 100644 index 00000000..d022254f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.shared + +interface CommandExecutor { + val executor: String +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt new file mode 100644 index 00000000..fe315bf4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework + +import dev.usbharu.hideout.core.application.shared.CommandExecutor + +open class HttpCommandExecutor( + override val executor: String, + val ip: String, + val userAgent: String, +) : CommandExecutor { + override fun toString(): String { + return "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')" + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt new file mode 100644 index 00000000..63617376 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework + +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.stereotype.Component +import org.springframework.web.context.request.RequestContextHolder +import org.springframework.web.context.request.ServletRequestAttributes + + +@Component +class SpringMvcCommandExecutorFactory { + fun getCommandExecutor(): HttpCommandExecutor { + val name = SecurityContextHolder.getContext().authentication?.name ?: "ANONYMOUS" + val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request + return HttpCommandExecutor(name, request.remoteAddr, request.getHeader("user-agent").orEmpty()) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index d9dc8331..761dacde 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -18,6 +18,7 @@ package dev.usbharu.hideout.core.interfaces.api.auth import dev.usbharu.hideout.core.application.actor.RegisterLocalActor import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService +import dev.usbharu.hideout.core.infrastructure.springframework.SpringMvcCommandExecutorFactory import jakarta.servlet.http.HttpServletRequest import org.springframework.stereotype.Controller import org.springframework.validation.annotation.Validated @@ -26,7 +27,10 @@ import org.springframework.web.bind.annotation.ModelAttribute import org.springframework.web.bind.annotation.PostMapping @Controller -class AuthController(private val registerLocalActorApplicationService: RegisterLocalActorApplicationService) { +class AuthController( + private val registerLocalActorApplicationService: RegisterLocalActorApplicationService, + private val springMvcCommandExecutorFactory: SpringMvcCommandExecutorFactory, +) { @GetMapping("/auth/sign_up") fun signUp(): String { return "sign_up" @@ -35,7 +39,10 @@ class AuthController(private val registerLocalActorApplicationService: RegisterL @PostMapping("/auth/sign_up") suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String { val registerLocalActor = RegisterLocalActor(signUpForm.username, signUpForm.password) - val uri = registerLocalActorApplicationService.register(registerLocalActor) + val uri = registerLocalActorApplicationService.execute( + registerLocalActor, + springMvcCommandExecutorFactory.getCommandExecutor() + ) request.login(signUpForm.username, signUpForm.password) return "redirect:$uri" } From 97d9bf0898eef78807d1ab700f286db631248b91 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:05:55 +0900 Subject: [PATCH 28/54] =?UTF-8?q?feat:=20OAuth2=E3=81=A7=E3=83=88=E3=83=BC?= =?UTF-8?q?=E3=82=AF=E3=83=B3=E3=81=AE=E7=99=BA=E8=A1=8C=E3=81=8C=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RegisterApplicationApplicationService.kt | 3 +- .../application/RegisteredApplication.kt | 2 +- .../hideout/core/config/SecurityConfig.kt | 19 +++- .../HideoutJdbcOauth2AuthorizationService.kt | 46 +++++++++ .../oauth2/HideoutUserDetails.kt | 94 ++++++++++++++++++- .../oauth2/UserDetailsServiceImpl.kt | 2 +- .../resources/db/migration/V1__Init_DB.sql | 48 +++++++++- hideout-core/src/main/resources/log4j2.xml | 2 +- .../mastodon/interfaces/api/SpringAppApi.kt | 4 +- 9 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt index 96f5867e..d287b28a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt @@ -61,6 +61,7 @@ class RegisterApplicationApplicationService( .apply { if (registerApplication.useRefreshToken) { authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + } else { tokenSettings( TokenSettings .builder() @@ -84,7 +85,7 @@ class RegisterApplicationApplicationService( id = id, name = registerApplication.name, clientSecret = clientSecret, - clientId = id, + clientId = id.toString(), redirectUris = registerApplication.redirectUris ) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt index 280afcf0..a5a18032 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisteredApplication.kt @@ -23,5 +23,5 @@ data class RegisteredApplication( val name: String, val redirectUris: Set, val clientSecret: String, - val clientId: Long, + val clientId: String, ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt index 9289f80b..805b7730 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -29,14 +29,17 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.invoke import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration +import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint @Configuration -@EnableWebSecurity(debug = false) +@EnableWebSecurity(debug = true) class SecurityConfig { @Bean fun passwordEncoder(): PasswordEncoder { @@ -82,6 +85,20 @@ class SecurityConfig { return JdbcRegisteredClientRepository(jdbcOperations) } + @Bean + fun oauth2AuthorizationConsentService( + jdbcOperations: JdbcOperations, + registeredClientRepository: RegisteredClientRepository, + ): OAuth2AuthorizationConsentService { + return JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository) + } + + @Bean + fun authorizationServerSettings(): AuthorizationServerSettings { + return AuthorizationServerSettings.builder().authorizationEndpoint("/oauth/authorize") + .tokenEndpoint("/oauth/token").tokenRevocationEndpoint("/oauth/revoke").build() + } + @Bean fun roleHierarchy(): RoleHierarchy { val roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy( diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt new file mode 100644 index 00000000..20c65b68 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jdbc.core.JdbcOperations +import org.springframework.jdbc.support.lob.DefaultLobHandler +import org.springframework.jdbc.support.lob.LobHandler +import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository +import org.springframework.stereotype.Component + +@Component +class HideoutJdbcOauth2AuthorizationService( + registeredClientRepository: RegisteredClientRepository, + jdbcOperations: JdbcOperations, + @Autowired(required = false) lobHandler: LobHandler = DefaultLobHandler(), +) : JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository, lobHandler) { + + + + init { + super.setAuthorizationRowMapper(HideoutOAuth2AuthorizationRowMapper(registeredClientRepository = registeredClientRepository)) + } + + class HideoutOAuth2AuthorizationRowMapper(registeredClientRepository: RegisteredClientRepository?) : + OAuth2AuthorizationRowMapper(registeredClientRepository) { + init { + objectMapper.addMixIn(HideoutUserDetails::class.java, UserDetailsMixin::class.java) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt index 2ba083e0..a85b948b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt @@ -16,16 +16,31 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.annotation.JsonDeserialize import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.userdetails.UserDetails +import java.io.Serial +import java.util.* class HideoutUserDetails( - private val authorities: MutableList, + authorities: Set, private val password: String, private val username: String, val userDetailsId: Long, ) : UserDetails { - override fun getAuthorities(): MutableCollection { + private val authorities: MutableSet = Collections.unmodifiableSet(authorities) + override fun getAuthorities(): MutableSet { return authorities } @@ -36,4 +51,79 @@ class HideoutUserDetails( override fun getUsername(): String { return username } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as HideoutUserDetails + + if (authorities != other.authorities) return false + if (password != other.password) return false + if (username != other.username) return false + if (userDetailsId != other.userDetailsId) return false + + return true + } + + override fun hashCode(): Int { + var result = authorities.hashCode() + result = 31 * result + password.hashCode() + result = 31 * result + username.hashCode() + result = 31 * result + userDetailsId.hashCode() + return result + } + + override fun toString(): String { + return "HideoutUserDetails(authorities=$authorities, password='$password', username='$username', userDetailsId=$userDetailsId)" + } + + companion object { + @Serial + private const val serialVersionUID = -899168205656607781L + } +} + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonDeserialize(using = UserDetailsDeserializer::class) +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE +) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSubTypes +@Suppress("UnnecessaryAbstractClass") +abstract class UserDetailsMixin + +class UserDetailsDeserializer : JsonDeserializer() { + + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): HideoutUserDetails { + val mapper = p.codec as ObjectMapper + val jsonNode: JsonNode = mapper.readTree(p) + val authorities: Set = mapper.convertValue( + jsonNode["authorities"], + SIMPLE_GRANTED_AUTHORITY_SET + ) + + val password = jsonNode.readText("password") + return HideoutUserDetails( + userDetailsId = jsonNode["userDetailsId"].longValue(), + username = jsonNode.readText("username"), + password = password, + authorities = authorities + ) + } + + fun JsonNode.readText(field: String, defaultValue: String = ""): String { + return when { + has(field) -> get(field).asText(defaultValue) + else -> defaultValue + } + } + + companion object { + private val SIMPLE_GRANTED_AUTHORITY_SET = object : TypeReference>() {} + } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt index c7db5d46..505be86c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -43,7 +43,7 @@ class UserDetailsServiceImpl( val userDetail = userDetailRepository.findByActorId(actor.id.id) ?: throw UsernameNotFoundException("${actor.id.id} not found") HideoutUserDetails( - authorities = mutableListOf(), + authorities = HashSet(), password = userDetail.password.password, actor.name.name, userDetailsId = userDetail.id.id diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 03ce40a6..3f76c59b 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -55,7 +55,7 @@ create table if not exists actors suspend boolean not null, move_to bigint null default null, emojis varchar(3000) not null default '', - deleted boolean not null default false, + deleted boolean not null default false, 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 @@ -185,3 +185,49 @@ create table if not exists oauth2_registered_client token_settings varchar(2000) NOT NULL, PRIMARY KEY (id) ); + +CREATE TABLE if not exists oauth2_authorization_consent +( + registered_client_id varchar(100) NOT NULL, + principal_name varchar(200) NOT NULL, + authorities varchar(1000) NOT NULL, + PRIMARY KEY (registered_client_id, principal_name) +); + +CREATE TABLE oauth2_authorization +( + id varchar(100) NOT NULL, + registered_client_id varchar(100) NOT NULL, + principal_name varchar(200) NOT NULL, + authorization_grant_type varchar(100) NOT NULL, + authorized_scopes varchar(1000) DEFAULT NULL, + attributes varchar(4000) DEFAULT NULL, + state varchar(500) DEFAULT NULL, + authorization_code_value varchar(4000) DEFAULT NULL, + authorization_code_issued_at timestamp DEFAULT NULL, + authorization_code_expires_at timestamp DEFAULT NULL, + authorization_code_metadata varchar(4000) DEFAULT NULL, + access_token_value varchar(4000) DEFAULT NULL, + access_token_issued_at timestamp DEFAULT NULL, + access_token_expires_at timestamp DEFAULT NULL, + access_token_metadata varchar(4000) DEFAULT NULL, + access_token_type varchar(100) DEFAULT NULL, + access_token_scopes varchar(1000) DEFAULT NULL, + oidc_id_token_value varchar(4000) DEFAULT NULL, + oidc_id_token_issued_at timestamp DEFAULT NULL, + oidc_id_token_expires_at timestamp DEFAULT NULL, + oidc_id_token_metadata varchar(4000) DEFAULT NULL, + refresh_token_value varchar(4000) DEFAULT NULL, + refresh_token_issued_at timestamp DEFAULT NULL, + refresh_token_expires_at timestamp DEFAULT NULL, + refresh_token_metadata varchar(4000) DEFAULT NULL, + user_code_value varchar(4000) DEFAULT NULL, + user_code_issued_at timestamp DEFAULT NULL, + user_code_expires_at timestamp DEFAULT NULL, + user_code_metadata varchar(4000) DEFAULT NULL, + device_code_value varchar(4000) DEFAULT NULL, + device_code_issued_at timestamp DEFAULT NULL, + device_code_expires_at timestamp DEFAULT NULL, + device_code_metadata varchar(4000) DEFAULT NULL, + PRIMARY KEY (id) +); diff --git a/hideout-core/src/main/resources/log4j2.xml b/hideout-core/src/main/resources/log4j2.xml index 6d467f2d..834d3910 100644 --- a/hideout-core/src/main/resources/log4j2.xml +++ b/hideout-core/src/main/resources/log4j2.xml @@ -6,7 +6,7 @@ - + diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt index 22d0a4a6..cc870055 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt @@ -33,7 +33,7 @@ class SpringAppApi(private val registerApplicationApplicationService: RegisterAp appsRequest.clientName, setOf(URI.create(appsRequest.redirectUris)), false, - appsRequest.scopes?.split(" ").orEmpty().toSet() + appsRequest.scopes?.split(" ").orEmpty().toSet().ifEmpty { setOf("read") } ) val registeredApplication = registerApplicationApplicationService.register(registerApplication) return ResponseEntity.ok( @@ -41,7 +41,7 @@ class SpringAppApi(private val registerApplicationApplicationService: RegisterAp registeredApplication.name, "invalid-vapid-key", null, - registeredApplication.clientId.toString(), + registeredApplication.clientId, registeredApplication.clientSecret, appsRequest.redirectUris ) From e14a4aa89a3662fd0b84f548d3dc226195acdf4a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:03:53 +0900 Subject: [PATCH 29/54] wip --- ...WithHttpSignatureSecurityContextFactory.kt | 2 +- .../RegisterLocalPostApplicationService.kt | 35 ++++++++----- .../hideout/core/config/SecurityConfig.kt | 20 +++++++ .../FailedToGetResourcesException.kt | 31 ----------- .../exception/HttpSignatureVerifyException.kt | 30 ----------- .../core/domain/exception/NotInitException.kt | 31 ----------- .../domain/exception/UserNotFoundException.kt | 31 ----------- .../exception/media/MediaConvertException.kt | 37 ------------- .../domain/exception/media/MediaException.kt | 38 -------------- .../exception/media/MediaFileSizeException.kt | 37 ------------- .../media/MediaFileSizeIsZeroException.kt | 37 ------------- .../exception/media/MediaProcessException.kt | 37 ------------- .../exception/media/MediaSaveException.kt | 30 ----------- .../media/RemoteMediaFileSizeException.kt | 37 ------------- .../media/UnsupportedMediaException.kt | 37 ------------- .../resource/PostNotFoundException.kt | 43 --------------- .../resource/UserNotFoundException.kt | 52 ------------------- .../local/LocalUserNotFoundException.kt | 47 ----------------- .../hideout/worker/ReceiveFollowTaskRunner.kt | 2 +- 19 files changed, 44 insertions(+), 570 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/FailedToGetResourcesException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HttpSignatureVerifyException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/NotInitException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/UserNotFoundException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaSaveException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/RemoteMediaFileSizeException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt diff --git a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt index ab161ca7..7ec4aaa5 100644 --- a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt +++ b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt @@ -17,7 +17,7 @@ package util import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index 8aac1a77..3b6dcd8c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -16,6 +16,9 @@ package dev.usbharu.hideout.core.application.post +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.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.media.MediaId @@ -23,6 +26,8 @@ import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service @@ -30,20 +35,24 @@ class RegisterLocalPostApplicationService( private val postFactory: PostFactoryImpl, private val actorRepository: ActorRepository, private val postRepository: PostRepository, -) { - suspend fun register(registerLocalPost: RegisterLocalPost) { - val actorId = ActorId(registerLocalPost.actorId) - val post = postFactory.createLocal( - actorId, + transaction: Transaction, +) : AbstractApplicationService(transaction, Companion.logger) { + + companion object { + val logger: Logger = LoggerFactory.getLogger(RegisterLocalPostApplicationService::class.java) + } + + override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor) { + val actorId = ActorId(command.actorId) + val post = postFactory.createLocal(actorId, actorRepository.findById(actorId)!!.name, - PostOverview(registerLocalPost.overview), - registerLocalPost.content, - registerLocalPost.visibility, - registerLocalPost.repostId?.let { PostId(it) }, - registerLocalPost.replyId?.let { PostId(it) }, - registerLocalPost.sensitive, - registerLocalPost.mediaIds.map { MediaId(it) } - ) + PostOverview(command.overview), + command.content, + command.visibility, + command.repostId?.let { PostId(it) }, + command.replyId?.let { PostId(it) }, + command.sensitive, + command.mediaIds.map { MediaId(it) }) postRepository.save(post) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt index 805b7730..cd4051d9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.config +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.HideoutUserDetails import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.annotation.Order @@ -27,14 +28,19 @@ import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.invoke +import org.springframework.security.core.Authentication import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.oauth2.core.AuthorizationGrantType import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService +import org.springframework.security.oauth2.server.authorization.OAuth2TokenType import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings +import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint @@ -99,6 +105,20 @@ class SecurityConfig { .tokenEndpoint("/oauth/token").tokenRevocationEndpoint("/oauth/revoke").build() } + @Bean + fun jwtTokenCustomizer(): OAuth2TokenCustomizer { + return OAuth2TokenCustomizer { context: JwtEncodingContext -> + + if (OAuth2TokenType.ACCESS_TOKEN == context.tokenType && + context.authorization?.authorizationGrantType == AuthorizationGrantType.AUTHORIZATION_CODE + ) { + val userDetailsImpl = context.getPrincipal().principal as HideoutUserDetails + context.claims.claim("uid", userDetailsImpl.userDetailsId.toString()) + } + } + } + + @Bean fun roleHierarchy(): RoleHierarchy { val roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy( diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/FailedToGetResourcesException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/FailedToGetResourcesException.kt deleted file mode 100644 index cda900d4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/FailedToGetResourcesException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception - -import java.io.Serial - -open class FailedToGetResourcesException : IllegalArgumentException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = -3117221954866309059L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HttpSignatureVerifyException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HttpSignatureVerifyException.kt deleted file mode 100644 index ef22cec4..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/HttpSignatureVerifyException.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception - -import java.io.Serial -import javax.naming.AuthenticationException - -class HttpSignatureVerifyException : AuthenticationException { - constructor() : super() - constructor(s: String?) : super(s) - - companion object { - @Serial - private const val serialVersionUID: Long = 1484943321770741944L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/NotInitException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/NotInitException.kt deleted file mode 100644 index 8118eb7e..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/NotInitException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception - -import java.io.Serial - -class NotInitException : IllegalStateException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = -5859046179473905716L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/UserNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/UserNotFoundException.kt deleted file mode 100644 index a70c9256..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/UserNotFoundException.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception - -import java.io.Serial - -class UserNotFoundException : IllegalArgumentException { - constructor() : super() - constructor(s: String?) : super(s) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - - companion object { - @Serial - private const val serialVersionUID: Long = 6343548635914580823L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt deleted file mode 100644 index 08e693c9..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaConvertException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -open class MediaConvertException : MediaException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -6349105549968160551L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt deleted file mode 100644 index b406ae99..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaException.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -@Suppress("UnnecessaryAbstractClass") -abstract class MediaException : RuntimeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 5988922562494187852L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt deleted file mode 100644 index 7a99d7c3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -open class MediaFileSizeException : MediaException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 8672626879026555064L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt deleted file mode 100644 index 12b0dba0..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaFileSizeIsZeroException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -class MediaFileSizeIsZeroException : MediaFileSizeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -2398394583775317875L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt deleted file mode 100644 index fc4bff6c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaProcessException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -class MediaProcessException : MediaException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -5195233013542703735L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaSaveException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaSaveException.kt deleted file mode 100644 index d4fddbf6..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/MediaSaveException.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -open class MediaSaveException : MediaException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/RemoteMediaFileSizeException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/RemoteMediaFileSizeException.kt deleted file mode 100644 index ed52372b..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/RemoteMediaFileSizeException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -class RemoteMediaFileSizeException : MediaFileSizeException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 9188247721397839435L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt deleted file mode 100644 index 1a97958f..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/media/UnsupportedMediaException.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.media - -import java.io.Serial - -class UnsupportedMediaException : MediaException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -116741513216017134L - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt deleted file mode 100644 index b723fa1a..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/PostNotFoundException.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.resource - -import java.io.Serial - -class PostNotFoundException : NotFoundException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 1315818410686905717L - - fun withApId(apId: String): PostNotFoundException = PostNotFoundException("apId: $apId was not found.") - - fun withId(id: Long): PostNotFoundException = PostNotFoundException("id: $id was not found.") - - fun withUrl(url: String): PostNotFoundException = PostNotFoundException("url: $url was not found.") - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt deleted file mode 100644 index 223e7820..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/UserNotFoundException.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.resource - -import java.io.Serial - -open class UserNotFoundException : NotFoundException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = 3219433672235626200L - - fun withName(string: String, throwable: Throwable? = null): UserNotFoundException = - UserNotFoundException("name: $string was not found.", throwable) - - fun withId(id: Long, throwable: Throwable? = null): UserNotFoundException = - UserNotFoundException("id: $id was not found.", throwable) - - fun withUrl(url: String, throwable: Throwable? = null): UserNotFoundException = - UserNotFoundException("url: $url was not found.", throwable) - - fun withNameAndDomain(name: String, domain: String, throwable: Throwable? = null): UserNotFoundException = - UserNotFoundException("name: $name domain: $domain (@$name@$domain) was not found.", throwable) - - fun withKeyId(keyId: String, throwable: Throwable? = null) = - UserNotFoundException("keyId: $keyId was not found.", throwable) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt deleted file mode 100644 index 939b711c..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/exception/resource/local/LocalUserNotFoundException.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.domain.exception.resource.local - -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException -import java.io.Serial - -class LocalUserNotFoundException : UserNotFoundException { - constructor() : super() - constructor(message: String?) : super(message) - constructor(message: String?, cause: Throwable?) : super(message, cause) - constructor(cause: Throwable?) : super(cause) - constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( - message, - cause, - enableSuppression, - writableStackTrace - ) - - companion object { - @Serial - private const val serialVersionUID: Long = -4742548128672528145L - - fun withName(string: String, throwable: Throwable? = null): LocalUserNotFoundException = - LocalUserNotFoundException("name: $string was not found.", throwable) - - fun withId(id: Long, throwable: Throwable? = null): LocalUserNotFoundException = - LocalUserNotFoundException("id: $id was not found.", throwable) - - fun withUrl(url: String, throwable: Throwable? = null): LocalUserNotFoundException = - LocalUserNotFoundException("url: $url was not found.", throwable) - } -} diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt index 9a73010f..72916733 100644 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt +++ b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt @@ -18,7 +18,7 @@ package dev.usbharu.hideout.worker import dev.usbharu.hideout.activitypub.service.objects.user.APUserService import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.external.job.ReceiveFollowTask import dev.usbharu.hideout.core.external.job.ReceiveFollowTaskDef import dev.usbharu.owl.consumer.AbstractTaskRunner From 4d68b150ddde860fe2d158243f950137d575acab Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:15:47 +0900 Subject: [PATCH 30/54] =?UTF-8?q?feat:=20=E3=83=AD=E3=82=B0=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/application/actor/GetUserDetail.kt | 19 +++++ .../actor/GetUserDetailApplicationService.kt | 49 +++++++++++++ .../core/application/actor/UserDetail.kt | 70 +++++++++++++++++++ .../model/emoji/CustomEmojiRepository.kt | 1 + .../model/userdetails/UserDetailRepository.kt | 1 + .../CustomEmojiRepositoryImpl.kt | 13 +++- .../UserDetailRepositoryImpl.kt | 15 ++++ .../oauth2/Oauth2CommandExecutor.kt | 21 ++++++ .../oauth2/Oauth2CommandExecutorFactory.kt | 34 +++++++++ .../JsonOrFormModelMethodProcessor.kt | 2 + .../interfaces/api/SpringAccountApi.kt | 59 +++++++++++++++- .../src/main/resources/openapi/mastodon.yaml | 5 +- hideout-mastodon/templates/dataClass.mustache | 3 +- .../templates/dataClassOptVar.mustache | 2 +- hideout-mastodon/templates/model.mustache | 2 +- 15 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetail.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetail.kt new file mode 100644 index 00000000..16ece3c9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetail.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.actor + +data class GetUserDetail(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt new file mode 100644 index 00000000..8e7f8eb9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.actor + +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.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class GetUserDetailApplicationService( + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, + private val customEmojiRepository: CustomEmojiRepository, + transaction: Transaction, +) : + AbstractApplicationService(transaction, Companion.logger) { + companion object { + val logger = LoggerFactory.getLogger(GetUserDetailApplicationService::class.java) + } + + override suspend fun internalExecute(command: GetUserDetail, executor: CommandExecutor): UserDetail { + val userDetail = userDetailRepository.findById(command.id) + ?: throw IllegalArgumentException("actor does not exist") + val actor = actorRepository.findById(userDetail.actorId)!! + + val emojis = customEmojiRepository.findByIds(actor.emojis.map { it.emojiId }) + + return UserDetail.of(actor, userDetail, emojis) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt new file mode 100644 index 00000000..6e066e3f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import java.time.Instant + +data class UserDetail( + val id: Long, + val userDetailId: Long, + val name: String, + val domain: String, + val screenName: String, + val url: String, + val iconUrl: String, + val description: String, + val locked: Boolean, + val emojis: List, + val createdAt: Instant, + val lastPostAt: Instant?, + val postsCount: Int, + val followingCount: Int?, + val followersCount: Int?, + val moveTo: Long?, + val suspend: Boolean, +) { + companion object { + fun of( + actor: Actor, + userDetail: UserDetail, + customEmojis: List, + ): dev.usbharu.hideout.core.application.actor.UserDetail { + return UserDetail( + actor.id.id, + userDetail.id.id, + actor.name.name, + actor.domain.domain, + actor.screenName.screenName, + actor.url.toString(), + actor.url.toString(), + actor.description.description, + actor.locked, + customEmojis, + actor.createdAt, + actor.lastPostAt, + actor.postsCount.postsCount, + actor.followingCount?.relationshipCount, + actor.followersCount?.relationshipCount, + actor.moveTo?.id, + actor.suspend + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt index dcda92d0..1eb20f2c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmojiRepository.kt @@ -21,4 +21,5 @@ interface CustomEmojiRepository { suspend fun findById(id: Long): CustomEmoji? suspend fun delete(customEmoji: CustomEmoji) suspend fun findByNamesAndDomain(names: List, domain: String): List + suspend fun findByIds(ids: List): List } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt index 54f2e84c..08e562bd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetailRepository.kt @@ -20,4 +20,5 @@ interface UserDetailRepository { suspend fun save(userDetail: UserDetail): UserDetail suspend fun delete(userDetail: UserDetail) suspend fun findByActorId(actorId: Long): UserDetail? + suspend fun findById(id: Long): UserDetail? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt index cd50d5c4..9af0e090 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/CustomEmojiRepositoryImpl.kt @@ -70,8 +70,8 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository, CustomEmojis.deleteWhere { id eq customEmoji.id.emojiId } } - override suspend fun findByNamesAndDomain(names: List, domain: String): List { - return CustomEmojis + override suspend fun findByNamesAndDomain(names: List, domain: String): List = query { + return@query CustomEmojis .selectAll() .where { CustomEmojis.name inList names and (CustomEmojis.domain eq domain) @@ -79,6 +79,15 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository, .map { it.toCustomEmoji() } } + override suspend fun findByIds(ids: List): List = query { + return@query CustomEmojis + .selectAll() + .where { + CustomEmojis.id inList ids + } + .map { it.toCustomEmoji() } + } + companion object { private val logger = LoggerFactory.getLogger(CustomEmojiRepositoryImpl::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt index a10357a9..03ff1695 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserDetailRepositoryImpl.kt @@ -74,6 +74,21 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() { } } + override suspend fun findById(id: Long): UserDetail? = query { + UserDetails + .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] + ) + } + } + companion object { private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt new file mode 100644 index 00000000..23f8a73e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import dev.usbharu.hideout.core.application.shared.CommandExecutor + +class Oauth2CommandExecutor(override val executor: String, val userDetailId: Long) : CommandExecutor \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt new file mode 100644 index 00000000..6dee87f0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.oauth2.jwt.Jwt +import org.springframework.stereotype.Component + +@Component +class Oauth2CommandExecutorFactory { + fun getCommandExecutor(): Oauth2CommandExecutor { + val principal = SecurityContextHolder.getContext().authentication.principal as Jwt + + return Oauth2CommandExecutor( + principal.subject, + principal.getClaim("uid").toLong() + ) + + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt index d7a97736..54c9b107 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt @@ -43,6 +43,8 @@ class JsonOrFormModelMethodProcessor( webRequest: NativeWebRequest, binderFactory: WebDataBinderFactory?, ): Any? { + + val contentType = webRequest.getHeader("Content-Type").orEmpty() logger.trace("ContentType is {}", contentType) if (contentType.contains(isJsonRegex)) { diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt index 43ec4488..351b4d56 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt @@ -16,14 +16,19 @@ package dev.usbharu.hideout.mastodon.interfaces.api +import dev.usbharu.hideout.core.application.actor.GetUserDetail +import dev.usbharu.hideout.core.application.actor.GetUserDetailApplicationService +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* -import kotlinx.coroutines.flow.Flow import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @Controller -class SpringAccountApi : AccountApi { +class SpringAccountApi( + private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, + private val getUserDetailApplicationService: GetUserDetailApplicationService, +) : AccountApi { override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { return super.apiV1AccountsIdBlockPost(id) } @@ -68,7 +73,55 @@ class SpringAccountApi : AccountApi { } override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity { - return super.apiV1AccountsVerifyCredentialsGet() + val commandExecutor = oauth2CommandExecutorFactory.getCommandExecutor() + val localActor = + getUserDetailApplicationService.execute(GetUserDetail(commandExecutor.userDetailId), commandExecutor) + + return ResponseEntity.ok( + CredentialAccount( + id = localActor.id.toString(), + username = localActor.name, + acct = localActor.name + "@" + localActor.domain, + url = localActor.url, + displayName = localActor.screenName, + note = localActor.description, + avatar = localActor.iconUrl, + avatarStatic = localActor.iconUrl, + header = localActor.iconUrl, + headerStatic = localActor.iconUrl, + locked = localActor.locked, + fields = emptyList(), + emojis = localActor.emojis.map { + CustomEmoji( + shortcode = it.name, + url = it.url.toString(), + staticUrl = it.url.toString(), + true, + category = it.category.orEmpty() + ) + }, + bot = false, + group = false, + discoverable = true, + createdAt = localActor.createdAt.toString(), + lastStatusAt = localActor.lastPostAt?.toString(), + statusesCount = localActor.postsCount, + followersCount = localActor.followersCount, + followingCount = localActor.followingCount, + moved = localActor.moveTo != null, + noindex = true, + suspendex = localActor.suspend, + limited = false, + role = null, + source = AccountSource( + localActor.description, + emptyList(), + AccountSource.Privacy.PUBLIC, + false, + 0 + ) + ) + ) } override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { diff --git a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml index e104eff7..3d78d56c 100644 --- a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml +++ b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml @@ -242,6 +242,9 @@ paths: application/json: schema: $ref: "#/components/schemas/AppsRequest" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/AppsRequest" responses: 200: @@ -1660,8 +1663,6 @@ components: - created_at - last_status_at - statuses_count - - followers_count - - followers_count - source AccountSource: diff --git a/hideout-mastodon/templates/dataClass.mustache b/hideout-mastodon/templates/dataClass.mustache index 0fb78397..f8a8e771 100644 --- a/hideout-mastodon/templates/dataClass.mustache +++ b/hideout-mastodon/templates/dataClass.mustache @@ -5,7 +5,8 @@ {{/vars}} */{{#discriminator}} {{>typeInfoAnnotation}}{{/discriminator}} -{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class {{classname}}( + +{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class {{classname}} @ConstructorProperties( {{#vars}}"{{baseName}}",{{/vars}} ) constructor( {{#requiredVars}} {{>dataClassReqVar}}{{^-last}}, {{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}}, diff --git a/hideout-mastodon/templates/dataClassOptVar.mustache b/hideout-mastodon/templates/dataClassOptVar.mustache index 3ba523bb..5b7e1df7 100644 --- a/hideout-mastodon/templates/dataClassOptVar.mustache +++ b/hideout-mastodon/templates/dataClassOptVar.mustache @@ -2,4 +2,4 @@ @Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} @ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}{{#deprecated}} @Deprecated(message = ""){{/deprecated}} -@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}} +@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{#lambda.camelcase}} {{{name}}} {{/lambda.camelcase}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}} diff --git a/hideout-mastodon/templates/model.mustache b/hideout-mastodon/templates/model.mustache index 48ca8ae6..d592ec82 100644 --- a/hideout-mastodon/templates/model.mustache +++ b/hideout-mastodon/templates/model.mustache @@ -20,7 +20,7 @@ import java.util.Objects {{#swagger1AnnotationLibrary}} import io.swagger.annotations.ApiModelProperty {{/swagger1AnnotationLibrary}} - +import java.beans.ConstructorProperties {{#models}} {{#model}} {{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}}{{>dataClass}}{{/isEnum}} From 8b3a6fc15afa151b5d77133aa44c9fc939d8ee28 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:13:24 +0900 Subject: [PATCH 31/54] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BFAPI=E3=82=92?= =?UTF-8?q?=E5=8F=A9=E3=81=91=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/mastodon/interfaces/api/SpringAccountApi.kt | 2 +- hideout-mastodon/templates/dataClass.mustache | 6 +++--- hideout-mastodon/templates/dataClassOptVar.mustache | 2 +- hideout-mastodon/templates/dataClassReqVar.mustache | 2 +- libs.versions.toml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt index 351b4d56..7483eda8 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt @@ -116,7 +116,7 @@ class SpringAccountApi( source = AccountSource( localActor.description, emptyList(), - AccountSource.Privacy.PUBLIC, + AccountSource.Privacy.`public`, false, 0 ) diff --git a/hideout-mastodon/templates/dataClass.mustache b/hideout-mastodon/templates/dataClass.mustache index f8a8e771..4695e2a9 100644 --- a/hideout-mastodon/templates/dataClass.mustache +++ b/hideout-mastodon/templates/dataClass.mustache @@ -26,9 +26,9 @@ * {{{description}}} * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} */ - enum class {{{nameInCamelCase}}}(val value: {{#isContainer}}{{#items}}{{{dataType}}}{{/items}}{{/isContainer}}{{^isContainer}}{{{dataType}}}{{/isContainer}}) { - {{#allowableValues}}{{#enumVars}} - @JsonProperty({{{value}}}) {{{name}}}({{{value}}}){{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} + enum class {{{nameInPascalCase}}}(val value: {{#isContainer}}{{#items}}{{{dataType}}}{{/items}}{{/isContainer}}{{^isContainer}}{{{dataType}}}{{/isContainer}}) { + {{#allowableValues}}{{#values}} + @JsonProperty("{{.}}") `{{.}}`("{{.}}"){{^-last}},{{/-last}}{{/values}}{{/allowableValues}} } {{/isEnum}}{{/vars}}{{/hasEnums}} } diff --git a/hideout-mastodon/templates/dataClassOptVar.mustache b/hideout-mastodon/templates/dataClassOptVar.mustache index 5b7e1df7..e8a4e681 100644 --- a/hideout-mastodon/templates/dataClassOptVar.mustache +++ b/hideout-mastodon/templates/dataClassOptVar.mustache @@ -2,4 +2,4 @@ @Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} @ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}{{#deprecated}} @Deprecated(message = ""){{/deprecated}} -@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{#lambda.camelcase}} {{{name}}} {{/lambda.camelcase}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}} +@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{#lambda.camelcase}} {{{name}}} {{/lambda.camelcase}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInPascalCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}} diff --git a/hideout-mastodon/templates/dataClassReqVar.mustache b/hideout-mastodon/templates/dataClassReqVar.mustache index 9ae7d3a9..15d2118a 100644 --- a/hideout-mastodon/templates/dataClassReqVar.mustache +++ b/hideout-mastodon/templates/dataClassReqVar.mustache @@ -1,4 +1,4 @@ {{#useBeanValidation}}{{>beanValidation}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}} @Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}} @ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}{{^isNullable}}@get:NotNull{{/isNullable}} -@get:JsonProperty("{{{baseName}}}", required = true){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}} +@get:JsonProperty("{{{baseName}}}", required = true){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInPascalCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}} diff --git a/libs.versions.toml b/libs.versions.toml index 68f1a07d..fd836260 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -110,5 +110,5 @@ spring-boot = { id = "org.springframework.boot", version = "3.3.0" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlin-spring = { id = "org.jetbrains.kotlin.plugin.spring", version.ref = "kotlin" } kover = { id = "org.jetbrains.kotlinx.kover", version = "0.8.0" } -openapi-generator = { id = "org.openapi.generator", version = "7.4.0" } +openapi-generator = { id = "org.openapi.generator", version = "7.6.0" } license-report = { id = "com.github.jk1.dependency-license-report", version = "2.8" } \ No newline at end of file From 98a795c374417dce6ffd9d3733b04892cd29462a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:24:25 +0900 Subject: [PATCH 32/54] sql --- .../application/post/RegisterLocalPost.kt | 4 +-- .../RegisterLocalPostApplicationService.kt | 8 +++-- .../infrastructure/factory/PostFactoryImpl.kt | 2 +- .../resources/db/migration/V1__Init_DB.sql | 20 ++++++------ .../interfaces/api/SpringStatusApi.kt | 32 +++++++++++++++++-- 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt index b5a6740b..16f1092e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt @@ -19,9 +19,9 @@ package dev.usbharu.hideout.core.application.post import dev.usbharu.hideout.core.domain.model.post.Visibility data class RegisterLocalPost( - val actorId: Long, + val userDetailId: Long, val content: String, - val overview: String, + val overview: String?, val visibility: Visibility, val repostId: Long?, val replyId: Long?, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index 3b6dcd8c..8b81c8de 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -25,6 +25,7 @@ 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.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -35,6 +36,7 @@ class RegisterLocalPostApplicationService( private val postFactory: PostFactoryImpl, private val actorRepository: ActorRepository, private val postRepository: PostRepository, + private val userDetailRepository: UserDetailRepository, transaction: Transaction, ) : AbstractApplicationService(transaction, Companion.logger) { @@ -43,10 +45,12 @@ class RegisterLocalPostApplicationService( } override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor) { - val actorId = ActorId(command.actorId) + val actorId = (userDetailRepository.findById(command.userDetailId) + ?: throw IllegalStateException("actor not found")).actorId + val post = postFactory.createLocal(actorId, actorRepository.findById(actorId)!!.name, - PostOverview(command.overview), + command.overview?.let { PostOverview(it) }, command.content, command.visibility, command.repostId?.let { PostId(it) }, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index ab31daf1..1c4acd6c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -38,7 +38,7 @@ class PostFactoryImpl( suspend fun createLocal( actorId: ActorId, actorName: ActorName, - overview: PostOverview, + overview: PostOverview?, content: String, visibility: Visibility, repostId: PostId?, diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 3f76c59b..b85bd31d 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -41,21 +41,21 @@ create table if not exists actors url varchar(1000) not null unique, public_key varchar(10000) not null, private_key varchar(10000) null, - created_at timestamp not null, + created_at timestamp not null, key_id varchar(1000) not null, "following" varchar(1000) null, followers varchar(1000) null, - "instance" bigint not null, + "instance" bigint not null, locked boolean not null, - following_count int null, - followers_count int null, + following_count int null, + followers_count int null, posts_count int not null, last_post_at timestamp null default null, - last_update_at timestamp not null, - suspend boolean not null, - move_to bigint null default null, - emojis varchar(3000) not null default '', - deleted boolean not null default false, + last_update_at timestamp not null, + suspend boolean not null, + move_to bigint null default null, + emojis varchar(3000) not null default '', + deleted boolean not null default false, 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 @@ -75,7 +75,7 @@ create table if not exists user_details actor_id bigint not null unique, password varchar(255) not null, auto_accept_followee_follow_request boolean not null, - last_migration timestamp null default null, + last_migration timestamp null default null, constraint fk_user_details_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict ); diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt index 3783b068..e1885640 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt @@ -16,14 +16,22 @@ package dev.usbharu.hideout.mastodon.interfaces.api +import dev.usbharu.hideout.core.application.post.RegisterLocalPost +import dev.usbharu.hideout.core.application.post.RegisterLocalPostApplicationService +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequest +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequest.Visibility.* import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @Controller -class SpringStatusApi : StatusApi { +class SpringStatusApi( + private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, + private val registerLocalPostApplicationService: RegisterLocalPostApplicationService, +) : StatusApi { override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity { return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji) } @@ -37,6 +45,26 @@ class SpringStatusApi : StatusApi { } override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity { - return super.apiV1StatusesPost(statusesRequest) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + registerLocalPostApplicationService.execute( + RegisterLocalPost( + userDetailId = executor.userDetailId, + content = statusesRequest.status.orEmpty(), + overview = statusesRequest.spoilerText, + visibility = when (statusesRequest.visibility) { + public -> Visibility.PUBLIC + unlisted -> Visibility.UNLISTED + private -> Visibility.FOLLOWERS + direct -> Visibility.DIRECT + null -> Visibility.PUBLIC + }, + repostId = null, + replyId = statusesRequest.inReplyToId?.toLong(), + sensitive = statusesRequest.sensitive == true, + mediaIds = statusesRequest.mediaIds.orEmpty().map { it.toLong() } + ), + executor + ) + return ResponseEntity.ok().build() } } \ No newline at end of file From f79f90c8ef72f5036b464e0d71242c83129ece29 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:10:19 +0900 Subject: [PATCH 33/54] sql --- .../resources/db/migration/V1__Init_DB.sql | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index b85bd31d..4cefd831 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -98,14 +98,16 @@ create table if not exists posts overview varchar(100) null, content varchar(5000) not null, text varchar(3000) not null, - created_at bigint not null, - visibility int default 0 not null, + created_at timestamp not null, + visibility varchar(100) not null, url varchar(500) not null, repost_id bigint null, reply_id bigint null, "sensitive" boolean default false not null, ap_id varchar(100) not null unique, - deleted boolean default false not null + deleted boolean default false not null, + hide boolean default false not null, + move_to bigint default null null ); alter table posts add constraint fk_posts_actor_id__id foreign key (actor_id) references actors (id) on delete restrict on update restrict; @@ -113,6 +115,9 @@ alter table posts add constraint fk_posts_repostid__id foreign key (repost_id) references posts (id) on delete restrict on update restrict; alter table posts add constraint fk_posts_replyid__id foreign key (reply_id) references posts (id) on delete restrict on update restrict; +alter table posts + add constraint fk_posts_move_to__id foreign key (move_to) references posts (id) on delete CASCADE on update cascade; + create table if not exists posts_media ( post_id bigint, @@ -137,6 +142,19 @@ alter table posts_emojis add constraint fk_posts_emojis_emoji_id__id foreign key (emoji_id) references emojis (id) on delete cascade on update cascade; +create table if not exists posts_visible_actors +( + post_id bigint not null, + actor_id bigint not null, + constraint pk_postsvisibleactors primary key (post_id, actor_id) +); + +alter table posts_visible_actors + add constraint fk_posts_visible_actors_post_id__id foreign key (post_id) references posts (id) on delete cascade on update cascade; +alter table posts_visible_actors + add constraint fk_posts_visible_actors_actor_id__id foreign key (actor_id) references actors (id) on delete cascade on update cascade; + + create table if not exists relationships ( id bigserial primary key, From e0c0c8b22af5f67253bc4b64a43df2139f09a66b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:10:32 +0900 Subject: [PATCH 34/54] sql --- hideout-core/src/main/resources/db/migration/V1__Init_DB.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index 4cefd831..c8dd74bd 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -98,7 +98,7 @@ create table if not exists posts overview varchar(100) null, content varchar(5000) not null, text varchar(3000) not null, - created_at timestamp not null, + created_at timestamp not null, visibility varchar(100) not null, url varchar(500) not null, repost_id bigint null, From 91867d6b83a9a1e16658a559e412b3f15c53bf04 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:10:49 +0900 Subject: [PATCH 35/54] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/config/SecurityConfig.kt | 20 +++++++++++++++++++ .../ExposedPostRepository.kt | 1 + .../infrastructure/factory/PostFactoryImpl.kt | 2 +- .../dev/usbharu/hideout/util/RsaUtil.kt | 9 +++++++++ hideout-core/src/main/resources/log4j2.xml | 1 + 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt index cd4051d9..f4aed6c5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -16,7 +16,14 @@ package dev.usbharu.hideout.core.config +import com.nimbusds.jose.jwk.JWKSet +import com.nimbusds.jose.jwk.RSAKey +import com.nimbusds.jose.jwk.source.ImmutableJWKSet +import com.nimbusds.jose.jwk.source.JWKSource +import com.nimbusds.jose.proc.SecurityContext import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.HideoutUserDetails +import dev.usbharu.hideout.util.RsaUtil +import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.annotation.Order @@ -118,6 +125,19 @@ class SecurityConfig { } } + @Bean + fun loadJwkSource(jwkConfig: JwkConfig): JWKSource { + val rsaKey = RSAKey.Builder(RsaUtil.decodeRsaPublicKey(jwkConfig.publicKey)) + .privateKey(RsaUtil.decodeRsaPrivateKey(jwkConfig.privateKey)).keyID(jwkConfig.keyId).build() + return ImmutableJWKSet(JWKSet(rsaKey)) + } + + @ConfigurationProperties("hideout.security.jwt") + data class JwkConfig( + val keyId: String, + val publicKey: String, + val privateKey: String, + ) @Bean fun roleHierarchy(): RoleHierarchy { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index 574ea476..b02351d7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -199,6 +199,7 @@ object Posts : Table("posts") { val deleted = bool("deleted") val hide = bool("hide") val moveTo = long("move_to").references(id).nullable() + override val primaryKey: PrimaryKey = PrimaryKey(id) } object PostsMedia : Table("posts_media") { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index 1c4acd6c..7ee64d0a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -47,7 +47,7 @@ class PostFactoryImpl( mediaIds: List, ): Post { val id = idGenerateService.generateId() - val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName + "/posts/" + id) + val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName.name + "/posts/" + id) return Post.create( PostId(id), actorId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt index 3460a515..8efbd8b0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt @@ -17,7 +17,9 @@ package dev.usbharu.hideout.util import java.security.KeyFactory +import java.security.interfaces.RSAPrivateKey import java.security.interfaces.RSAPublicKey +import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec object RsaUtil { @@ -36,4 +38,11 @@ object RsaUtil { return decodeRsaPublicKey(replace) } + fun decodeRsaPrivateKey(byteArray: ByteArray): RSAPrivateKey { + val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(byteArray) + return KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey + } + + fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded)) + } diff --git a/hideout-core/src/main/resources/log4j2.xml b/hideout-core/src/main/resources/log4j2.xml index 834d3910..195006c3 100644 --- a/hideout-core/src/main/resources/log4j2.xml +++ b/hideout-core/src/main/resources/log4j2.xml @@ -11,5 +11,6 @@ + \ No newline at end of file From 86daf1041babf492133d7149bbb0a1fc4c3134ad Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 7 Jun 2024 20:44:12 +0900 Subject: [PATCH 36/54] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=82=92?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/application/post/GetPost.kt | 21 ++ .../post/GetPostApplicationService.kt | 40 +++ .../hideout/core/application/post/Post.kt | 58 ++++ .../RegisterLocalPostApplicationService.kt | 7 +- .../DelegateCommandExecutorFactory.kt | 36 +++ .../resources/db/migration/V1__Init_DB.sql | 2 +- hideout-mastodon/build.gradle.kts | 1 + .../mastodon/application/status/GetStatus.kt | 21 ++ .../status/GetStatusApplicationService.kt | 42 +++ .../ExposedAccountQueryService.kt | 73 +++++ .../exposedquery/ExposedStatusQueryService.kt | 291 ++++++++++++++++++ .../interfaces/api/SpringStatusApi.kt | 27 +- .../mastodon/query/AccountQueryService.kt | 24 ++ .../mastodon/query/StatusQueryService.kt | 45 +++ .../src/main/resources/openapi/mastodon.yaml | 2 - 15 files changed, 678 insertions(+), 12 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPost.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatus.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatusApplicationService.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryService.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPost.kt new file mode 100644 index 00000000..90ef8560 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPost.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.post + +data class GetPost( + val postId: Long, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt new file mode 100644 index 00000000..6c8bf98b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.post + +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.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class GetPostApplicationService(private val postRepository: PostRepository, transaction: Transaction) : + AbstractApplicationService(transaction, logger) { + + override suspend fun internalExecute(command: GetPost, executor: CommandExecutor): Post { + val post = postRepository.findById(PostId(command.postId)) ?: throw Exception("Post not found") + + return Post.of(post) + } + + companion object { + private val logger = LoggerFactory.getLogger(GetPostApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt new file mode 100644 index 00000000..538b8911 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.post + +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.post.Visibility +import java.net.URI +import java.time.Instant + +data class Post( + val id: Long, + val actorId: Long, + val overview: String?, + val text: String, + val content: String, + val createdAt: Instant, + val visibility: Visibility, + val url: URI, + val repostId: Long?, + val replyId: Long?, + val sensitive: Boolean, + val mediaIds: List, + val moveTo: Long?, +) { + companion object { + fun of(post: Post): dev.usbharu.hideout.core.application.post.Post { + return Post( + post.id.id, + post.actorId.id, + post.overview?.overview, + post.text, + post.content.content, + post.createdAt, + post.visibility, + post.url, + post.repostId?.id, + post.replyId?.id, + post.sensitive, + post.mediaIds.map { it.id }, + post.moveTo?.id + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index 8b81c8de..bb67bad9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.application.post 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.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.PostId @@ -38,13 +37,13 @@ class RegisterLocalPostApplicationService( private val postRepository: PostRepository, private val userDetailRepository: UserDetailRepository, transaction: Transaction, -) : AbstractApplicationService(transaction, Companion.logger) { +) : AbstractApplicationService(transaction, Companion.logger) { companion object { val logger: Logger = LoggerFactory.getLogger(RegisterLocalPostApplicationService::class.java) } - override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor) { + override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor): Long { val actorId = (userDetailRepository.findById(command.userDetailId) ?: throw IllegalStateException("actor not found")).actorId @@ -59,5 +58,7 @@ class RegisterLocalPostApplicationService( command.mediaIds.map { MediaId(it) }) postRepository.save(post) + + return post.id.id } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt new file mode 100644 index 00000000..6312b05b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.springframework + +import dev.usbharu.hideout.core.application.shared.CommandExecutor +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.oauth2.jwt.Jwt +import org.springframework.stereotype.Component + +@Component +class DelegateCommandExecutorFactory( + private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, + private val mvcCommandExecutorFactory: SpringMvcCommandExecutorFactory, +) { + fun getCommandExecutor(): CommandExecutor { + if (SecurityContextHolder.getContext().authentication.principal is Jwt) { + return oauth2CommandExecutorFactory.getCommandExecutor() + } + return mvcCommandExecutorFactory.getCommandExecutor() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index c8dd74bd..dc757618 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -86,7 +86,7 @@ create table if not exists media url varchar(255) not null unique, remote_url varchar(255) null unique, thumbnail_url varchar(255) null unique, - "type" int not null, + "type" varchar(100) not null, blurhash varchar(255) null, mime_type varchar(255) not null, description varchar(4000) null diff --git a/hideout-mastodon/build.gradle.kts b/hideout-mastodon/build.gradle.kts index 2907614d..54fea879 100644 --- a/hideout-mastodon/build.gradle.kts +++ b/hideout-mastodon/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(libs.jakarta.annotation) implementation(libs.jakarta.validation) + implementation(libs.bundles.exposed) implementation(libs.bundles.openapi) implementation(libs.bundles.coroutines) } diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatus.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatus.kt new file mode 100644 index 00000000..37b6882e --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatus.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.status + +data class GetStatus( + val id: String, +) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatusApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatusApplicationService.kt new file mode 100644 index 00000000..545bca34 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/status/GetStatusApplicationService.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.status + +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.mastodon.interfaces.api.generated.model.Status +import dev.usbharu.hideout.mastodon.query.StatusQueryService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class GetStatusApplicationService( + private val statusQueryService: StatusQueryService, + transaction: Transaction, +) : AbstractApplicationService( + transaction, + logger +) { + companion object { + val logger = LoggerFactory.getLogger(GetStatusApplicationService::class.java)!! + } + + override suspend fun internalExecute(command: GetStatus, executor: CommandExecutor): Status { + return statusQueryService.findByPostId(command.id.toLong()) ?: throw Exception("Not fount") + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryService.kt new file mode 100644 index 00000000..41ca4ab2 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryService.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.infrastructure.exposedquery + +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Account +import dev.usbharu.hideout.mastodon.query.AccountQueryService +import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.selectAll +import org.springframework.stereotype.Repository + +@Repository +class AccountQueryServiceImpl(private val applicationConfig: ApplicationConfig) : AccountQueryService { + override suspend fun findById(accountId: Long): Account? { + val query = Actors.selectAll().where { Actors.id eq accountId } + + return query + .singleOrNull() + ?.let { toAccount(it) } + } + + override suspend fun findByIds(accountIds: List): List { + val query = Actors.selectAll().where { Actors.id inList accountIds } + + return query + .map { toAccount(it) } + } + + private fun toAccount( + resultRow: ResultRow, + ): Account { + val userUrl = "${applicationConfig.url}/users/${resultRow[Actors.id]}" + + return Account( + id = resultRow[Actors.id].toString(), + username = resultRow[Actors.name], + acct = "${resultRow[Actors.name]}@${resultRow[Actors.domain]}", + url = resultRow[Actors.url], + displayName = resultRow[Actors.screenName], + note = resultRow[Actors.description], + avatar = userUrl + "/icon.jpg", + avatarStatic = userUrl + "/icon.jpg", + header = userUrl + "/header.jpg", + headerStatic = userUrl + "/header.jpg", + locked = resultRow[Actors.locked], + fields = emptyList(), + emojis = emptyList(), + bot = false, + group = false, + discoverable = true, + createdAt = resultRow[Actors.createdAt].toString(), + lastStatusAt = resultRow[Actors.lastPostAt]?.toString(), + statusesCount = resultRow[Actors.postsCount], + followersCount = resultRow[Actors.followersCount], + followingCount = resultRow[Actors.followingCount], + ) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt new file mode 100644 index 00000000..bfeca74c --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.infrastructure.exposedquery + +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji +import dev.usbharu.hideout.core.domain.model.media.* +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.infrastructure.exposedrepository.* +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Account +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status.Visibility.* +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.selectAll +import org.springframework.stereotype.Repository +import java.net.URI +import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.CustomEmoji as MastodonEmoji + +@Suppress("IncompleteDestructuring") +@Repository +class StatusQueryServiceImpl : StatusQueryService { + override suspend fun findByPostIds(ids: List): List = findByPostIdsWithMedia(ids) + + override suspend fun findByPostIdsWithMediaIds(statusQueries: List): List { + val postIdSet = mutableSetOf() + postIdSet.addAll(statusQueries.flatMap { listOfNotNull(it.postId, it.replyId, it.repostId) }) + val mediaIdSet = mutableSetOf() + mediaIdSet.addAll(statusQueries.flatMap { it.mediaIds }) + + val emojiIdSet = mutableSetOf() + emojiIdSet.addAll(statusQueries.flatMap { it.emojiIds }) + + val postMap = Posts + .leftJoin(Actors) + .selectAll().where { Posts.id inList postIdSet } + .associate { it[Posts.id] to toStatus(it) } + val mediaMap = Media.selectAll().where { Media.id inList mediaIdSet } + .associate { + it[Media.id] to it.toMedia().toMediaAttachments() + } + + val emojiMap = CustomEmojis.selectAll().where { CustomEmojis.id inList emojiIdSet }.associate { + it[CustomEmojis.id] to it.toCustomEmoji().toMastodonEmoji() + } + return statusQueries.mapNotNull { statusQuery -> + postMap[statusQuery.postId]?.copy( + inReplyToId = statusQuery.replyId?.toString(), + inReplyToAccountId = postMap[statusQuery.replyId]?.account?.id, + reblog = postMap[statusQuery.repostId], + mediaAttachments = statusQuery.mediaIds.mapNotNull { mediaMap[it] }, + emojis = statusQuery.emojiIds.mapNotNull { emojiMap[it] } + ) + } + } + + override suspend fun accountsStatus( + accountId: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + includeFollowers: Boolean, + ): List { + val query = Posts + .leftJoin(PostsMedia) + .leftJoin(Actors) + .leftJoin(Media) + .selectAll().where { Posts.actorId eq accountId } + + if (onlyMedia) { + query.andWhere { PostsMedia.mediaId.isNotNull() } + } + if (excludeReplies) { + query.andWhere { Posts.replyId.isNotNull() } + } + if (excludeReblogs) { + query.andWhere { Posts.repostId.isNotNull() } + } + if (includeFollowers) { + query.andWhere { Posts.visibility inList listOf(public.name, unlisted.name, private.name) } + } else { + query.andWhere { Posts.visibility inList listOf(public.name, unlisted.name) } + } + + val pairs = query + .groupBy { it[Posts.id] } + .map { it.value } + .map { + toStatus(it.first()).copy( + mediaAttachments = it.mapNotNull { resultRow -> + resultRow.toMediaOrNull()?.toMediaAttachments() + } + ) to it.first()[Posts.repostId] + } + + val statuses = resolveReplyAndRepost(pairs) + return statuses + } + + override suspend fun findByPostId(id: Long): Status? { + val map = Posts + .leftJoin(PostsMedia) + .leftJoin(Actors) + .leftJoin(Media) + .selectAll() + .where { Posts.id eq id } + .groupBy { it[Posts.id] } + .map { it.value } + .map { + toStatus(it.first()).copy( + mediaAttachments = it.mapNotNull { resultRow -> + resultRow.toMediaOrNull()?.toMediaAttachments() + }, + emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() } + ) to it.first()[Posts.repostId] + } + return resolveReplyAndRepost(map).singleOrNull() + } + + private fun resolveReplyAndRepost(pairs: List>): List { + val statuses = pairs.map { it.first } + return pairs + .map { + if (it.second != null) { + it.first.copy(reblog = statuses.find { (id) -> id == it.second.toString() }) + } else { + it.first + } + } + .map { + if (it.inReplyToId != null) { + println("statuses trace: $statuses") + println("inReplyToId trace: ${it.inReplyToId}") + it.copy(inReplyToAccountId = statuses.find { (id) -> id == it.inReplyToId }?.account?.id) + } else { + it + } + } + } + + private suspend fun findByPostIdsWithMedia(ids: List): List { + val pairs = Posts + .leftJoin(PostsMedia) + .leftJoin(PostsEmojis) + .leftJoin(CustomEmojis) + .leftJoin(Actors) + .leftJoin(Media) + .selectAll().where { Posts.id inList ids } + .groupBy { it[Posts.id] } + .map { it.value } + .map { + toStatus(it.first()).copy( + mediaAttachments = it.mapNotNull { resultRow -> + resultRow.toMediaOrNull()?.toMediaAttachments() + }, + emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() } + ) to it.first()[Posts.repostId] + } + return resolveReplyAndRepost(pairs) + } +} + +private fun CustomEmoji.toMastodonEmoji(): MastodonEmoji = MastodonEmoji( + shortcode = this.name, + url = this.url.toString(), + staticUrl = this.url.toString(), + visibleInPicker = true, + category = this.category.orEmpty() +) + +private fun toStatus(it: ResultRow) = Status( + id = it[Posts.id].toString(), + uri = it[Posts.apId], + createdAt = it[Posts.createdAt].toString(), + account = Account( + id = it[Actors.id].toString(), + username = it[Actors.name], + acct = "${it[Actors.name]}@${it[Actors.domain]}", + url = it[Actors.url], + displayName = it[Actors.screenName], + note = it[Actors.description], + avatar = it[Actors.url] + "/icon.jpg", + avatarStatic = it[Actors.url] + "/icon.jpg", + header = it[Actors.url] + "/header.jpg", + headerStatic = it[Actors.url] + "/header.jpg", + locked = it[Actors.locked], + fields = emptyList(), + emojis = emptyList(), + bot = false, + group = false, + discoverable = true, + createdAt = it[Actors.createdAt].toString(), + lastStatusAt = it[Actors.lastPostAt]?.toString(), + statusesCount = it[Actors.postsCount], + followersCount = it[Actors.followersCount], + followingCount = it[Actors.followingCount], + noindex = false, + moved = false, + suspendex = false, + limited = false + ), + content = it[Posts.text], + visibility = when (Visibility.valueOf(it[Posts.visibility])) { + Visibility.PUBLIC -> public + Visibility.UNLISTED -> unlisted + Visibility.FOLLOWERS -> private + Visibility.DIRECT -> direct + }, + sensitive = it[Posts.sensitive], + spoilerText = it[Posts.overview].orEmpty(), + mediaAttachments = emptyList(), + mentions = emptyList(), + tags = emptyList(), + emojis = emptyList(), + reblogsCount = 0, + favouritesCount = 0, + repliesCount = 0, + url = it[Posts.apId], + inReplyToId = it[Posts.replyId]?.toString(), + inReplyToAccountId = null, + language = null, + text = it[Posts.text], + editedAt = null +) + +fun ResultRow.toMedia(): EntityMedia { + val fileType = FileType.valueOf(this[Media.type]) + val mimeType = this[Media.mimeType] + return EntityMedia( + id = MediaId(this[Media.id]), + name = MediaName(this[Media.name]), + url = URI.create(this[Media.url]), + remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) }, + thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) }, + type = fileType, + blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) }, + mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType), + description = this[Media.description]?.let { MediaDescription(it) } + ) +} + +fun ResultRow.toMediaOrNull(): EntityMedia? { + val fileType = FileType.valueOf(this.getOrNull(Media.type) ?: return null) + val mimeType = this.getOrNull(Media.mimeType) ?: return null + return EntityMedia( + id = MediaId(this.getOrNull(Media.id) ?: return null), + name = MediaName(this.getOrNull(Media.name) ?: return null), + url = URI.create(this.getOrNull(Media.url) ?: return null), + remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) }, + thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) }, + type = FileType.valueOf(this[Media.type]), + blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) }, + mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType), + description = MediaDescription(this[Media.description] ?: return null) + ) +} + +fun EntityMedia.toMediaAttachments(): MediaAttachment = MediaAttachment( + id = id.toString(), + type = when (type) { + FileType.Image -> MediaAttachment.Type.image + FileType.Video -> MediaAttachment.Type.video + FileType.Audio -> MediaAttachment.Type.audio + FileType.Unknown -> MediaAttachment.Type.unknown + }, + url = url.toString(), + previewUrl = thumbnailUrl?.toString(), + remoteUrl = remoteUrl?.toString(), + description = description?.description, + blurhash = blurHash?.hash, + textUrl = url.toString() +) \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt index e1885640..322d3f68 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt @@ -19,7 +19,10 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.post.RegisterLocalPost import dev.usbharu.hideout.core.application.post.RegisterLocalPostApplicationService import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +import dev.usbharu.hideout.core.infrastructure.springframework.DelegateCommandExecutorFactory +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutor +import dev.usbharu.hideout.mastodon.application.status.GetStatus +import dev.usbharu.hideout.mastodon.application.status.GetStatusApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequest @@ -29,8 +32,9 @@ import org.springframework.stereotype.Controller @Controller class SpringStatusApi( - private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, + private val delegateCommandExecutorFactory: DelegateCommandExecutorFactory, private val registerLocalPostApplicationService: RegisterLocalPostApplicationService, + private val getStatusApplicationService: GetStatusApplicationService, ) : StatusApi { override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity { return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji) @@ -41,12 +45,18 @@ class SpringStatusApi( } override suspend fun apiV1StatusesIdGet(id: String): ResponseEntity { - return super.apiV1StatusesIdGet(id) + + return ResponseEntity.ok( + getStatusApplicationService.execute( + GetStatus(id), + delegateCommandExecutorFactory.getCommandExecutor() + ) + ) } override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() - registerLocalPostApplicationService.execute( + val executor = delegateCommandExecutorFactory.getCommandExecutor() as Oauth2CommandExecutor + val execute = registerLocalPostApplicationService.execute( RegisterLocalPost( userDetailId = executor.userDetailId, content = statusesRequest.status.orEmpty(), @@ -65,6 +75,11 @@ class SpringStatusApi( ), executor ) - return ResponseEntity.ok().build() + + + val status = getStatusApplicationService.execute(GetStatus(execute.toString()), executor) + return ResponseEntity.ok( + status + ) } } \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt new file mode 100644 index 00000000..61de616c --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/AccountQueryService.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.query + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Account + +interface AccountQueryService { + suspend fun findById(accountId: Long): Account? + suspend fun findByIds(accountIds: List): List +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt new file mode 100644 index 00000000..dc7cc88e --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.query + +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status + +interface StatusQueryService { + suspend fun findByPostIds(ids: List): List + suspend fun findByPostIdsWithMediaIds(statusQueries: List): List + + @Suppress("LongParameterList") + suspend fun accountsStatus( + accountId: Long, + onlyMedia: Boolean = false, + excludeReplies: Boolean = false, + excludeReblogs: Boolean = false, + pinned: Boolean = false, + tagged: String?, + includeFollowers: Boolean = false, + ): List + + suspend fun findByPostId(id: Long): Status? +} + +data class StatusQuery( + val postId: Long, + val replyId: Long?, + val repostId: Long?, + val mediaIds: List, + val emojiIds: List, +) \ No newline at end of file diff --git a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml index 3d78d56c..91c75422 100644 --- a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml +++ b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml @@ -1577,8 +1577,6 @@ components: - discoverable - created_at - statuses_count - - followers_count - - followers_count CredentialAccount: type: object From 54e3af2253869da49e77743453968ae18da9737c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 8 Jun 2024 16:37:57 +0900 Subject: [PATCH 37/54] =?UTF-8?q?feat:=20=E3=83=89=E3=83=A1=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E5=B1=A4=E3=81=AB=E8=AA=8D=E5=8F=AF=E7=9A=84=E3=81=AA?= =?UTF-8?q?=E3=82=82=E3=81=AE=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/DeleteLocalPostApplicationService.kt | 14 +- .../RegisterLocalPostApplicationService.kt | 22 ++- .../post/UpdateLocalNoteApplicationService.kt | 37 ++-- .../application/shared/CommandExecutor.kt | 4 + .../core/domain/event/post/PostEvent.kt | 7 +- .../hideout/core/domain/model/actor/Actor.kt | 10 ++ .../hideout/core/domain/model/actor/Role.kt | 21 +++ .../hideout/core/domain/model/post/Post.kt | 169 +++++++++++++----- .../shared/domainevent/DomainEventBody.kt | 4 +- .../exposed/ActorResultRowMapper.kt | 3 +- .../infrastructure/exposed/PostQueryMapper.kt | 29 ++- .../factory/ActorFactoryImpl.kt | 3 +- .../infrastructure/factory/PostFactoryImpl.kt | 31 ++-- .../domain/model/actor/TestActorFactory.kt | 3 +- .../core/domain/model/post/PostTest.kt | 98 +++++----- 15 files changed, 302 insertions(+), 153 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt index b470ce4d..971f0f7b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt @@ -16,15 +16,23 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository 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.userdetails.UserDetailRepository import org.springframework.stereotype.Service @Service -class DeleteLocalPostApplicationService(private val postRepository: PostRepository) { - suspend fun delete(postId: Long) { +class DeleteLocalPostApplicationService( + private val postRepository: PostRepository, + private val userDetailRepository: UserDetailRepository, + private val actorRepository: ActorRepository, +) { + suspend fun delete(postId: Long, userDetailId: Long) { val findById = postRepository.findById(PostId(postId))!! - findById.delete() + val user = userDetailRepository.findById(userDetailId)!! + val actor = actorRepository.findById(user.actorId)!! + findById.delete(actor) postRepository.save(findById) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index bb67bad9..88a6e869 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -47,15 +47,19 @@ class RegisterLocalPostApplicationService( val actorId = (userDetailRepository.findById(command.userDetailId) ?: throw IllegalStateException("actor not found")).actorId - val post = postFactory.createLocal(actorId, - actorRepository.findById(actorId)!!.name, - command.overview?.let { PostOverview(it) }, - command.content, - command.visibility, - command.repostId?.let { PostId(it) }, - command.replyId?.let { PostId(it) }, - command.sensitive, - command.mediaIds.map { MediaId(it) }) + val actor = actorRepository.findById(actorId)!! + + val post = postFactory.createLocal( + actor = actor, + actorName = actor.name, + overview = command.overview?.let { PostOverview(it) }, + content = command.content, + visibility = command.visibility, + repostId = command.repostId?.let { PostId(it) }, + replyId = command.replyId?.let { PostId(it) }, + sensitive = command.sensitive, + mediaIds = command.mediaIds.map { MediaId(it) }, + ) postRepository.save(post) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt index 0348c80f..7a885791 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt @@ -16,30 +16,45 @@ package dev.usbharu.hideout.core.application.post +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.application.shared.UserDetailGettableCommandExecutor +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository 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.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class UpdateLocalNoteApplicationService( - private val transaction: Transaction, + transaction: Transaction, private val postRepository: PostRepository, private val postContentFactoryImpl: PostContentFactoryImpl, -) { - suspend fun update(updateLocalNote: UpdateLocalNote) { - transaction.transaction { - val post = postRepository.findById(PostId(updateLocalNote.postId))!! + private val userDetailRepository: UserDetailRepository, + private val actorRepository: ActorRepository, +) : AbstractApplicationService(transaction, logger) { - post.content = postContentFactoryImpl.create(updateLocalNote.content) - post.overview = updateLocalNote.overview?.let { PostOverview(it) } - post.addMediaIds(updateLocalNote.mediaIds.map { MediaId(it) }) - post.sensitive = updateLocalNote.sensitive + override suspend fun internalExecute(command: UpdateLocalNote, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) - postRepository.save(post) - } + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + val post = postRepository.findById(PostId(command.postId))!! + + post.setContent(postContentFactoryImpl.create(command.content), actor) + post.setOverview(command.overview?.let { PostOverview(it) }, actor) + post.addMediaIds(command.mediaIds.map { MediaId(it) }, actor) + post.setSensitive(command.sensitive, actor) + + postRepository.save(post) + } + + companion object { + private val logger = LoggerFactory.getLogger(UpdateLocalNoteApplicationService::class.java) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt index d022254f..9d530133 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt @@ -18,4 +18,8 @@ package dev.usbharu.hideout.core.application.shared interface CommandExecutor { val executor: String +} + +interface UserDetailGettableCommandExecutor : CommandExecutor { + val userDetailId: Long } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt index 1ae9ddb9..ad0fd36f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt @@ -16,20 +16,21 @@ package dev.usbharu.hideout.core.domain.event.post +import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.post.Post import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody -class PostDomainEventFactory(private val post: Post) { +class PostDomainEventFactory(private val post: Post, private val actor: Actor? = null) { fun createEvent(postEvent: PostEvent): DomainEvent { return DomainEvent.create( postEvent.eventName, - PostEventBody(post) + PostEventBody(post, actor) ) } } -class PostEventBody(post: Post) : DomainEventBody(mapOf("post" to post)) +class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor)) enum class PostEvent(val eventName: String) { delete("PostDelete"), diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 8d13c8e6..cbbb29db 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -52,8 +52,18 @@ class Actor( moveTo: ActorId? = null, emojiIds: Set, deleted: Boolean, + roles: Set, ) : DomainEventStorable() { + var roles = roles + private set + + fun setRole(roles: Set, actor: Actor) { + require(actor.roles.contains(Role.ADMINISTRATOR).not()) + + this.roles = roles + } + var suspend = suspend set(value) { if (field != value && value) { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt new file mode 100644 index 00000000..ee12ee08 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.model.actor + +enum class Role { + LOCAL, MODERATOR, ADMINISTRATOR, REMOTE +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index 80099fc8..9f932dbf 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -18,9 +18,12 @@ package dev.usbharu.hideout.core.domain.model.post import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory import dev.usbharu.hideout.core.domain.event.post.PostEvent +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.media.MediaId +import dev.usbharu.hideout.core.domain.model.post.Post.Companion.Action.* import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant @@ -28,7 +31,7 @@ import java.time.Instant class Post( val id: PostId, actorId: ActorId, - overview: PostOverview? = null, + overview: PostOverview?, content: PostContent, val createdAt: Instant, visibility: Visibility, @@ -39,13 +42,12 @@ class Post( val apId: URI, deleted: Boolean, mediaIds: List, - visibleActors: Set = emptySet(), - hide: Boolean = false, - moveTo: PostId? = null, + visibleActors: Set, + hide: Boolean, + moveTo: PostId?, ) : DomainEventStorable() { - var actorId = actorId - private set + val actorId = actorId get() { if (deleted) { return ActorId.ghost @@ -54,27 +56,35 @@ class Post( } var visibility = visibility - set(value) { - require(visibility != Visibility.DIRECT) - require(value != Visibility.DIRECT) - require(field.ordinal >= value.ordinal) + private set - require(deleted.not()) + fun setVisibility(visibility: Visibility, actor: Actor) { - if (field != value) { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) - } - field = value + require(isAllow(actor, UPDATE, this)) + require(this.visibility != Visibility.DIRECT) + require(visibility != Visibility.DIRECT) + require(this.visibility.ordinal >= visibility.ordinal) + + require(deleted.not()) + + if (this.visibility != visibility) { + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) } + this.visibility = visibility + } var visibleActors = visibleActors - set(value) { - require(deleted.not()) - if (visibility == Visibility.DIRECT) { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) - field = field.plus(value) - } + private set + + fun setVisibleActors(visibleActors: Set, actor: Actor) { + + require(isAllow(actor, UPDATE, this)) + require(deleted.not()) + if (visibility == Visibility.DIRECT) { + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + this.visibleActors = this.visibleActors.plus(visibleActors) } + } var content = content get() { @@ -83,13 +93,16 @@ class Post( } return field } - set(value) { - require(deleted.not()) - if (field != value) { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) - } - field = value + private set + + fun setContent(content: PostContent, actor: Actor) { + require(isAllow(actor, UPDATE, this)) + require(deleted.not()) + if (this.content != content) { + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) } + this.content = content + } var overview = overview get() { @@ -98,22 +111,28 @@ class Post( } return field } - set(value) { - require(deleted.not()) - if (field != value) { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) - } - field = value + private set + + fun setOverview(overview: PostOverview?, actor: Actor) { + require(isAllow(actor, UPDATE, this)) + require(deleted.not()) + if (this.overview != overview) { + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) } + this.overview = overview + } var sensitive = sensitive - set(value) { - require(deleted.not()) - if (field != value) { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) - } - field = value + private set + + fun setSensitive(sensitive: Boolean, actor: Actor) { + isAllow(actor, UPDATE, this) + require(deleted.not()) + if (this.sensitive != sensitive) { + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) } + this.sensitive = sensitive + } val text: String get() { @@ -140,18 +159,20 @@ class Post( } private set - fun addMediaIds(mediaIds: List) { + fun addMediaIds(mediaIds: List, actor: Actor) { + require(isAllow(actor, UPDATE, this)) require(deleted.not()) - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) this.mediaIds = this.mediaIds.plus(mediaIds).distinct() } var deleted = deleted private set - fun delete() { + fun delete(actor: Actor) { + isAllow(actor, DELETE, this) if (deleted.not()) { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.delete)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.delete)) content = PostContent.empty overview = null mediaIds = emptyList() @@ -185,7 +206,8 @@ class Post( var moveTo = moveTo private set - fun moveTo(moveTo: PostId) { + fun moveTo(moveTo: PostId, actor: Actor) { + require(isAllow(actor, MOVE, this)) require(this.moveTo == null) this.moveTo = moveTo } @@ -203,6 +225,27 @@ class Post( return id.hashCode() } + fun reconstructWith(mediaIds: List, emojis: List, visibleActors: Set): Post { + return Post( + id = id, + actorId = actorId, + overview = overview, + content = PostContent(this.content.text, this.content.content, emojis), + createdAt = createdAt, + visibility = visibility, + url = url, + repostId = repostId, + replyId = replyId, + sensitive = sensitive, + apId = apId, + deleted = deleted, + mediaIds = mediaIds, + visibleActors = visibleActors, + hide = hide, + moveTo = moveTo + ) + } + companion object { fun create( id: PostId, @@ -221,14 +264,25 @@ class Post( visibleActors: Set = emptySet(), hide: Boolean = false, moveTo: PostId? = null, + actor: Actor, ): Post { + + require(actor.deleted.not()) + require(actor.moveTo == null) + + val visibility1 = if (actor.suspend && visibility == Visibility.PUBLIC) { + Visibility.UNLISTED + } else { + visibility + } + val post = Post( id, actorId, overview, content, createdAt, - visibility, + visibility1, url, repostId, replyId, @@ -243,5 +297,30 @@ class Post( post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.create)) return post } + + fun isAllow(actor: Actor, action: Action, resource: Post): Boolean { + return when (action) { + UPDATE -> { + if (actor.deleted) { + return true + } + resource.actorId == actor.id || actor.roles.contains(Role.ADMINISTRATOR) || actor.roles.contains( + Role.MODERATOR + ) + } + + MOVE -> resource.actorId == actor.id && actor.deleted.not() + DELETE -> resource.actorId == actor.id || + actor.roles.contains(Role.ADMINISTRATOR) || + actor.roles.contains(Role.MODERATOR) + + } + } + + enum class Action { + UPDATE, + MOVE, + DELETE, + } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt index 8a46bb19..808c6bdf 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt @@ -16,8 +16,8 @@ package dev.usbharu.hideout.core.domain.shared.domainevent -abstract class DomainEventBody(val map: Map) { - fun toMap(): Map { +abstract class DomainEventBody(val map: Map) { + fun toMap(): Map { return map } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt index 639bd216..3b26139e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt @@ -58,7 +58,8 @@ class ActorResultRowMapper : ResultRowMapper { .filter { it.isNotEmpty() } .map { EmojiId(it.toLong()) } .toSet(), - deleted = resultRow[Actors.deleted] + deleted = resultRow[Actors.deleted], + roles = emptySet() ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt index 6a338544..47d72f3c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt @@ -39,26 +39,25 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : .first() .let(postResultRowMapper::map) .apply { - addMediaIds( - it.mapNotNull { resultRow: ResultRow -> + reconstructWith( + mediaIds = it.mapNotNull { resultRow: ResultRow -> resultRow .getOrNull(PostsMedia.mediaId) ?.let { mediaId -> MediaId(mediaId) } - } - ) - content = content.copy(emojiIds = it - .mapNotNull { resultRow: ResultRow -> + }, + emojis = it + .mapNotNull { resultRow: ResultRow -> + resultRow + .getOrNull(PostsEmojis.emojiId) + ?.let { emojiId -> EmojiId(emojiId) } + }, + visibleActors = it.mapNotNull { resultRow: ResultRow -> resultRow - .getOrNull(PostsEmojis.emojiId) - ?.let { emojiId -> EmojiId(emojiId) } - } + .getOrNull(PostsVisibleActors.actorId) + ?.let { actorId -> ActorId(actorId) } + }.toSet() ) - visibleActors = it.mapNotNull { resultRow: ResultRow -> - resultRow - .getOrNull(PostsVisibleActors.actorId) - ?.let { actorId -> ActorId(actorId) } - }.toSet() - clearDomainEvents() + } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index b03e208a..09ba2411 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -60,7 +60,8 @@ class ActorFactoryImpl( lastPostAt = null, suspend = false, emojiIds = emptySet(), - deleted = false + deleted = false, + roles = emptySet() ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt index 7ee64d0a..4b1d4b29 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/PostFactoryImpl.kt @@ -17,7 +17,7 @@ package dev.usbharu.hideout.core.infrastructure.factory import dev.usbharu.hideout.core.config.ApplicationConfig -import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.ActorName import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.Post @@ -36,7 +36,7 @@ class PostFactoryImpl( private val applicationConfig: ApplicationConfig, ) { suspend fun createLocal( - actorId: ActorId, + actor: Actor, actorName: ActorName, overview: PostOverview?, content: String, @@ -49,19 +49,20 @@ class PostFactoryImpl( val id = idGenerateService.generateId() val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName.name + "/posts/" + id) return Post.create( - PostId(id), - actorId, - overview, - postContentFactoryImpl.create(content), - Instant.now(), - visibility, - url, - repostId, - replyId, - sensitive, - url, - false, - mediaIds, + id = PostId(id), + actorId = actor.id, + overview = overview, + content = postContentFactoryImpl.create(content), + createdAt = Instant.now(), + visibility = visibility, + url = url, + repostId = repostId, + replyId = replyId, + sensitive = sensitive, + apId = url, + deleted = false, + mediaIds = mediaIds, + actor = actor, ) } } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt index c0919feb..4bb92910 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt @@ -20,7 +20,7 @@ object TestActorFactory { inbox: URI = URI.create("https://example.com/$id/inbox"), outbox: URI = URI.create("https://example.com/$id/outbox"), uri: URI = URI.create("https://example.com/$id"), - publicKey: ActorPublicKey, + publicKey: ActorPublicKey = ActorPublicKey(""), privateKey: ActorPrivateKey? = null, createdAt: Instant = Instant.now(), keyId: String = "https://example.com/$id#key-id", @@ -65,6 +65,7 @@ object TestActorFactory { moveTo = moveTo, emojiIds = emojiIds, deleted = deleted, + roles = emptySet() ) } } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index f0e4cf1f..5c4f0226 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -2,6 +2,8 @@ package dev.usbharu.hideout.core.domain.model.post import dev.usbharu.hideout.core.domain.event.post.PostEvent import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows @@ -29,100 +31,102 @@ class PostTest { fun visibilityãŒDIRECTã®ã¨ã変更ã§ããªã„() { val post = TestPostFactory.create(visibility = Visibility.DIRECT) + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + assertThrows { - post.visibility = Visibility.PUBLIC + post.setVisibility(Visibility.PUBLIC, actor) } assertThrows { - post.visibility = Visibility.UNLISTED + post.setVisibility(Visibility.UNLISTED, actor) } assertThrows { - post.visibility = Visibility.FOLLOWERS + post.setVisibility(Visibility.FOLLOWERS, actor) } } @Test fun visibilityã‚’å°ã•ãã™ã‚‹ã“ã¨ã¯ã§ããªã„PUBLIC() { val post = TestPostFactory.create(visibility = Visibility.PUBLIC) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.visibility = Visibility.DIRECT + post.setVisibility(Visibility.DIRECT, actor) } assertThrows { - post.visibility = Visibility.UNLISTED + post.setVisibility(Visibility.UNLISTED, actor) } assertThrows { - post.visibility = Visibility.FOLLOWERS + post.setVisibility(Visibility.FOLLOWERS, actor) } } @Test fun visibilityã‚’å°ã•ãã™ã‚‹ã“ã¨ã¯ã§ããªã„UNLISTED() { val post = TestPostFactory.create(visibility = Visibility.UNLISTED) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.visibility = Visibility.DIRECT + post.setVisibility(Visibility.DIRECT, actor) } assertThrows { - post.visibility = Visibility.FOLLOWERS + post.setVisibility(Visibility.FOLLOWERS, actor) } } @Test fun visibilityã‚’å°ã•ãã™ã‚‹ã“ã¨ã¯ã§ããªã„FOLLOWERS() { val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.visibility = Visibility.DIRECT + post.setVisibility(Visibility.DIRECT, actor) } } @Test fun visibilityã‚’DIRECTã«ã‚ã¨ã‹ã‚‰ã™ã‚‹ã“ã¨ã¯ã§ããªã„() { val post = TestPostFactory.create(visibility = Visibility.DIRECT) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.visibility = Visibility.DIRECT + post.setVisibility(Visibility.DIRECT, actor) } } @Test fun visibilityを大ããã™ã‚‹ã“ã¨ãŒã§ãã‚‹FOLLOWERS() { val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertDoesNotThrow { - post.visibility = Visibility.UNLISTED + post.setVisibility(Visibility.UNLISTED, actor) } val post2 = TestPostFactory.create(visibility = Visibility.FOLLOWERS) assertDoesNotThrow { - post2.visibility = Visibility.PUBLIC + post2.setVisibility(Visibility.PUBLIC, actor) } } @Test fun visibilityを大ããã™ã‚‹ã“ã¨ãŒã§ãã‚‹UNLISTED() { val post = TestPostFactory.create(visibility = Visibility.UNLISTED) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertDoesNotThrow { - post.visibility = Visibility.PUBLIC + post.setVisibility(Visibility.PUBLIC, actor) } } @Test fun deletedãŒtrueã®ã¨ãvisibilityを変更ã§ããªã„() { val post = TestPostFactory.create(visibility = Visibility.UNLISTED, deleted = true) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.visibility = Visibility.PUBLIC + post.setVisibility(Visibility.PUBLIC, actor) } } @Test fun visibilityãŒå¤‰æ›´ã•ã‚Œãªã„é™ã‚Šãƒ‰ãƒ¡ã‚¤ãƒ³ã‚¤ãƒ™ãƒ³ãƒˆã¯ç™ºç”Ÿã—ãªã„() { val post = TestPostFactory.create(visibility = Visibility.UNLISTED) - - post.visibility = Visibility.UNLISTED + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setVisibility(Visibility.UNLISTED, actor) assertEmpty(post) } @@ -130,7 +134,8 @@ class PostTest { @Test fun visibilityãŒå¤‰æ›´ã•ã‚Œã‚‹ã¨updateイベントãŒç™ºç”Ÿã™ã‚‹() { val post = TestPostFactory.create(visibility = Visibility.UNLISTED) - post.visibility = Visibility.PUBLIC + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setVisibility(Visibility.PUBLIC, actor) assertContainsEvent(post, PostEvent.update.eventName) } @@ -138,51 +143,51 @@ class PostTest { @Test fun deletedãŒtrueã®ã¨ãvisibleActorsを変更ã§ããªã„() { val post = TestPostFactory.create(deleted = true) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.visibleActors = setOf(ActorId(100)) + post.setVisibleActors(setOf(ActorId(100)), actor) } } @Test fun ã‚”visibilityãŒDIRECT以外ã®æ™‚visibleActorsを変更ã§ããªã„() { val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) - - post.visibleActors = setOf(ActorId(100)) + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setVisibleActors(setOf(ActorId(100)), actor) assertEmpty(post) val post2 = TestPostFactory.create(visibility = Visibility.UNLISTED) - post2.visibleActors = setOf(ActorId(100)) + post2.setVisibleActors(setOf(ActorId(100)), actor) assertEmpty(post2) val post3 = TestPostFactory.create(visibility = Visibility.PUBLIC) - post3.visibleActors = setOf(ActorId(100)) + post3.setVisibleActors(setOf(ActorId(100)), actor) assertEmpty(post3) } @Test fun visibilityãŒDIRECTã®æ™‚visibleActorsを変更ã§ãã‚‹() { val post = TestPostFactory.create(visibility = Visibility.DIRECT) - - post.visibleActors = setOf(ActorId(100)) + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setVisibleActors(setOf(ActorId(100)), actor) assertEquals(setOf(ActorId(100)), post.visibleActors) } @Test fun visibleActorsã‹ã‚‰å‰Šé™¤ã•ã‚Œã‚‹ã“ã¨ã¯ãªã„() { val post = TestPostFactory.create(visibility = Visibility.DIRECT, visibleActors = listOf(100)) - - post.visibleActors = setOf(ActorId(200)) + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setVisibleActors(setOf(ActorId(200)), actor) assertEquals(setOf(ActorId(100), ActorId(200)), post.visibleActors) } @Test fun visibleActorsã«è¿½åŠ ã•ã‚ŒãŸæ™‚updateイベントãŒç™ºç”Ÿã™ã‚‹() { val post = TestPostFactory.create(visibility = Visibility.DIRECT) - - post.visibleActors = setOf(ActorId(100)) + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setVisibleActors(setOf(ActorId(100)), actor) assertContainsEvent(post, PostEvent.update.eventName) } @@ -197,17 +202,17 @@ class PostTest { @Test fun deletedãŒtrueã®æ™‚contentをセットã§ããªã„() { val post = TestPostFactory.create(deleted = true) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.content = PostContent("test", "test", emptyList()) + post.setContent(PostContent("test", "test", emptyList()), actor) } } @Test fun contentã®å†…容ãŒå¤‰æ›´ã•ã‚ŒãŸã‚‰updateイベントãŒç™ºç”Ÿã™ã‚‹() { val post = TestPostFactory.create() - - post.content = PostContent("test", "test", emptyList()) + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setContent(PostContent("test", "test", emptyList()), actor) assertContainsEvent(post, PostEvent.update.eventName) } @@ -228,19 +233,19 @@ class PostTest { @Test fun deletedãŒtrueã®ã¨ãセットã§ããªã„() { val post = TestPostFactory.create(deleted = true) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) assertThrows { - post.overview = PostOverview("aaaa") + post.setOverview(PostOverview("aaaa"), actor) } } @Test fun deletedãŒfalseã®ã¨ãセットã§ãã‚‹() { val post = TestPostFactory.create(deleted = false) - + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) val overview = PostOverview("aaaa") assertDoesNotThrow { - post.overview = overview + post.setOverview(overview, actor) } assertEquals(overview, post.overview) @@ -250,11 +255,10 @@ class PostTest { @Test fun overviewã®å†…容ãŒæ›´æ–°ã•ã‚Œãªã‹ã£ãŸæ™‚イベントãŒç™ºç”Ÿã—ãªã„() { val post = TestPostFactory.create(overview = "aaaa") - post.overview = PostOverview("aaaa") + val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) + post.setOverview(PostOverview("aaaa"), actor) assertEmpty(post) } - - } \ No newline at end of file From a939dd5f305c8ac019e6bd03d5eff4f9881e5303 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 8 Jun 2024 18:12:08 +0900 Subject: [PATCH 38/54] =?UTF-8?q?feat:=20=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E7=AD=89=E3=81=8C=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/application/relationship/Block.kt | 19 ++++ .../application/relationship/FollowRequest.kt | 19 ++++ .../relationship/GetRelationship.kt | 19 ++++ .../GetRelationshipApplicationService.kt | 68 +++++++++++++ .../application/relationship/Relationship.kt | 58 +++++++++++ .../core/application/relationship/Unblock.kt | 19 ++++ .../UserBlockApplicationService.kt | 69 +++++++++++++ .../UserFollowRequestApplicationService.kt | 61 ++++++++++++ .../UserUnblockApplicationService.kt | 59 +++++++++++ .../ActorInstanceRelationship.kt | 12 ++- .../ActorInstanceRelationshipRepository.kt | 4 + .../domain/model/relationship/Relationship.kt | 31 ++++++ .../relationship/RelationshipRepository.kt | 3 + .../relationship/RelationshipDomainService.kt | 33 +++++++ ...osedActorInstanceRelationshipRepository.kt | 98 +++++++++++++++++++ .../ExposedRelationshipRepository.kt | 94 ++++++++++++++++++ .../oauth2/Oauth2CommandExecutor.kt | 4 +- .../resources/db/migration/V1__Init_DB.sql | 12 +++ .../application/accounts/GetAccount.kt | 19 ++++ .../accounts/GetAccountApplicationService.kt | 40 ++++++++ .../interfaces/api/SpringAccountApi.kt | 57 ++++++++++- 21 files changed, 792 insertions(+), 6 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccount.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt new file mode 100644 index 00000000..a80ad2b7 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +data class Block(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt new file mode 100644 index 00000000..eb61df82 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +data class FollowRequest(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt new file mode 100644 index 00000000..c040253b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +data class GetRelationship(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt new file mode 100644 index 00000000..9ba79ff3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +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.application.shared.UserDetailGettableCommandExecutor +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.actorinstancerelationship.ActorInstanceRelationship +import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationshipRepository +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class GetRelationshipApplicationService( + private val relationshipRepository: RelationshipRepository, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, + private val actorInstanceRelationshipRepository: ActorInstanceRelationshipRepository, + transaction: Transaction, +) : + AbstractApplicationService( + transaction, logger + ) { + companion object { + private val logger = LoggerFactory.getLogger(GetRelationshipApplicationService::class.java) + } + + override suspend fun internalExecute(command: GetRelationship, executor: CommandExecutor): Relationship { + require(executor is UserDetailGettableCommandExecutor) + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + 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)) + + val relationship1 = (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) + ?: ActorInstanceRelationship.default( + actor.id, + target.instance + ) + + return Relationship.of(relationship, relationship1, actorInstanceRelationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt new file mode 100644 index 00000000..c7b2377a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship +import dev.usbharu.hideout.core.domain.model.relationship.Relationship + +data class Relationship( + val actorId: Long, + val targetId: Long, + val following: Boolean, + val followedBy: Boolean, + val blocking: Boolean, + val blockedBy: Boolean, + val muting: Boolean, + val followRequesting: Boolean, + val followRequestedBy: Boolean, + val domainBlocking: Boolean, + val domainMuting: Boolean, + val domainDoNotSendPrivate: Boolean, +) { + companion object { + fun of( + relationship: Relationship, + relationship2: Relationship, + actorInstanceRelationship: ActorInstanceRelationship, + ): dev.usbharu.hideout.core.application.relationship.Relationship { + return Relationship( + relationship.actorId.id, + relationship.targetActorId.id, + relationship.following, + relationship2.following, + relationship.blocking, + relationship2.blocking, + relationship.muting, + relationship.followRequesting, + relationship2.followRequesting, + actorInstanceRelationship.isBlocking(), + actorInstanceRelationship.isMuting(), + actorInstanceRelationship.isDoNotSendPrivate() + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt new file mode 100644 index 00000000..db5d856d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +data class Unblock(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt new file mode 100644 index 00000000..8103b9e4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.service.relationship.RelationshipDomainService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserBlockApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, + private val relationshipDomainService: RelationshipDomainService, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: Block, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) ?: Relationship.default( + actor.id, + targetId + ) + + val inverseRelationship = + relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) ?: Relationship.default( + targetId, + actor.id + ) + + relationshipDomainService.block(relationship, inverseRelationship) + + + relationshipRepository.save(relationship) + relationshipRepository.save(inverseRelationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt new file mode 100644 index 00000000..29c6f805 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserFollowRequestApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : AbstractApplicationService( + transaction, logger +) { + + override suspend fun internalExecute(command: FollowRequest, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) ?: Relationship.default( + actor.id, + targetId + ) + + relationship.followRequest() + + relationshipRepository.save(relationship) + } + + companion object { + private val logger = LoggerFactory.getLogger(UserFollowRequestApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt new file mode 100644 index 00000000..3bf42d3e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship + +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserUnblockApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: Unblock, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) ?: Relationship.default( + actor.id, + targetId + ) + + relationship.unblock() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt index 178716a0..3cef6c86 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -96,5 +96,15 @@ data class ActorInstanceRelationship( ")" } - + companion object { + fun default(actorId: ActorId, instanceId: InstanceId): ActorInstanceRelationship { + return ActorInstanceRelationship( + actorId = actorId, + instanceId = instanceId, + blocking = false, + muting = false, + doNotSendPrivate = false + ) + } + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt index 5bc7abd5..ea2bba54 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationshipRepository.kt @@ -16,7 +16,11 @@ package dev.usbharu.hideout.core.domain.model.actorinstancerelationship +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.instance.InstanceId + interface ActorInstanceRelationshipRepository { suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship suspend fun delete(actorInstanceRelationship: ActorInstanceRelationship) + suspend fun findByActorIdAndInstanceId(actorId: ActorId, instanceId: InstanceId): ActorInstanceRelationship? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt index dd06c900..930670a9 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt @@ -102,4 +102,35 @@ class Relationship( fun rejectFollowRequest() { followRequesting = false } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Relationship + + if (actorId != other.actorId) return false + if (targetActorId != other.targetActorId) return false + + return true + } + + override fun hashCode(): Int { + var result = actorId.hashCode() + result = 31 * result + targetActorId.hashCode() + return result + } + + + companion object { + fun default(actorId: ActorId, targetActorId: ActorId): Relationship = Relationship( + actorId = actorId, + targetActorId = targetActorId, + following = false, + blocking = false, + muting = false, + followRequesting = false, + mutingFollowRequest = false + ) + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index 6b84b91c..d74884af 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -16,7 +16,10 @@ package dev.usbharu.hideout.core.domain.model.relationship +import dev.usbharu.hideout.core.domain.model.actor.ActorId + interface RelationshipRepository { suspend fun save(relationship: Relationship): Relationship suspend fun delete(relationship: Relationship) + suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): Relationship? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt new file mode 100644 index 00000000..2c81724c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.domain.service.relationship + +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import org.springframework.stereotype.Service + +@Service +class RelationshipDomainService { + fun block(relationship: Relationship, inverseRelationship: Relationship) { + require(relationship != inverseRelationship) + require(relationship.actorId == inverseRelationship.targetActorId) + require(relationship.targetActorId == inverseRelationship.actorId) + + relationship.block() + inverseRelationship.unfollow() + inverseRelationship.unfollowRequest() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt new file mode 100644 index 00000000..9cc1cfd4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship +import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationshipRepository +import dev.usbharu.hideout.core.domain.model.instance.InstanceId +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 ExposedActorInstanceRelationshipRepository(override val domainEventPublisher: DomainEventPublisher) : + ActorInstanceRelationshipRepository, AbstractRepository(), + DomainEventPublishableRepository { + override suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship { + query { + ActorInstanceRelationships.upsert { + it[actorId] = actorInstanceRelationship.actorId.id + it[instanceId] = actorInstanceRelationship.instanceId.instanceId + it[blocking] = actorInstanceRelationship.isBlocking() + it[muting] = actorInstanceRelationship.isMuting() + it[doNotSendPrivate] = actorInstanceRelationship.isDoNotSendPrivate() + } + } + update(actorInstanceRelationship) + return actorInstanceRelationship + } + + override suspend fun delete(actorInstanceRelationship: ActorInstanceRelationship) { + query { + ActorInstanceRelationships.deleteWhere { + actorId eq actorInstanceRelationship.actorId.id and (instanceId eq actorInstanceRelationship.instanceId.instanceId) + } + } + update(actorInstanceRelationship) + } + + override suspend fun findByActorIdAndInstanceId( + actorId: ActorId, + instanceId: InstanceId, + ): ActorInstanceRelationship? = query { + ActorInstanceRelationships + .selectAll() + .where { + ActorInstanceRelationships.actorId eq actorId.id and (ActorInstanceRelationships.instanceId eq instanceId.instanceId) + } + .singleOrNull() + ?.toActorInstanceRelationship() + } + + override val logger: Logger + get() = Companion.logger + + companion object { + private val logger = LoggerFactory.getLogger(ExposedActorInstanceRelationshipRepository::class.java) + } +} + +private fun ResultRow.toActorInstanceRelationship(): ActorInstanceRelationship { + return ActorInstanceRelationship( + actorId = ActorId(this[ActorInstanceRelationships.actorId]), + instanceId = InstanceId(this[ActorInstanceRelationships.instanceId]), + blocking = this[ActorInstanceRelationships.blocking], + muting = this[ActorInstanceRelationships.muting], + doNotSendPrivate = this[ActorInstanceRelationships.doNotSendPrivate], + ) +} + +object ActorInstanceRelationships : Table("actor_instance_relationships") { + val actorId = long("actor_id").references(Actors.id) + val instanceId = long("instance_id").references(Instance.id) + val blocking = bool("blocking") + val muting = bool("muting") + val doNotSendPrivate = bool("do_not_send_private") + + override val primaryKey: PrimaryKey = PrimaryKey(actorId, instanceId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt new file mode 100644 index 00000000..eef233b0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +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 +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 ExposedRelationshipRepository(override val domainEventPublisher: DomainEventPublisher) : RelationshipRepository, + AbstractRepository(), + DomainEventPublishableRepository { + override suspend fun save(relationship: Relationship): Relationship { + query { + Relationships.upsert { + it[actorId] = relationship.actorId.id + it[targetActorId] = relationship.targetActorId.id + it[following] = relationship.following + it[blocking] = relationship.blocking + it[muting] = relationship.muting + it[followRequesting] = relationship.followRequesting + it[mutingFollowRequest] = relationship.mutingFollowRequest + } + } + update(relationship) + return relationship + } + + override suspend fun delete(relationship: Relationship) { + query { + Relationships.deleteWhere { + actorId eq relationship.actorId.id and (targetActorId eq relationship.targetActorId.id) + } + } + update(relationship) + } + + override suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): Relationship? = query { + Relationships.selectAll().where { + Relationships.actorId eq actorId.id and (Relationships.targetActorId eq targetId.id) + }.singleOrNull()?.toRelationships() + } + + override val logger: Logger + get() = Companion.logger + + + companion object { + private val logger = LoggerFactory.getLogger(ExposedRelationshipRepository::class.java) + } +} + +fun ResultRow.toRelationships(): Relationship = Relationship( + actorId = ActorId(this[Relationships.actorId]), + targetActorId = ActorId(this[Relationships.targetActorId]), + following = this[Relationships.following], + blocking = this[Relationships.blocking], + muting = this[Relationships.muting], + followRequesting = this[Relationships.followRequesting], + mutingFollowRequest = this[Relationships.mutingFollowRequest] +) + +object Relationships : Table("relationships") { + val actorId = long("actor_id").references(Actors.id) + val targetActorId = long("target_actor_id").references(Actors.id) + val following = bool("following") + val blocking = bool("blocking") + val muting = bool("muting") + val followRequesting = bool("follow_requesting") + val mutingFollowRequest = bool("muting_follow_request") + + override val primaryKey: PrimaryKey = PrimaryKey(actorId, targetActorId) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt index 23f8a73e..3dd05d31 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt @@ -17,5 +17,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import dev.usbharu.hideout.core.application.shared.CommandExecutor +import dev.usbharu.hideout.core.application.shared.UserDetailGettableCommandExecutor -class Oauth2CommandExecutor(override val executor: String, val userDetailId: Long) : CommandExecutor \ No newline at end of file +class Oauth2CommandExecutor(override val executor: String, override val userDetailId: Long) : CommandExecutor, + UserDetailGettableCommandExecutor \ No newline at end of file diff --git a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql index dc757618..fdc8afea 100644 --- a/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql +++ b/hideout-core/src/main/resources/db/migration/V1__Init_DB.sql @@ -249,3 +249,15 @@ CREATE TABLE oauth2_authorization device_code_metadata varchar(4000) DEFAULT NULL, PRIMARY KEY (id) ); + +create table if not exists actor_instance_relationships +( + actor_id bigint not null, + instance_id bigint not null, + blocking boolean not null, + muting boolean not null, + do_not_send_private boolean not null, + PRIMARY KEY (actor_id, instance_id), + constraint fk_actor_instance_relationships_actor_id__id foreign key (actor_id) references actors (id) on delete cascade on update cascade, + constraint fk_actor_instance_relationships_instance_id__id foreign key (instance_id) references instance (id) on delete cascade on update cascade +); \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccount.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccount.kt new file mode 100644 index 00000000..340607b9 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccount.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.accounts + +data class GetAccount(val accountId: String) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt new file mode 100644 index 00000000..5fb7249f --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.accounts + +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.mastodon.interfaces.api.generated.model.Account +import dev.usbharu.hideout.mastodon.query.AccountQueryService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class GetAccountApplicationService(private val accountQueryService: AccountQueryService, transaction: Transaction) : + AbstractApplicationService( + transaction, + logger + ) { + override suspend fun internalExecute(command: GetAccount, executor: CommandExecutor): Account { + return accountQueryService.findById(command.accountId.toLong()) ?: throw Exception("Account not found") + } + + companion object { + private val logger = LoggerFactory.getLogger(GetAccountApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt index 7483eda8..8367597f 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt @@ -18,9 +18,14 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.actor.GetUserDetail import dev.usbharu.hideout.core.application.actor.GetUserDetailApplicationService +import dev.usbharu.hideout.core.application.relationship.* +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutor import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +import dev.usbharu.hideout.mastodon.application.accounts.GetAccount +import dev.usbharu.hideout.mastodon.application.accounts.GetAccountApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Relationship import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @@ -28,20 +33,60 @@ import org.springframework.stereotype.Controller class SpringAccountApi( private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, private val getUserDetailApplicationService: GetUserDetailApplicationService, + private val getAccountApplicationService: GetAccountApplicationService, + private val userFollowRequestApplicationService: UserFollowRequestApplicationService, + private val getRelationshipApplicationService: GetRelationshipApplicationService, + private val userBlockApplicationService: UserBlockApplicationService, + private val userUnblockApplicationService: UserUnblockApplicationService, ) : AccountApi { override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { - return super.apiV1AccountsIdBlockPost(id) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userBlockApplicationService.execute(Block(id.toLong()), executor) + return fetchRelationship(id, executor) } override suspend fun apiV1AccountsIdFollowPost( id: String, followRequestBody: FollowRequestBody?, ): ResponseEntity { - return super.apiV1AccountsIdFollowPost(id, followRequestBody) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userFollowRequestApplicationService.execute( + FollowRequest(id.toLong()), executor + ) + return fetchRelationship(id, executor) + } + + private suspend fun fetchRelationship( + id: String, + executor: Oauth2CommandExecutor, + ): ResponseEntity { + val relationship = getRelationshipApplicationService.execute(GetRelationship(id.toLong()), executor) + return ResponseEntity.ok( + Relationship( + id = relationship.targetId.toString(), + following = relationship.following, + showingReblogs = true, + notifying = false, + followedBy = relationship.followedBy, + blocking = relationship.blocking, + blockedBy = relationship.blockedBy, + muting = relationship.muting, + mutingNotifications = false, + requested = relationship.followRequesting, + domainBlocking = relationship.domainBlocking, + endorsed = false, + note = "" + ) + ) } override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity { - return super.apiV1AccountsIdGet(id) + return ResponseEntity.ok( + getAccountApplicationService.execute( + GetAccount(id), + oauth2CommandExecutorFactory.getCommandExecutor() + ) + ) } override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { @@ -53,7 +98,11 @@ class SpringAccountApi( } override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { - return super.apiV1AccountsIdUnblockPost(id) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userUnblockApplicationService.execute( + Unblock(id.toLong()), executor + ) + return fetchRelationship(id, executor) } override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { From 1245165516ddbca1539cfdff8c841d7f8ecebc4f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 8 Jun 2024 18:49:57 +0900 Subject: [PATCH 39/54] =?UTF-8?q?feat:=20=E3=83=9F=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E3=83=88=E7=AD=89=E3=81=8C=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AcceptFollowRequest.kt | 19 ++++++ ...erAcceptFollowRequestApplicationService.kt | 58 ++++++++++++++++ .../relationship/{ => block}/Block.kt | 2 +- .../UserBlockApplicationService.kt | 2 +- .../{ => followrequest}/FollowRequest.kt | 2 +- .../UserFollowRequestApplicationService.kt | 2 +- .../relationship/{ => get}/GetRelationship.kt | 2 +- .../GetRelationshipApplicationService.kt | 2 +- .../relationship/{ => get}/Relationship.kt | 4 +- .../application/relationship/mute/Mute.kt | 19 ++++++ .../mute/UserMuteApplicationService.kt | 60 +++++++++++++++++ .../RejectFollowRequest.kt | 19 ++++++ ...erRejectFollowRequestApplicationService.kt | 58 ++++++++++++++++ .../RemoveFromFollowers.kt | 19 ++++++ ...erRemoveFromFollowersApplicationService.kt | 60 +++++++++++++++++ .../relationship/{ => unblock}/Unblock.kt | 2 +- .../UserUnblockApplicationService.kt | 3 +- .../relationship/unfollow/Unfollow.kt | 19 ++++++ .../UserUnfollowApplicationService.kt | 60 +++++++++++++++++ .../application/relationship/unmute/Unmute.kt | 19 ++++++ .../unmute/UserUnmuteApplicationService.kt | 60 +++++++++++++++++ .../interfaces/api/SpringAccountApi.kt | 66 ++++++++++++++++--- 22 files changed, 539 insertions(+), 18 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => block}/Block.kt (90%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => block}/UserBlockApplicationService.kt (97%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => followrequest}/FollowRequest.kt (90%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => followrequest}/UserFollowRequestApplicationService.kt (97%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => get}/GetRelationship.kt (91%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => get}/GetRelationshipApplicationService.kt (98%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => get}/Relationship.kt (93%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => unblock}/Unblock.kt (90%) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/{ => unblock}/UserUnblockApplicationService.kt (93%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt new file mode 100644 index 00000000..6c0d9f20 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest + +data class AcceptFollowRequest(val sourceActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt new file mode 100644 index 00000000..3a89b565 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest + +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserAcceptFollowRequestApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: AcceptFollowRequest, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.sourceActorId) + + val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) + ?: throw Exception("Follow request not found") + + relationship.acceptFollowRequest() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt similarity index 90% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt index a80ad2b7..7a095b92 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Block.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.block data class Block(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt similarity index 97% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt index 8103b9e4..eff97a4f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserBlockApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.block import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.CommandExecutor diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt similarity index 90% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt index eb61df82..3f8de0a7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/FollowRequest.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.followrequest data class FollowRequest(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt similarity index 97% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt index 29c6f805..1ca218e2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.followrequest import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.CommandExecutor diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt similarity index 91% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt index c040253b..90df1b82 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.get data class GetRelationship(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt similarity index 98% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt index 9ba79ff3..3b865cf2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/GetRelationshipApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.get import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.CommandExecutor diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt index c7b2377a..b14c78fd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Relationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.get import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship import dev.usbharu.hideout.core.domain.model.relationship.Relationship @@ -38,7 +38,7 @@ data class Relationship( relationship: Relationship, relationship2: Relationship, actorInstanceRelationship: ActorInstanceRelationship, - ): dev.usbharu.hideout.core.application.relationship.Relationship { + ): dev.usbharu.hideout.core.application.relationship.get.Relationship { return Relationship( relationship.actorId.id, relationship.targetActorId.id, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt new file mode 100644 index 00000000..79a56830 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.mute + +data class Mute(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt new file mode 100644 index 00000000..6d39c013 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.mute + +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserMuteApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: Mute, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) ?: Relationship.default( + actor.id, + targetId + ) + + relationship.mute() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt new file mode 100644 index 00000000..4662eff1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest + +data class RejectFollowRequest(val sourceActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt new file mode 100644 index 00000000..703eb6ad --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest + +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserRejectFollowRequestApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: RejectFollowRequest, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.sourceActorId) + + val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) + ?: throw Exception("Follow request not found") + + relationship.rejectFollowRequest() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt new file mode 100644 index 00000000..f9642099 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.removefromfollowers + +data class RemoveFromFollowers(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt new file mode 100644 index 00000000..c4f2e544 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.removefromfollowers + +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserRemoveFromFollowersApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: RemoveFromFollowers, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) ?: Relationship.default( + targetId, + actor.id + ) + + relationship.unfollow() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt similarity index 90% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt index db5d856d..7b85c603 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/Unblock.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.unblock data class Unblock(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt similarity index 93% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt index 3bf42d3e..a7f50705 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/UserUnblockApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.application.relationship +package dev.usbharu.hideout.core.application.relationship.unblock +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.CommandExecutor import dev.usbharu.hideout.core.application.shared.Transaction diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt new file mode 100644 index 00000000..60190dab --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.unfollow + +data class Unfollow(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt new file mode 100644 index 00000000..5f00f000 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.unfollow + +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserUnfollowApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: Unfollow, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) ?: Relationship.default( + actor.id, + targetId + ) + + relationship.unfollow() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt new file mode 100644 index 00000000..1939ee25 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.unmute + +data class Unmute(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt new file mode 100644 index 00000000..5b224e7f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.relationship.unmute + +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +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.application.shared.UserDetailGettableCommandExecutor +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.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserUnmuteApplicationService( + private val relationshipRepository: RelationshipRepository, + transaction: Transaction, + private val actorRepository: ActorRepository, + private val userDetailRepository: UserDetailRepository, +) : + AbstractApplicationService(transaction, logger) { + companion object { + private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) + } + + override suspend fun internalExecute(command: Unmute, executor: CommandExecutor) { + require(executor is UserDetailGettableCommandExecutor) + + val userDetail = userDetailRepository.findById(executor.userDetailId)!! + val actor = actorRepository.findById(userDetail.actorId)!! + + val targetId = ActorId(command.targetActorId) + val relationship = relationshipRepository.findByActorIdAndTargetId(actor.id, targetId) ?: Relationship.default( + actor.id, + targetId + ) + + relationship.unmute() + + relationshipRepository.save(relationship) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt index 8367597f..7f207ce7 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt @@ -18,14 +18,32 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.actor.GetUserDetail import dev.usbharu.hideout.core.application.actor.GetUserDetailApplicationService -import dev.usbharu.hideout.core.application.relationship.* +import dev.usbharu.hideout.core.application.relationship.acceptfollowrequest.AcceptFollowRequest +import dev.usbharu.hideout.core.application.relationship.acceptfollowrequest.UserAcceptFollowRequestApplicationService +import dev.usbharu.hideout.core.application.relationship.block.Block +import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService +import dev.usbharu.hideout.core.application.relationship.followrequest.FollowRequest +import dev.usbharu.hideout.core.application.relationship.followrequest.UserFollowRequestApplicationService +import dev.usbharu.hideout.core.application.relationship.get.GetRelationship +import dev.usbharu.hideout.core.application.relationship.get.GetRelationshipApplicationService +import dev.usbharu.hideout.core.application.relationship.mute.Mute +import dev.usbharu.hideout.core.application.relationship.mute.UserMuteApplicationService +import dev.usbharu.hideout.core.application.relationship.rejectfollowrequest.RejectFollowRequest +import dev.usbharu.hideout.core.application.relationship.rejectfollowrequest.UserRejectFollowRequestApplicationService +import dev.usbharu.hideout.core.application.relationship.removefromfollowers.RemoveFromFollowers +import dev.usbharu.hideout.core.application.relationship.removefromfollowers.UserRemoveFromFollowersApplicationService +import dev.usbharu.hideout.core.application.relationship.unblock.Unblock +import dev.usbharu.hideout.core.application.relationship.unblock.UserUnblockApplicationService +import dev.usbharu.hideout.core.application.relationship.unfollow.Unfollow +import dev.usbharu.hideout.core.application.relationship.unfollow.UserUnfollowApplicationService +import dev.usbharu.hideout.core.application.relationship.unmute.Unmute +import dev.usbharu.hideout.core.application.relationship.unmute.UserUnmuteApplicationService import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutor import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory import dev.usbharu.hideout.mastodon.application.accounts.GetAccount import dev.usbharu.hideout.mastodon.application.accounts.GetAccountApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* -import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Relationship import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @@ -38,7 +56,15 @@ class SpringAccountApi( private val getRelationshipApplicationService: GetRelationshipApplicationService, private val userBlockApplicationService: UserBlockApplicationService, private val userUnblockApplicationService: UserUnblockApplicationService, + private val userMuteApplicationService: UserMuteApplicationService, + private val userUnmuteApplicationService: UserUnmuteApplicationService, + private val userAcceptFollowRequestApplicationService: UserAcceptFollowRequestApplicationService, + private val userRejectFollowRequestApplicationService: UserRejectFollowRequestApplicationService, + private val userRemoveFromFollowersApplicationService: UserRemoveFromFollowersApplicationService, + private val userUnfollowApplicationService: UserUnfollowApplicationService, ) : AccountApi { + + override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { val executor = oauth2CommandExecutorFactory.getCommandExecutor() userBlockApplicationService.execute(Block(id.toLong()), executor) @@ -90,11 +116,19 @@ class SpringAccountApi( } override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { - return super.apiV1AccountsIdMutePost(id) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userMuteApplicationService.execute( + Mute(id.toLong()), executor + ) + return fetchRelationship(id, executor) } override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { - return super.apiV1AccountsIdRemoveFromFollowersPost(id) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userRemoveFromFollowersApplicationService.execute( + RemoveFromFollowers(id.toLong()), executor + ) + return fetchRelationship(id, executor) } override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { @@ -106,11 +140,19 @@ class SpringAccountApi( } override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { - return super.apiV1AccountsIdUnfollowPost(id) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userUnfollowApplicationService.execute( + Unfollow(id.toLong()), executor + ) + return fetchRelationship(id, executor) } override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity { - return super.apiV1AccountsIdUnmutePost(id) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userUnmuteApplicationService.execute( + Unmute(id.toLong()), executor + ) + return fetchRelationship(id, executor) } override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity { @@ -174,11 +216,19 @@ class SpringAccountApi( } override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { - return super.apiV1FollowRequestsAccountIdAuthorizePost(accountId) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userAcceptFollowRequestApplicationService.execute( + AcceptFollowRequest(accountId.toLong()), executor + ) + return fetchRelationship(accountId, executor) } override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity { - return super.apiV1FollowRequestsAccountIdRejectPost(accountId) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userRejectFollowRequestApplicationService.execute( + RejectFollowRequest(accountId.toLong()), executor + ) + return fetchRelationship(accountId, executor) } } \ No newline at end of file From 70a1a17e372746a275a3d83b3965d57ea10be009 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 10 Jun 2024 00:12:01 +0900 Subject: [PATCH 40/54] wip --- .../core/domain/model/filter/Filter.kt | 51 +++++++++++++++++++ .../core/domain/model/filter/FilterAction.kt | 6 +++ .../core/domain/model/filter/FilterContext.kt | 9 ++++ .../core/domain/model/filter/FilterId.kt | 4 ++ .../core/domain/model/filter/FilterKeyword.kt | 17 +++++++ .../domain/model/filter/FilterKeywordId.kt | 4 ++ .../model/filter/FilterKeywordKeyword.kt | 4 ++ .../core/domain/model/filter/FilterMode.kt | 7 +++ .../core/domain/model/filter/FilterName.kt | 4 ++ .../domain/model/filter/FilterRepository.kt | 6 +++ .../core/domain/model/filter/FilterResult.kt | 4 ++ .../core/domain/model/filter/FilteredPost.kt | 5 ++ .../service/filter/FilterDomainService.kt | 22 ++++++++ 13 files changed, 143 insertions(+) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordId.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordKeyword.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterName.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt new file mode 100644 index 00000000..f1ecdd1a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -0,0 +1,51 @@ +package dev.usbharu.hideout.core.domain.model.filter + +import dev.usbharu.hideout.core.domain.model.filter.Filter.Companion.Action.SET_KEYWORDS +import dev.usbharu.hideout.core.domain.model.filter.FilterMode.* +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId + +class Filter( + val id: FilterId, + val userDetailId: UserDetailId, + var name: FilterName, + val filterContext: List, + val filterAction: FilterAction, + filterKeywords: Set +) { + var filterKeywords = filterKeywords + private set + + fun setFilterKeywords(filterKeywords: Set, user: UserDetail) { + require(isAllow(user, SET_KEYWORDS, this)) + this.filterKeywords = filterKeywords + } + + fun compileFilter(): Regex { + val words = mutableListOf() + val wholeWords = mutableListOf() + val regexes = mutableListOf() + + for (filterKeyword in filterKeywords) { + when (filterKeyword.mode) { + WHOLE_WORD -> wholeWords.add(filterKeyword.keyword.keyword) + REGEX -> regexes.add(filterKeyword.keyword.keyword) + NONE -> words.add(filterKeyword.keyword.keyword) + } + } + + return Regex("") + } + + companion object { + fun isAllow(user: UserDetail, action: Action, resource: Filter): Boolean { + return when (action) { + SET_KEYWORDS -> resource.userDetailId == user.id + } + } + + enum class Action { + SET_KEYWORDS + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt new file mode 100644 index 00000000..ac3e8b88 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt @@ -0,0 +1,6 @@ +package dev.usbharu.hideout.core.domain.model.filter + +enum class FilterAction { + warn, + hide +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt new file mode 100644 index 00000000..418f9573 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt @@ -0,0 +1,9 @@ +package dev.usbharu.hideout.core.domain.model.filter + +enum class FilterContext { + home, + notifications, + public, + thread, + account +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterId.kt new file mode 100644 index 00000000..ce42a4c4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterId.kt @@ -0,0 +1,4 @@ +package dev.usbharu.hideout.core.domain.model.filter + +@JvmInline +value class FilterId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt new file mode 100644 index 00000000..5565c8e9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt @@ -0,0 +1,17 @@ +package dev.usbharu.hideout.core.domain.model.filter + +import dev.usbharu.hideout.core.domain.model.filter.FilterMode.* + +class FilterKeyword( + val id: FilterKeywordId, + var keyword: FilterKeywordKeyword, + val mode: FilterMode +) { + fun match(string: String): Boolean { + when (mode) { + WHOLE_WORD -> TODO() + REGEX -> TODO() + NONE -> TODO() + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordId.kt new file mode 100644 index 00000000..4f1b2df5 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordId.kt @@ -0,0 +1,4 @@ +package dev.usbharu.hideout.core.domain.model.filter + +@JvmInline +value class FilterKeywordId(val id: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordKeyword.kt new file mode 100644 index 00000000..89e959b3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeywordKeyword.kt @@ -0,0 +1,4 @@ +package dev.usbharu.hideout.core.domain.model.filter + +@JvmInline +value class FilterKeywordKeyword(val keyword: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt new file mode 100644 index 00000000..57e38fb7 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterMode.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.domain.model.filter + +enum class FilterMode { + WHOLE_WORD, + REGEX, + NONE +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterName.kt new file mode 100644 index 00000000..09e424de --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterName.kt @@ -0,0 +1,4 @@ +package dev.usbharu.hideout.core.domain.model.filter + +@JvmInline +value class FilterName(val name: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt new file mode 100644 index 00000000..17a4cacc --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt @@ -0,0 +1,6 @@ +package dev.usbharu.hideout.core.domain.model.filter + +interface FilterRepository { + suspend fun save(filter: Filter): Filter + suspend fun delete(filter: Filter) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt new file mode 100644 index 00000000..8466d9b4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt @@ -0,0 +1,4 @@ +package dev.usbharu.hideout.core.domain.model.filter + +class FilterResult(val filter: Filter, val matchedKeyword: FilterKeywordKeyword) { +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt new file mode 100644 index 00000000..4081c651 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.domain.model.filter + +import dev.usbharu.hideout.core.domain.model.post.Post + +class FilteredPost(val post: Post, val filterResults: List) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt new file mode 100644 index 00000000..47812ae6 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.core.domain.service.filter + +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.FilteredPost +import dev.usbharu.hideout.core.domain.model.post.Post + +interface IFilterDomainService { + fun apply(post: Post, context: FilterContext, filters: List): FilteredPost + fun applyAll(postList: List, context: FilterContext, filters: List): List +} + +class FilterDomainService : IFilterDomainService { + override fun apply(post: Post, context: FilterContext, filters: List): FilteredPost { + filters.filter { it.filterContext.contains(context) } + } + + override fun applyAll(postList: List, context: FilterContext, filters: List): List { + TODO("Not yet implemented") + } + +} \ No newline at end of file From 57d3091b18c21f7e5221bfeb5f0d02742296e120 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 10 Jun 2024 14:37:16 +0900 Subject: [PATCH 41/54] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A3=E3=83=AB?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=81=AE=E5=AE=9F=E8=A3=85=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/filter/Filter.kt | 4 +- .../core/domain/model/filter/FilterResult.kt | 2 +- .../service/filter/FilterDomainService.kt | 51 ++++++++++++++++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt index f1ecdd1a..cf679a1f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -34,7 +34,9 @@ class Filter( } } - return Regex("") + return (wholeWords + regexes + wholeWords) + .joinToString("|") + .toRegex() } companion object { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt index 8466d9b4..0878c0cd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt @@ -1,4 +1,4 @@ package dev.usbharu.hideout.core.domain.model.filter -class FilterResult(val filter: Filter, val matchedKeyword: FilterKeywordKeyword) { +class FilterResult(val filter: Filter, val matchedKeyword: String) { } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt index 47812ae6..3e685747 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt @@ -2,21 +2,68 @@ package dev.usbharu.hideout.core.domain.service.filter 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.FilterResult import dev.usbharu.hideout.core.domain.model.filter.FilteredPost import dev.usbharu.hideout.core.domain.model.post.Post +import org.springframework.stereotype.Service interface IFilterDomainService { fun apply(post: Post, context: FilterContext, filters: List): FilteredPost fun applyAll(postList: List, context: FilterContext, filters: List): List } +@Service class FilterDomainService : IFilterDomainService { override fun apply(post: Post, context: FilterContext, filters: List): FilteredPost { - filters.filter { it.filterContext.contains(context) } + val filterResults = filters + .filter { it.filterContext.contains(context) } + .flatMap { filter -> + val regex = filter.compileFilter() + post + .overview + ?.overview + ?.let { it1 -> regex.findAll(it1) } + .orEmpty() + .toList() + .map { FilterResult(filter, it.value) } + .plus( + post + .text + .let { regex.findAll(it) } + .toList() + .map { FilterResult(filter, it.value) } + ) + } + return FilteredPost(post, filterResults) } override fun applyAll(postList: List, context: FilterContext, filters: List): List { - TODO("Not yet implemented") + return filters + .filter { it.filterContext.contains(context) } + .map { it to it.compileFilter() } + .flatMap { compiledFilter -> + postList + .map { post -> + val filterResults = post + .overview + ?.overview + ?.let { overview -> compiledFilter.second.findAll(overview) } + .orEmpty() + .toList() + .map { FilterResult(compiledFilter.first, it.value) } + .plus( + post + .text + .let { compiledFilter.second.findAll(it) } + .toList() + .map { FilterResult(compiledFilter.first, it.value) } + ) + + post to filterResults + } + + } + .map { FilteredPost(it.first, it.second) } } } \ No newline at end of file From 1a074b87604c58f9127baad7f0729c5c091457c7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:14:01 +0900 Subject: [PATCH 42/54] =?UTF-8?q?hideout-worker=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hideout-worker/build.gradle.kts | 68 ----- .../gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - hideout-worker/gradlew | 249 ------------------ hideout-worker/gradlew.bat | 92 ------- hideout-worker/settings.gradle.kts | 18 -- .../dev/usbharu/hideout/HideoutWorker.kt | 29 -- .../usbharu/hideout/SpringTaskRunnerLoader.kt | 26 -- .../dev/usbharu/hideout/WorkerRunner.kt | 65 ----- .../hideout/worker/DeliverAcceptTaskRunner.kt | 45 ---- .../hideout/worker/DeliverCreateTaskRunner.kt | 43 --- .../hideout/worker/DeliverDeleteTaskRunner.kt | 37 --- .../worker/DeliverReactionTaskRunner.kt | 43 --- .../hideout/worker/DeliverRejectTaskRunner.kt | 42 --- .../hideout/worker/DeliverUndoTaskRunner.kt | 42 --- .../usbharu/hideout/worker/InboxTaskRunner.kt | 158 ----------- .../hideout/worker/ReceiveFollowTaskRunner.kt | 51 ---- .../hideout/worker/SpringConsumerConfig.kt | 28 -- .../hideout/worker/UpdateActorWorker.kt | 43 --- 19 files changed, 1086 deletions(-) delete mode 100644 hideout-worker/build.gradle.kts delete mode 100644 hideout-worker/gradle/wrapper/gradle-wrapper.jar delete mode 100644 hideout-worker/gradle/wrapper/gradle-wrapper.properties delete mode 100644 hideout-worker/gradlew delete mode 100644 hideout-worker/gradlew.bat delete mode 100644 hideout-worker/settings.gradle.kts delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/HideoutWorker.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/SpringTaskRunnerLoader.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/WorkerRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/SpringConsumerConfig.kt delete mode 100644 hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt diff --git a/hideout-worker/build.gradle.kts b/hideout-worker/build.gradle.kts deleted file mode 100644 index db74eb66..00000000 --- a/hideout-worker/build.gradle.kts +++ /dev/null @@ -1,68 +0,0 @@ -plugins { - alias(libs.plugins.kotlin.jvm) - alias(libs.plugins.kotlin.spring) - alias(libs.plugins.spring.boot) -} - -apply { - plugin("io.spring.dependency-management") -} - -group = "dev.usbharu" -version = "1.0-SNAPSHOT" - -repositories { - mavenCentral() - maven { - url = uri("https://git.usbharu.dev/api/packages/usbharu/maven") - } - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/usbharu/http-signature") - credentials { - - username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME") - password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN") - } - } - maven { - name = "GitHubPackages2" - url = uri("https://maven.pkg.github.com/multim-dev/emoji-kt") - credentials { - - username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME") - password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN") - } - } -} - -dependencies { - testImplementation(kotlin("test")) - implementation("dev.usbharu:owl-consumer:0.0.1") - implementation("dev.usbharu:owl-common:0.0.1") - implementation("dev.usbharu:owl-common-serialize-jackson:0.0.1") - implementation("dev.usbharu:hideout-core:0.0.1") - implementation("dev.usbharu:http-signature:1.0.0") - implementation("org.springframework.boot:spring-boot-starter") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.springframework.boot:spring-boot-starter-log4j2") - implementation(libs.jackson.databind) - implementation(libs.jackson.module.kotlin) - implementation(libs.bundles.coroutines) - - testImplementation("org.springframework.boot:spring-boot-starter-test") -} - -configurations { - all { - exclude("org.springframework.boot", "spring-boot-starter-logging") - exclude("ch.qos.logback", "logback-classic") - } -} - -tasks.test { - useJUnitPlatform() -} -kotlin { - jvmToolchain(21) -} \ No newline at end of file diff --git a/hideout-worker/gradle/wrapper/gradle-wrapper.jar b/hideout-worker/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e6441136f3d4ba8a0da8d277868979cfbc8ad796..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/hideout-worker/gradlew.bat b/hideout-worker/gradlew.bat deleted file mode 100644 index 7101f8e4..00000000 --- a/hideout-worker/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/hideout-worker/settings.gradle.kts b/hideout-worker/settings.gradle.kts deleted file mode 100644 index cf826ffd..00000000 --- a/hideout-worker/settings.gradle.kts +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" -} -rootProject.name = "hideout-worker" - -dependencyResolutionManagement { - repositories { - mavenCentral() - } - - versionCatalogs { - create("libs") { - from(files("../libs.versions.toml")) - } - } -} - -includeBuild("../hideout-core") \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/HideoutWorker.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/HideoutWorker.kt deleted file mode 100644 index 3ef74a43..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/HideoutWorker.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.context.properties.ConfigurationPropertiesScan -import org.springframework.boot.runApplication - -@SpringBootApplication -@ConfigurationPropertiesScan -class HideoutWorker - -fun main(args: Array) { - runApplication(*args) -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/SpringTaskRunnerLoader.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/SpringTaskRunnerLoader.kt deleted file mode 100644 index 6ff0b4a3..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/SpringTaskRunnerLoader.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout - -import dev.usbharu.owl.consumer.TaskRunner -import dev.usbharu.owl.consumer.TaskRunnerLoader -import org.springframework.stereotype.Component - -@Component -class SpringTaskRunnerLoader(private val taskRunners: List) : TaskRunnerLoader { - override fun load(): Map = taskRunners.associateBy { it.name } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/WorkerRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/WorkerRunner.kt deleted file mode 100644 index 47f0b98e..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/WorkerRunner.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout - -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.worker.SpringConsumerConfig -import dev.usbharu.owl.common.property.* -import dev.usbharu.owl.consumer.StandaloneConsumer -import dev.usbharu.owl.consumer.StandaloneConsumerConfig -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.ApplicationArguments -import org.springframework.boot.ApplicationRunner -import org.springframework.stereotype.Component - -@Component -class WorkerRunner( - private val springTaskRunnerLoader: SpringTaskRunnerLoader, - @Qualifier("activitypub") private val objectMapper: ObjectMapper, - private val springCConsumerConfig: SpringConsumerConfig, -) : ApplicationRunner { - override fun run(args: ApplicationArguments?) { - GlobalScope.launch(Dispatchers.Default) { - val consumer = StandaloneConsumer( - taskRunnerLoader = springTaskRunnerLoader, - propertySerializerFactory = CustomPropertySerializerFactory( - setOf( - IntegerPropertySerializer(), - StringPropertyValueSerializer(), - DoublePropertySerializer(), - BooleanPropertySerializer(), - LongPropertySerializer(), - FloatPropertySerializer(), - ObjectPropertySerializer(objectMapper), - ) - ), - config = StandaloneConsumerConfig( - springCConsumerConfig.address, - springCConsumerConfig.port, - springCConsumerConfig.name, - springCConsumerConfig.hostname, - springCConsumerConfig.concurrency - ) - ) - consumer.init() - consumer.start() - } - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt deleted file mode 100644 index 71817b59..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverAcceptTaskRunner.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.external.job.DeliverAcceptTask -import dev.usbharu.hideout.core.external.job.DeliverAcceptTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class DeliverAcceptTaskRunner( - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, - private val transaction: Transaction, -) : AbstractTaskRunner(DeliverAcceptTaskDef) { - override suspend fun typedRun(typedParam: DeliverAcceptTask, taskRequest: TaskRequest): TaskResult { - - transaction.transaction { - apRequestService.apPost( - typedParam.inbox, - typedParam.accept, - actorRepository.findById(typedParam.signer) - ) - } - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt deleted file mode 100644 index 0f71f98c..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverCreateTaskRunner.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.external.job.DeliverCreateTask -import dev.usbharu.hideout.core.external.job.DeliverCreateTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class DeliverCreateTaskRunner( - private val transaction: Transaction, - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, -) : AbstractTaskRunner(DeliverCreateTaskDef) { - override suspend fun typedRun(typedParam: DeliverCreateTask, taskRequest: TaskRequest): TaskResult { - transaction.transaction { - val signer = actorRepository.findByUrl(typedParam.actor) - - apRequestService.apPost(typedParam.inbox, typedParam.create, signer) - } - - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt deleted file mode 100644 index 9ce3dad7..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverDeleteTaskRunner.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.external.job.DeliverDeleteTask -import dev.usbharu.hideout.core.external.job.DeliverDeleteTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class DeliverDeleteTaskRunner( - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, -) : - AbstractTaskRunner(DeliverDeleteTaskDef) { - override suspend fun typedRun(typedParam: DeliverDeleteTask, taskRequest: TaskRequest): TaskResult { - apRequestService.apPost(typedParam.inbox, typedParam.delete, actorRepository.findById(typedParam.signer)) - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt deleted file mode 100644 index 4867056a..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverReactionTaskRunner.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.external.job.DeliverReactionTask -import dev.usbharu.hideout.core.external.job.DeliverReactionTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class DeliverReactionTaskRunner( - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, -) : AbstractTaskRunner(DeliverReactionTaskDef) { - override suspend fun typedRun(typedParam: DeliverReactionTask, taskRequest: TaskRequest): TaskResult { - val signer = actorRepository.findByUrl(typedParam.actor) - - apRequestService.apPost( - typedParam.inbox, - typedParam.like, - signer - ) - - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt deleted file mode 100644 index b84a5d7a..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverRejectTaskRunner.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.external.job.DeliverRejectTask -import dev.usbharu.hideout.core.external.job.DeliverRejectTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class DeliverRejectTaskRunner( - private val transaction: Transaction, - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, -) : AbstractTaskRunner(DeliverRejectTaskDef) { - override suspend fun typedRun(typedParam: DeliverRejectTask, taskRequest: TaskRequest): TaskResult { - val signer = transaction.transaction { - actorRepository.findById(typedParam.signer) - } - apRequestService.apPost(typedParam.inbox, typedParam.reject, signer) - - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt deleted file mode 100644 index 4c08786f..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/DeliverUndoTaskRunner.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.common.APRequestService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.external.job.DeliverUndoTask -import dev.usbharu.hideout.core.external.job.DeliverUndoTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class DeliverUndoTaskRunner( - private val transaction: Transaction, - private val apRequestService: APRequestService, - private val actorRepository: ActorRepository, -) : AbstractTaskRunner(DeliverUndoTaskDef) { - override suspend fun typedRun(typedParam: DeliverUndoTask, taskRequest: TaskRequest): TaskResult { - val signer = transaction.transaction { - actorRepository.findById(typedParam.signer) - } - apRequestService.apPost(typedParam.inbox, typedParam.undo, signer) - - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt deleted file mode 100644 index ae4af395..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/InboxTaskRunner.kt +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import com.fasterxml.jackson.core.JsonParseException -import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessContext -import dev.usbharu.hideout.activitypub.service.common.ActivityPubProcessor -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.external.job.InboxTask -import dev.usbharu.hideout.core.external.job.InboxTaskDef -import dev.usbharu.hideout.util.RsaUtil -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import dev.usbharu.httpsignature.common.PublicKey -import dev.usbharu.httpsignature.verify.HttpSignatureVerifier -import dev.usbharu.httpsignature.verify.Signature -import dev.usbharu.httpsignature.verify.SignatureHeaderParser -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Value -import org.springframework.stereotype.Component - -@Component -class InboxTaskRunner( - private val activityPubProcessorList: List>, - private val signatureHeaderParser: SignatureHeaderParser, - private val signatureVerifier: HttpSignatureVerifier, - private val apUserService: APUserService, - private val objectMapper: ObjectMapper, - private val transaction: Transaction, -) : AbstractTaskRunner(InboxTaskDef) { - - @Value("\${hideout.debug.trace-inbox:false}") - private var traceJson: Boolean = false - - override suspend fun typedRun(typedParam: InboxTask, taskRequest: TaskRequest): TaskResult { - val jsonNode = objectMapper.readTree(typedParam.json) - - logger.info("START Process inbox. type: {}", typedParam.type) - if (traceJson) { - logger.trace("type: {}\njson: \n{}", typedParam.type, jsonNode.toPrettyString()) - } - - val map = typedParam.headers - - val httpRequest = typedParam.httpRequest.copy(headers = HttpHeaders(map)) - - logger.trace("Request: {}\nheaders: {}", httpRequest, map) - - val signature = parseSignatureHeader(httpRequest.headers) - - logger.debug("Has signature? {}", signature != null) - - // todo ä¸æ­£ãªactorã‚’å–å¾—ã—ã¦ã—ã¾ã‚ãªã„よã†ã«ã™ã‚‹ - val verify = - signature?.let { - verifyHttpSignature( - httpRequest, - it, - transaction, - jsonNode.get("actor")?.asText() ?: signature.keyId - ) - } - ?: false - - logger.debug("Is verifying success? {}", verify) - - val activityPubProcessor = - activityPubProcessorList.firstOrNull { it.isSupported(typedParam.type) } as? ActivityPubProcessor - - if (activityPubProcessor == null) { - logger.warn("ActivityType {} is not support.", typedParam.type) - throw IllegalStateException("ActivityPubProcessor not found. type: ${typedParam.type}") - } - - val value = try { - objectMapper.treeToValue(jsonNode, activityPubProcessor.type()) - } catch (e: JsonParseException) { - logger.warn("Invalid JSON\n\n{}\n\n", jsonNode.toPrettyString()) - throw e - } - activityPubProcessor.process(ActivityPubProcessContext(value, jsonNode, httpRequest, signature, verify)) - - logger.info("SUCCESS Process inbox. type: {}", typedParam.type) - - - return TaskResult.ok() - } - - private suspend fun verifyHttpSignature( - httpRequest: HttpRequest, - signature: Signature, - transaction: Transaction, - actor: String, - ): Boolean { - val requiredHeaders = when (httpRequest.method) { - HttpMethod.GET -> getRequiredHeaders - HttpMethod.POST -> postRequiredHeaders - } - if (signature.headers.containsAll(requiredHeaders).not()) { - logger.warn("FAILED Invalid signature. require: {}", requiredHeaders) - return false - } - - val user = transaction.transaction { - apUserService.fetchPersonWithEntity(actor).second - } - - @Suppress("TooGenericExceptionCaught") - val verify = try { - signatureVerifier.verify( - httpRequest, - PublicKey(RsaUtil.decodeRsaPublicKeyPem(user.publicKey), signature.keyId) - ) - } catch (e: Exception) { - logger.warn("FAILED Verify Http Signature", e) - return false - } - - return verify.success - } - - @Suppress("TooGenericExceptionCaught") - private fun parseSignatureHeader(httpHeaders: HttpHeaders): Signature? { - return try { - println("Signature Header =" + httpHeaders.get("Signature").single()) - signatureHeaderParser.parse(httpHeaders) - } catch (e: RuntimeException) { - logger.trace("FAILED parse signature header", e) - null - } - } - - companion object { - private val logger = LoggerFactory.getLogger(InboxTaskRunner::class.java) - private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") - private val getRequiredHeaders = listOf("(request-target)", "date", "host") - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt deleted file mode 100644 index 72916733..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/ReceiveFollowTaskRunner.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.external.job.ReceiveFollowTask -import dev.usbharu.hideout.core.external.job.ReceiveFollowTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class ReceiveFollowTaskRunner( - private val transaction: Transaction, - private val apUserService: APUserService, - private val actorRepository: ActorRepository, - private val relationshipService: RelationshipService, -) : AbstractTaskRunner(ReceiveFollowTaskDef) { - override suspend fun typedRun(typedParam: ReceiveFollowTask, taskRequest: TaskRequest): TaskResult { - - transaction.transaction { - - apUserService.fetchPerson(typedParam.actor, typedParam.targetActor) - val targetEntity = actorRepository.findByUrl(typedParam.targetActor) ?: throw UserNotFoundException.withUrl( - typedParam.targetActor - ) - val followActorEntity = actorRepository.findByUrl(typedParam.follow.actor) - ?: throw UserNotFoundException.withUrl(typedParam.follow.actor) - relationshipService.followRequest(followActorEntity.id, targetEntity.id) - } - - return TaskResult.ok() - } -} \ No newline at end of file diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/SpringConsumerConfig.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/SpringConsumerConfig.kt deleted file mode 100644 index 2fa99d6b..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/SpringConsumerConfig.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import org.springframework.boot.context.properties.ConfigurationProperties - -@ConfigurationProperties("hideout.worker") -data class SpringConsumerConfig( - val address: String = "localhost", - val port: Int = 50051, - val name: String = "hideout-worker", - val hostname: String = "localhost", - val concurrency: Int = 10, -) diff --git a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt b/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt deleted file mode 100644 index e7de84b0..00000000 --- a/hideout-worker/src/main/kotlin/dev/usbharu/hideout/worker/UpdateActorWorker.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.worker - -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.external.job.UpdateActorTask -import dev.usbharu.hideout.core.external.job.UpdateActorTaskDef -import dev.usbharu.owl.consumer.AbstractTaskRunner -import dev.usbharu.owl.consumer.TaskRequest -import dev.usbharu.owl.consumer.TaskResult -import org.springframework.stereotype.Component - -@Component -class UpdateActorWorker( - private val transaction: Transaction, - private val apUserService: APUserService, - private val postService: PostService, -) : AbstractTaskRunner(UpdateActorTaskDef) { - override suspend fun typedRun(typedParam: UpdateActorTask, taskRequest: TaskRequest): TaskResult { - transaction.transaction { - apUserService.fetchPerson(typedParam.apId, idOverride = typedParam.id) - - postService.restoreByRemoteActor(typedParam.id) - } - - return TaskResult.ok() - } -} \ No newline at end of file From 5ab62d1250e584a5f9f64527018b257499ad0a12 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:31:47 +0900 Subject: [PATCH 43/54] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/mastodon/account/AccountApiPaginationTest.kt | 4 ++++ .../core/application/shared/AbstractApplicationService.kt | 2 +- .../hideout/core/domain/model/filter/FilterResult.kt | 3 +-- .../hideout/core/interfaces/api/auth/AuthController.kt | 4 +--- hideout-core/src/main/resources/application.yml | 1 + .../core/domain/model/actor/ActorDescriptionTest.kt | 4 +--- .../dev/usbharu/hideout/core/domain/model/post/PostTest.kt | 2 +- .../hideout/mastodon/interfaces/api/SpringTimelineApi.kt | 7 +------ hideout-mastodon/src/main/resources/openapi/mastodon.yaml | 2 +- 9 files changed, 12 insertions(+), 17 deletions(-) diff --git a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt index 81971e7a..baf0a42a 100644 --- a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt +++ b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("SpringJavaInjectionPointsAutowiringInspection") + package mastodon.account import com.fasterxml.jackson.core.type.TypeReference @@ -41,6 +43,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.transaction.annotation.Transactional import org.springframework.web.context.WebApplicationContext +@Suppress("NonAsciiCharacters") @SpringBootTest(classes = [SpringApplication::class]) @AutoConfigureMockMvc @Transactional @@ -48,6 +51,7 @@ import org.springframework.web.context.WebApplicationContext @Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) @Sql("/sql/accounts/test-accounts-statuses.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) class AccountApiPaginationTest { + @Suppress("SpringJavaInjectionPointsAutowiringInspection") @Autowired private lateinit var context: WebApplicationContext diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt index 9e34cc91..79c0f352 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt @@ -25,7 +25,7 @@ abstract class AbstractApplicationService( ) : ApplicationService { override suspend fun execute(command: T, executor: CommandExecutor): R { return try { - logger.debug("START ${command::class.simpleName} by $executor") + logger.debug("START {} by {}", command::class.simpleName, executor) val response = transaction.transaction { internalExecute(command, executor) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt index 0878c0cd..b291e6bd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt @@ -1,4 +1,3 @@ package dev.usbharu.hideout.core.domain.model.filter -class FilterResult(val filter: Filter, val matchedKeyword: String) { -} \ No newline at end of file +class FilterResult(val filter: Filter, val matchedKeyword: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 761dacde..39e8d6c8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -32,9 +32,7 @@ class AuthController( private val springMvcCommandExecutorFactory: SpringMvcCommandExecutorFactory, ) { @GetMapping("/auth/sign_up") - fun signUp(): String { - return "sign_up" - } + fun signUp(): String = "sign_up" @PostMapping("/auth/sign_up") suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String { diff --git a/hideout-core/src/main/resources/application.yml b/hideout-core/src/main/resources/application.yml index e5c023ec..55cec25b 100644 --- a/hideout-core/src/main/resources/application.yml +++ b/hideout-core/src/main/resources/application.yml @@ -1,3 +1,4 @@ +#file: noinspection SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection,SpellCheckingInspection hideout: url: "https://test-hideout-dev.usbharu.dev" use-mongodb: true diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt index 90670a45..acfa8b11 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescriptionTest.kt @@ -1,5 +1,3 @@ package dev.usbharu.hideout.core.domain.model.actor -class ActorDescriptionTest { - -} \ No newline at end of file +class ActorDescriptionTest \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index 5c4f0226..65ab6cbb 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -193,7 +193,7 @@ class PostTest { } @Test - fun hideãŒtrueã®ã¨ãcontetnãŒemptyã‚’è¿”ã™() { + fun hideãŒtrueã®ã¨ãcontentãŒemptyã‚’è¿”ã™() { val post = TestPostFactory.create(hide = true) assertEquals(PostContent.empty, post.content) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt index 7220865f..2f631d70 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt @@ -17,12 +17,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.mastodon.interfaces.api.generated.TimelineApi -import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status -import kotlinx.coroutines.flow.Flow -import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @Controller -class SpringTimelineApi : TimelineApi { - -} \ No newline at end of file +class SpringTimelineApi : TimelineApi \ No newline at end of file diff --git a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml index 91c75422..bc5195d5 100644 --- a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml +++ b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml @@ -1540,7 +1540,7 @@ components: type: boolean moved: type: boolean - suspendex: + suspended: type: boolean limited: type: boolean From ea666cf0f8f171a299efff6475da89befccd5e3d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:38:48 +0900 Subject: [PATCH 44/54] style: fix lint --- .../actor/GetUserDetailApplicationService.kt | 2 +- .../application/RegisterApplication.kt | 2 +- .../RegisterApplicationApplicationService.kt | 5 +---- .../InitLocalInstanceApplicationService.kt | 2 +- .../post/GetPostApplicationService.kt | 2 +- .../post/RegisterLocalPostApplicationService.kt | 6 ++++-- ...UserAcceptFollowRequestApplicationService.kt | 2 +- .../block/UserBlockApplicationService.kt | 3 +-- .../UserFollowRequestApplicationService.kt | 5 +++-- .../get/GetRelationshipApplicationService.kt | 17 +++++++++++------ .../mute/UserMuteApplicationService.kt | 2 +- ...UserRejectFollowRequestApplicationService.kt | 2 +- ...UserRemoveFromFollowersApplicationService.kt | 2 +- .../unblock/UserUnblockApplicationService.kt | 2 +- .../unfollow/UserUnfollowApplicationService.kt | 2 +- .../unmute/UserUnmuteApplicationService.kt | 2 +- .../shared/AbstractApplicationService.kt | 3 +-- .../application/shared/ApplicationService.kt | 2 +- .../core/application/shared/CommandExecutor.kt | 2 +- .../hideout/core/config/SecurityConfig.kt | 3 +-- .../hideout/core/config/SpringMvcConfig.kt | 2 +- .../core/domain/model/actor/ActorDescription.kt | 1 - .../core/domain/model/actor/ActorScreenName.kt | 1 - .../hideout/core/domain/model/actor/Role.kt | 2 +- .../domain/model/application/Application.kt | 2 +- .../model/application/ApplicationRepository.kt | 2 +- .../hideout/core/domain/model/filter/Filter.kt | 2 +- .../core/domain/model/filter/FilterKeyword.kt | 2 +- .../domain/model/filter/FilterRepository.kt | 2 +- .../core/domain/model/filter/FilterResult.kt | 2 +- .../core/domain/model/filter/FilteredPost.kt | 2 +- .../hideout/core/domain/model/post/Post.kt | 7 ++----- .../core/domain/model/post/PostContent.kt | 1 - .../domain/model/relationship/Relationship.kt | 1 - .../actor/local/LocalActorDomainServiceImpl.kt | 2 +- ...LocalActorMigrationCheckDomainServiceImpl.kt | 2 +- .../service/filter/FilterDomainService.kt | 4 +--- .../service/post/DefaultPostContentFormatter.kt | 2 +- .../domain/service/post/PostContentFormatter.kt | 3 +-- .../relationship/RelationshipDomainService.kt | 2 +- .../infrastructure/exposed/PostQueryMapper.kt | 3 +-- .../exposed/PostResultRowMapper.kt | 2 +- ...xposedActorInstanceRelationshipRepository.kt | 6 +++--- .../ExposedApplicationRepository.kt | 4 +--- .../ExposedRelationshipRepository.kt | 6 +++--- .../DelegateCommandExecutorFactory.kt | 2 +- .../springframework/HttpCommandExecutor.kt | 2 +- .../SpringMvcCommandExecutorFactory.kt | 3 +-- .../SpringSecurityPasswordEncoder.kt | 6 ++++-- .../HideoutJdbcOauth2AuthorizationService.kt | 8 ++++---- .../oauth2/HideoutUserDetails.kt | 2 +- .../oauth2/Oauth2CommandExecutor.kt | 5 +++-- .../oauth2/Oauth2CommandExecutorFactory.kt | 3 +-- .../oauth2/UserDetailsServiceImpl.kt | 3 +-- .../generate/JsonOrFormModelMethodProcessor.kt | 2 -- .../kotlin/dev/usbharu/hideout/util/RsaUtil.kt | 1 - settings.gradle.kts | 1 - 57 files changed, 78 insertions(+), 93 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt index 8e7f8eb9..e3b3b3d8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt @@ -46,4 +46,4 @@ class GetUserDetailApplicationService( return UserDetail.of(actor, userDetail, emojis) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt index b1351109..f39b957f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplication.kt @@ -23,4 +23,4 @@ data class RegisterApplication( val redirectUris: Set, val useRefreshToken: Boolean, val scopes: Set, -) \ No newline at end of file +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt index d287b28a..104cd3e0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt @@ -43,9 +43,7 @@ class RegisterApplicationApplicationService( private val applicationRepository: ApplicationRepository, ) { suspend fun register(registerApplication: RegisterApplication): RegisteredApplication { - return transaction.transaction { - val id = idGenerateService.generateId() val clientSecret = secureTokenGenerator.generate() val registeredClient = RegisteredClient @@ -89,6 +87,5 @@ class RegisterApplicationApplicationService( redirectUris = registerApplication.redirectUris ) } - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt index d2123c5d..0a8a6e98 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt @@ -56,4 +56,4 @@ class InitLocalInstanceApplicationService( instanceRepository.save(instance) } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt index 6c8bf98b..e1d480f6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt @@ -37,4 +37,4 @@ class GetPostApplicationService(private val postRepository: PostRepository, tran companion object { private val logger = LoggerFactory.getLogger(GetPostApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index 88a6e869..181951b6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -44,8 +44,10 @@ class RegisterLocalPostApplicationService( } override suspend fun internalExecute(command: RegisterLocalPost, executor: CommandExecutor): Long { - val actorId = (userDetailRepository.findById(command.userDetailId) - ?: throw IllegalStateException("actor not found")).actorId + val actorId = ( + userDetailRepository.findById(command.userDetailId) + ?: throw IllegalStateException("actor not found") + ).actorId val actor = actorRepository.findById(actorId)!! diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt index 3a89b565..1d4a3569 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt @@ -55,4 +55,4 @@ class UserAcceptFollowRequestApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt index eff97a4f..e6d7e416 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt @@ -62,8 +62,7 @@ class UserBlockApplicationService( relationshipDomainService.block(relationship, inverseRelationship) - relationshipRepository.save(relationship) relationshipRepository.save(inverseRelationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt index 1ca218e2..52204beb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt @@ -35,7 +35,8 @@ class UserFollowRequestApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : AbstractApplicationService( - transaction, logger + transaction, + logger ) { override suspend fun internalExecute(command: FollowRequest, executor: CommandExecutor) { @@ -58,4 +59,4 @@ class UserFollowRequestApplicationService( companion object { private val logger = LoggerFactory.getLogger(UserFollowRequestApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt index 3b865cf2..809f15b2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt @@ -38,7 +38,8 @@ class GetRelationshipApplicationService( transaction: Transaction, ) : AbstractApplicationService( - transaction, logger + transaction, + logger ) { companion object { private val logger = LoggerFactory.getLogger(GetRelationshipApplicationService::class.java) @@ -50,11 +51,15 @@ class GetRelationshipApplicationService( val actor = actorRepository.findById(userDetail.actorId)!! 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)) + val relationship = ( + 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)) + val relationship1 = ( + 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) @@ -65,4 +70,4 @@ class GetRelationshipApplicationService( return Relationship.of(relationship, relationship1, actorInstanceRelationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt index 6d39c013..c3cafb42 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt @@ -57,4 +57,4 @@ class UserMuteApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt index 703eb6ad..dd597e39 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt @@ -55,4 +55,4 @@ class UserRejectFollowRequestApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt index c4f2e544..122e6a59 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt @@ -57,4 +57,4 @@ class UserRemoveFromFollowersApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt index a7f50705..9de1ea7a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt @@ -57,4 +57,4 @@ class UserUnblockApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt index 5f00f000..4228b293 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt @@ -57,4 +57,4 @@ class UserUnfollowApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt index 5b224e7f..a7e5ae21 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt @@ -57,4 +57,4 @@ class UserUnmuteApplicationService( relationshipRepository.save(relationship) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt index 79c0f352..720585dd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/AbstractApplicationService.kt @@ -38,8 +38,7 @@ abstract class AbstractApplicationService( logger.warn("Command execution error", e) throw e } - } protected abstract suspend fun internalExecute(command: T, executor: CommandExecutor): R -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt index 86f66991..ad729fad 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/ApplicationService.kt @@ -18,4 +18,4 @@ package dev.usbharu.hideout.core.application.shared interface ApplicationService { suspend fun execute(command: T, executor: CommandExecutor): R -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt index 9d530133..460d76cf 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt @@ -22,4 +22,4 @@ interface CommandExecutor { interface UserDetailGettableCommandExecutor : CommandExecutor { val userDetailId: Long -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt index f4aed6c5..f32b4d9d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt @@ -87,7 +87,6 @@ class SecurityConfig { authorize(anyRequest, authenticated) } formLogin { - } } return http.build() @@ -195,4 +194,4 @@ class SecurityConfig { return roleHierarchyImpl } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt index 00b6697d..ee75a0ef 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SpringMvcConfig.kt @@ -43,4 +43,4 @@ class JsonOrFormModelMethodProcessorConfig { ) ) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt index 7bc487a1..740e34d5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorDescription.kt @@ -16,7 +16,6 @@ package dev.usbharu.hideout.core.domain.model.actor - class ActorDescription(description: String) { val description: String = description.take(length) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt index 4f7a8aed..30b4aff3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorScreenName.kt @@ -16,7 +16,6 @@ package dev.usbharu.hideout.core.domain.model.actor - class ActorScreenName(screenName: String) { val screenName: String = screenName.take(length) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt index ee12ee08..39c3acd4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Role.kt @@ -18,4 +18,4 @@ package dev.usbharu.hideout.core.domain.model.actor enum class Role { LOCAL, MODERATOR, ADMINISTRATOR, REMOTE -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt index 8cee8422..968bb90e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/Application.kt @@ -19,4 +19,4 @@ package dev.usbharu.hideout.core.domain.model.application class Application( val applicationId: ApplicationId, val name: ApplicationName, -) \ No newline at end of file +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt index 22d41d8f..0150e266 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/application/ApplicationRepository.kt @@ -19,4 +19,4 @@ package dev.usbharu.hideout.core.domain.model.application interface ApplicationRepository { suspend fun save(application: Application): Application suspend fun delete(application: Application) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt index cf679a1f..9135e519 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -50,4 +50,4 @@ class Filter( SET_KEYWORDS } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt index 5565c8e9..0e8774ba 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterKeyword.kt @@ -14,4 +14,4 @@ class FilterKeyword( NONE -> TODO() } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt index 17a4cacc..990472f1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt @@ -3,4 +3,4 @@ package dev.usbharu.hideout.core.domain.model.filter interface FilterRepository { suspend fun save(filter: Filter): Filter suspend fun delete(filter: Filter) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt index b291e6bd..478f05e5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterResult.kt @@ -1,3 +1,3 @@ package dev.usbharu.hideout.core.domain.model.filter -class FilterResult(val filter: Filter, val matchedKeyword: String) \ No newline at end of file +class FilterResult(val filter: Filter, val matchedKeyword: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt index 4081c651..80226401 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilteredPost.kt @@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.model.filter import dev.usbharu.hideout.core.domain.model.post.Post -class FilteredPost(val post: Post, val filterResults: List) \ No newline at end of file +class FilteredPost(val post: Post, val filterResults: List) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index 9f932dbf..db7f1693 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -59,7 +59,6 @@ class Post( private set fun setVisibility(visibility: Visibility, actor: Actor) { - require(isAllow(actor, UPDATE, this)) require(this.visibility != Visibility.DIRECT) require(visibility != Visibility.DIRECT) @@ -77,7 +76,6 @@ class Post( private set fun setVisibleActors(visibleActors: Set, actor: Actor) { - require(isAllow(actor, UPDATE, this)) require(deleted.not()) if (visibility == Visibility.DIRECT) { @@ -266,7 +264,6 @@ class Post( moveTo: PostId? = null, actor: Actor, ): Post { - require(actor.deleted.not()) require(actor.moveTo == null) @@ -310,10 +307,10 @@ class Post( } MOVE -> resource.actorId == actor.id && actor.deleted.not() - DELETE -> resource.actorId == actor.id || + DELETE -> + resource.actorId == actor.id || actor.roles.contains(Role.ADMINISTRATOR) || actor.roles.contains(Role.MODERATOR) - } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index 38e4052d..d8e0a18d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -25,5 +25,4 @@ data class PostContent(val text: String, val content: String, val emojiIds: List val contentLength = 5000 val textLength = 3000 } - } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt index 930670a9..e4893712 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt @@ -121,7 +121,6 @@ class Relationship( return result } - companion object { fun default(actorId: ActorId, targetActorId: ActorId): Relationship = Relationship( actorId = actorId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt index ef77d51b..6f0dd6de 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImpl.kt @@ -38,4 +38,4 @@ class LocalActorDomainServiceImpl( return ActorPublicKey.create(generateKeyPair.public) to ActorPrivateKey.create(generateKeyPair.private) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt index e2b2f621..f74bf530 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt @@ -40,4 +40,4 @@ class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomain return AccountMigrationCheck.CanAccountMigration() } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt index 3e685747..97ec2cfb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/filter/FilterDomainService.kt @@ -61,9 +61,7 @@ class FilterDomainService : IFilterDomainService { post to filterResults } - } .map { FilteredPost(it.first, it.second) } } - -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt index 9054f12b..b1b0f86e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt @@ -96,4 +96,4 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po } } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt index a0004b39..e74b5f64 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/PostContentFormatter.kt @@ -16,7 +16,6 @@ package dev.usbharu.hideout.core.domain.service.post - interface PostContentFormatter { fun format(content: String): FormattedPostContent } @@ -24,4 +23,4 @@ interface PostContentFormatter { data class FormattedPostContent( val html: String, val content: String, -) \ No newline at end of file +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt index 2c81724c..b59c0319 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/relationship/RelationshipDomainService.kt @@ -30,4 +30,4 @@ class RelationshipDomainService { inverseRelationship.unfollow() inverseRelationship.unfollowRequest() } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt index 47d72f3c..1ea7e1b8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostQueryMapper.kt @@ -57,8 +57,7 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper) : ?.let { actorId -> ActorId(actorId) } }.toSet() ) - } } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt index 1076477c..0fb4c803 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/PostResultRowMapper.kt @@ -45,4 +45,4 @@ class PostResultRowMapper : ResultRowMapper { moveTo = resultRow[Posts.moveTo]?.let { PostId(it) } ) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt index 9cc1cfd4..d32a3c59 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt @@ -28,10 +28,10 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository - @Repository class ExposedActorInstanceRelationshipRepository(override val domainEventPublisher: DomainEventPublisher) : - ActorInstanceRelationshipRepository, AbstractRepository(), + ActorInstanceRelationshipRepository, + AbstractRepository(), DomainEventPublishableRepository { override suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship { query { @@ -95,4 +95,4 @@ object ActorInstanceRelationships : Table("actor_instance_relationships") { val doNotSendPrivate = bool("do_not_send_private") override val primaryKey: PrimaryKey = PrimaryKey(actorId, instanceId) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt index 7c04f9dc..f2d2552e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt @@ -43,15 +43,13 @@ class ExposedApplicationRepository : ApplicationRepository, AbstractRepository() override val logger: Logger get() = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedApplicationRepository::class.java) } } - object Applications : Table("applications") { val id = long("id") val name = varchar("name", 500) override val primaryKey: PrimaryKey = PrimaryKey(id) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt index eef233b0..e44b286f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt @@ -28,7 +28,8 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedRelationshipRepository(override val domainEventPublisher: DomainEventPublisher) : RelationshipRepository, +class ExposedRelationshipRepository(override val domainEventPublisher: DomainEventPublisher) : + RelationshipRepository, AbstractRepository(), DomainEventPublishableRepository { override suspend fun save(relationship: Relationship): Relationship { @@ -65,7 +66,6 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve override val logger: Logger get() = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedRelationshipRepository::class.java) } @@ -91,4 +91,4 @@ object Relationships : Table("relationships") { val mutingFollowRequest = bool("muting_follow_request") override val primaryKey: PrimaryKey = PrimaryKey(actorId, targetActorId) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt index 6312b05b..cedebc83 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt @@ -33,4 +33,4 @@ class DelegateCommandExecutorFactory( } return mvcCommandExecutorFactory.getCommandExecutor() } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt index fe315bf4..2a04b380 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt @@ -26,4 +26,4 @@ open class HttpCommandExecutor( override fun toString(): String { return "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')" } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt index 63617376..7a9b5940 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt @@ -21,7 +21,6 @@ import org.springframework.stereotype.Component import org.springframework.web.context.request.RequestContextHolder import org.springframework.web.context.request.ServletRequestAttributes - @Component class SpringMvcCommandExecutorFactory { fun getCommandExecutor(): HttpCommandExecutor { @@ -29,4 +28,4 @@ class SpringMvcCommandExecutorFactory { val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request return HttpCommandExecutor(name, request.remoteAddr, request.getHeader("user-agent").orEmpty()) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt index 1f8ae461..51ab4a81 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt @@ -20,9 +20,11 @@ import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder import org.springframework.stereotype.Component @Component -class SpringSecurityPasswordEncoder(private val passwordEncoder: org.springframework.security.crypto.password.PasswordEncoder) : +class SpringSecurityPasswordEncoder( + private val passwordEncoder: org.springframework.security.crypto.password.PasswordEncoder, +) : PasswordEncoder { override suspend fun encode(input: String): String { return passwordEncoder.encode(input) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt index 20c65b68..2044962e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutJdbcOauth2AuthorizationService.kt @@ -31,10 +31,10 @@ class HideoutJdbcOauth2AuthorizationService( @Autowired(required = false) lobHandler: LobHandler = DefaultLobHandler(), ) : JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository, lobHandler) { - - init { - super.setAuthorizationRowMapper(HideoutOAuth2AuthorizationRowMapper(registeredClientRepository = registeredClientRepository)) + super.setAuthorizationRowMapper( + HideoutOAuth2AuthorizationRowMapper(registeredClientRepository = registeredClientRepository) + ) } class HideoutOAuth2AuthorizationRowMapper(registeredClientRepository: RegisteredClientRepository?) : @@ -43,4 +43,4 @@ class HideoutJdbcOauth2AuthorizationService( objectMapper.addMixIn(HideoutUserDetails::class.java, UserDetailsMixin::class.java) } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt index a85b948b..0b2f61d4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt @@ -126,4 +126,4 @@ class UserDetailsDeserializer : JsonDeserializer() { companion object { private val SIMPLE_GRANTED_AUTHORITY_SET = object : TypeReference>() {} } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt index 3dd05d31..9cf58a40 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt @@ -19,5 +19,6 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 import dev.usbharu.hideout.core.application.shared.CommandExecutor import dev.usbharu.hideout.core.application.shared.UserDetailGettableCommandExecutor -class Oauth2CommandExecutor(override val executor: String, override val userDetailId: Long) : CommandExecutor, - UserDetailGettableCommandExecutor \ No newline at end of file +class Oauth2CommandExecutor(override val executor: String, override val userDetailId: Long) : + CommandExecutor, + UserDetailGettableCommandExecutor diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt index 6dee87f0..1416a2a3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt @@ -29,6 +29,5 @@ class Oauth2CommandExecutorFactory { principal.subject, principal.getClaim("uid").toLong() ) - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt index 505be86c..5ca4964e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImpl.kt @@ -49,6 +49,5 @@ class UserDetailsServiceImpl( userDetailsId = userDetail.id.id ) } - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt index 54c9b107..d7a97736 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/generate/JsonOrFormModelMethodProcessor.kt @@ -43,8 +43,6 @@ class JsonOrFormModelMethodProcessor( webRequest: NativeWebRequest, binderFactory: WebDataBinderFactory?, ): Any? { - - val contentType = webRequest.getHeader("Content-Type").orEmpty() logger.trace("ContentType is {}", contentType) if (contentType.contains(isJsonRegex)) { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt index 8efbd8b0..56b20ac3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt @@ -44,5 +44,4 @@ object RsaUtil { } fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded)) - } diff --git a/settings.gradle.kts b/settings.gradle.kts index 65982597..8c6faaff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,6 @@ plugins { rootProject.name = "hideout" includeBuild("hideout-core") -includeBuild("hideout-worker") includeBuild("hideout-mastodon") dependencyResolutionManagement { From 078547d5ffd61258640c8715530164d2b6e5db70 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:23:35 +0900 Subject: [PATCH 45/54] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A3=E3=83=AB?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=81=AE=E8=BF=BD=E5=8A=A0=E3=81=A8=E5=89=8A?= =?UTF-8?q?=E9=99=A4=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/application/filter/DeleteFilter.kt | 19 +++ .../hideout/core/application/filter/Filter.kt | 49 ++++++ .../core/application/filter/FilterKeyword.kt | 25 +++ .../core/application/filter/GetFilter.kt | 19 +++ .../core/application/filter/RegisterFilter.kt | 27 ++++ .../filter/RegisterFilterKeyword.kt | 24 +++ .../UserDeleteFilterApplicationService.kt | 40 +++++ .../filter/UserGetFilterApplicationService.kt | 41 +++++ .../UserRegisterFilterApplicationService.kt | 67 ++++++++ .../core/domain/model/filter/Filter.kt | 33 +++- .../domain/model/filter/FilterRepository.kt | 3 + .../exposed/FilterQueryMapper.kt | 50 ++++++ .../exposed/FilterResultRowMapper.kt | 35 +++++ .../ExposedFilterRepository.kt | 98 ++++++++++++ .../application/filter/DeleteFilterV1.kt | 19 +++ .../DeleteFilterV1ApplicationService.kt | 41 +++++ .../application/filter/GetFilterV1.kt | 19 +++ .../filter/GetFilterV1ApplicationService.kt | 61 ++++++++ .../interfaces/api/SpringFilterApi.kt | 144 ++++++++++++++++-- 19 files changed, 802 insertions(+), 12 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/DeleteFilter.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/FilterKeyword.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/GetFilter.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilterKeyword.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1ApplicationService.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/DeleteFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/DeleteFilter.kt new file mode 100644 index 00000000..52a2bf0a --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/DeleteFilter.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +data class DeleteFilter(val filterId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt new file mode 100644 index 00000000..0231663e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.domain.model.filter.Filter +import dev.usbharu.hideout.core.domain.model.filter.FilterAction +import dev.usbharu.hideout.core.domain.model.filter.FilterContext + +data class Filter( + val filterId: Long, + val userDetailId: Long, + val name: String, + val filterContext: Set, + val filterAction: FilterAction, + val filterKeywords: Set, +) { + companion object { + fun of(filter: Filter): dev.usbharu.hideout.core.application.filter.Filter { + return Filter( + filterId = filter.id.id, + userDetailId = filter.userDetailId.id, + name = filter.name.name, + filterContext = filter.filterContext, + filterAction = filter.filterAction, + filterKeywords = filter.filterKeywords.map { + FilterKeyword( + it.id.id, + it.keyword.keyword, + it.mode + ) + }.toSet() + ) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/FilterKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/FilterKeyword.kt new file mode 100644 index 00000000..bb58f6e6 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/FilterKeyword.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.domain.model.filter.FilterMode + +data class FilterKeyword( + val id: Long, + val keyword: String, + val filterMode: FilterMode, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/GetFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/GetFilter.kt new file mode 100644 index 00000000..b9089ab0 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/GetFilter.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +data class GetFilter(val filterId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt new file mode 100644 index 00000000..3fd9a35f --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.domain.model.filter.FilterAction +import dev.usbharu.hideout.core.domain.model.filter.FilterContext + +data class RegisterFilter( + val filterName: String, + val filterContext: Set, + val filterAction: FilterAction, + val filterKeywords: Set, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilterKeyword.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilterKeyword.kt new file mode 100644 index 00000000..b6e3ed31 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilterKeyword.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.domain.model.filter.FilterMode + +data class RegisterFilterKeyword( + val keyword: String, + val filterMode: FilterMode, +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt new file mode 100644 index 00000000..3b032fc4 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +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.filter.FilterId +import dev.usbharu.hideout.core.domain.model.filter.FilterRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserDeleteFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : + AbstractApplicationService( + transaction, logger + ) { + companion object { + private val logger = LoggerFactory.getLogger(UserDeleteFilterApplicationService::class.java) + } + + override suspend fun internalExecute(command: DeleteFilter, executor: CommandExecutor) { + val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("not found") + filterRepository.delete(filter) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt new file mode 100644 index 00000000..bf10cc6b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +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.filter.FilterId +import dev.usbharu.hideout.core.domain.model.filter.FilterRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserGetFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : + AbstractApplicationService( + transaction, logger + ) { + override suspend fun internalExecute(command: GetFilter, executor: CommandExecutor): Filter { + val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("Not Found") + + return Filter.of(filter) + } + + companion object { + private val logger = LoggerFactory.getLogger(UserGetFilterApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt new file mode 100644 index 00000000..a1dc48a3 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.filter + +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.application.shared.UserDetailGettableCommandExecutor +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class UserRegisterFilterApplicationService( + private val idGenerateService: IdGenerateService, + private val filterRepository: FilterRepository, + transaction: Transaction, +) : + AbstractApplicationService( + transaction, logger + ) { + + companion object { + private val logger = LoggerFactory.getLogger(UserRegisterFilterApplicationService::class.java) + } + + override suspend fun internalExecute(command: RegisterFilter, executor: CommandExecutor): Filter { + require(executor is UserDetailGettableCommandExecutor) + + + val filter = dev.usbharu.hideout.core.domain.model.filter.Filter.create( + FilterId(idGenerateService.generateId()), + UserDetailId(executor.userDetailId), + FilterName(command.filterName), + command.filterContext, + command.filterAction, + command.filterKeywords + .map { + FilterKeyword( + FilterKeywordId(idGenerateService.generateId()), + FilterKeywordKeyword(it.keyword), + it.filterMode + ) + }.toSet() + ) + + filterRepository.save(filter) + return Filter.of(filter) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt index 9135e519..e174ec3b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -9,9 +9,9 @@ class Filter( val id: FilterId, val userDetailId: UserDetailId, var name: FilterName, - val filterContext: List, + val filterContext: Set, val filterAction: FilterAction, - filterKeywords: Set + filterKeywords: Set, ) { var filterKeywords = filterKeywords private set @@ -39,6 +39,17 @@ class Filter( .toRegex() } + fun reconstructWith(filterKeywords: Set): Filter { + return Filter( + this.id, + this.userDetailId, + this.name, + this.filterContext, + this.filterAction, + filterKeywords + ) + } + companion object { fun isAllow(user: UserDetail, action: Action, resource: Filter): Boolean { return when (action) { @@ -49,5 +60,23 @@ class Filter( enum class Action { SET_KEYWORDS } + + fun create( + id: FilterId, + userDetailId: UserDetailId, + name: FilterName, + filterContext: Set, + filterAction: FilterAction, + filterKeywords: Set, + ): Filter { + return Filter( + id, + userDetailId, + name, + filterContext, + filterAction, + filterKeywords + ) + } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt index 990472f1..88ff3feb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterRepository.kt @@ -3,4 +3,7 @@ package dev.usbharu.hideout.core.domain.model.filter 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? } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt new file mode 100644 index 00000000..9fe7081d --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposed + +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.infrastructure.exposedrepository.FilterKeywords +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Filters +import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component + +@Component +class FilterQueryMapper(private val filterResultRowMapper: ResultRowMapper) : QueryMapper { + override fun map(query: Query): List { + return query + .groupBy { it[Filters.id] } + .map { it.value } + .map { + it + .first() + .let(filterResultRowMapper::map) + .apply { + reconstructWith(it.mapNotNull { resultRow: ResultRow -> + FilterKeyword( + FilterKeywordId(resultRow.getOrNull(FilterKeywords.id) ?: return@mapNotNull null), + FilterKeywordKeyword( + resultRow.getOrNull(FilterKeywords.keyword) ?: return@mapNotNull null + ), + FilterMode.valueOf(resultRow.getOrNull(FilterKeywords.mode) ?: return@mapNotNull null) + ) + + }.toSet()) + } + } + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt new file mode 100644 index 00000000..13221bd7 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposed + +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Filters +import org.jetbrains.exposed.sql.ResultRow +import org.springframework.stereotype.Component + +@Component +class FilterResultRowMapper : ResultRowMapper { + override fun map(resultRow: ResultRow): Filter = Filter( + FilterId(resultRow[Filters.id]), + UserDetailId(resultRow[Filters.userId]), + FilterName(resultRow[Filters.name]), + resultRow[Filters.context].split(",").filter { it.isNotEmpty() }.map { FilterContext.valueOf(it) }.toSet(), + FilterAction.valueOf(resultRow[Filters.filterAction]), + emptySet() + ) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt new file mode 100644 index 00000000..0bcbfc8e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +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.infrastructure.exposed.QueryMapper +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 ExposedFilterRepository(private val filterQueryMapper: QueryMapper) : FilterRepository, + AbstractRepository() { + override suspend fun save(filter: Filter): Filter = query { + Filters.upsert { + it[id] = filter.id.id + it[userId] = filter.userDetailId.id + it[name] = filter.name.name + it[context] = filter.filterContext.joinToString(",") { it.name } + it[filterAction] = filter.filterAction.name + } + FilterKeywords.deleteWhere { + filterId eq filter.id.id + } + FilterKeywords.batchUpsert(filter.filterKeywords) { + this[FilterKeywords.id] = it.id.id + this[FilterKeywords.filterId] = filter.id.id + this[FilterKeywords.keyword] = it.keyword.keyword + this[FilterKeywords.mode] = it.mode.name + } + filter + } + + override suspend fun delete(filter: Filter): Unit = query { + FilterKeywords.deleteWhere { filterId eq filter.id.id } + Filters.deleteWhere { id eq filter.id.id } + } + + override suspend fun findByFilterKeywordId(filterKeywordId: FilterKeywordId): Filter? { + val filterId = FilterKeywords + .selectAll() + .where { FilterKeywords.id eq filterKeywordId.id } + .firstOrNull()?.get(FilterKeywords.filterId) ?: return null + val where = Filters.selectAll().where { Filters.id eq filterId } + return filterQueryMapper.map(where).firstOrNull() + } + + override suspend fun findByFilterId(filterId: FilterId): Filter? { + val where = Filters.selectAll().where { Filters.id eq filterId.id } + return filterQueryMapper.map(where).firstOrNull() + } + + override val logger: Logger + get() = Companion.logger + + companion object { + private val logger = LoggerFactory.getLogger(ExposedFilterRepository::class.java) + } +} + +object Filters : Table("filters") { + val id = long("id") + val userId = long("user_id").references(UserDetails.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE) + val name = varchar("name", 255) + val context = varchar("context", 500) + val filterAction = varchar("action", 255) + + override val primaryKey: PrimaryKey = PrimaryKey(id) +} + +object FilterKeywords : Table("filter_keywords") { + val id = long("id") + val filterId = + long("filter_id").references(Filters.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE) + val keyword = varchar("keyword", 1000) + val mode = varchar("mode", 100) + + override val primaryKey: PrimaryKey = PrimaryKey(id) +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1.kt new file mode 100644 index 00000000..6baf4976 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.filter + +data class DeleteFilterV1(val filterKeywordId: Long) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1ApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1ApplicationService.kt new file mode 100644 index 00000000..88134308 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/DeleteFilterV1ApplicationService.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.filter + +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.filter.FilterKeywordId +import dev.usbharu.hideout.core.domain.model.filter.FilterRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class DeleteFilterV1ApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : + AbstractApplicationService( + transaction, logger + ) { + companion object { + private val logger = LoggerFactory.getLogger(DeleteFilterV1ApplicationService::class.java) + } + + override suspend fun internalExecute(command: DeleteFilterV1, executor: CommandExecutor) { + val filter = filterRepository.findByFilterKeywordId(FilterKeywordId(command.filterKeywordId)) + ?: throw Exception("Not Found") + filterRepository.delete(filter) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1.kt new file mode 100644 index 00000000..e6d1c26c --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.filter + +data class GetFilterV1(val filterKeywordId: Long) \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt new file mode 100644 index 00000000..22411fb9 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.mastodon.application.filter + +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.filter.FilterContext.* +import dev.usbharu.hideout.core.domain.model.filter.FilterKeywordId +import dev.usbharu.hideout.core.domain.model.filter.FilterMode +import dev.usbharu.hideout.core.domain.model.filter.FilterRepository +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.V1Filter +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class GetFilterV1ApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : + AbstractApplicationService( + transaction, logger + ) { + override suspend fun internalExecute(command: GetFilterV1, executor: CommandExecutor): V1Filter { + val filter = filterRepository.findByFilterKeywordId(FilterKeywordId(command.filterKeywordId)) + ?: throw Exception("Not Found") + + val filterKeyword = filter.filterKeywords.find { it.id.id == command.filterKeywordId } + return V1Filter( + id = filter.id.id.toString(), + phrase = filterKeyword?.keyword?.keyword, + context = filter.filterContext.map { + when (it) { + home -> V1Filter.Context.home + notifications -> V1Filter.Context.notifications + public -> V1Filter.Context.public + thread -> V1Filter.Context.thread + account -> V1Filter.Context.account + } + }, + expiresAt = null, + irreversible = false, + wholeWord = filterKeyword?.mode == FilterMode.WHOLE_WORD + ) + } + + companion object { + private val logger = LoggerFactory.getLogger(GetFilterV1ApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt index 9a4e7a05..4bfdbea5 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt @@ -16,21 +16,50 @@ package dev.usbharu.hideout.mastodon.interfaces.api +import dev.usbharu.hideout.core.application.filter.* +import dev.usbharu.hideout.core.domain.model.filter.FilterAction +import dev.usbharu.hideout.core.domain.model.filter.FilterContext +import dev.usbharu.hideout.core.domain.model.filter.FilterMode +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1 +import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1ApplicationService +import dev.usbharu.hideout.mastodon.application.filter.GetFilterV1 +import dev.usbharu.hideout.mastodon.application.filter.GetFilterV1ApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.FilterApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* -import kotlinx.coroutines.flow.Flow +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Filter +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.FilterKeyword +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.FilterPostRequest.Context +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.V1FilterPostRequest.Context.* import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @Controller -class SpringFilterApi : FilterApi { +class SpringFilterApi( + private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, + private val userRegisterFilterApplicationService: UserRegisterFilterApplicationService, + private val getFilterV1ApplicationService: GetFilterV1ApplicationService, + private val deleteFilterV1ApplicationService: DeleteFilterV1ApplicationService, + private val userDeleteFilterApplicationService: UserDeleteFilterApplicationService, + private val userGetFilterApplicationService: UserGetFilterApplicationService, +) : FilterApi { override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity { - return super.apiV1FiltersIdDelete(id) + return ResponseEntity.ok( + deleteFilterV1ApplicationService.execute( + DeleteFilterV1(id.toLong()), + oauth2CommandExecutorFactory.getCommandExecutor() + ) + ) } override suspend fun apiV1FiltersIdGet(id: String): ResponseEntity { - return super.apiV1FiltersIdGet(id) + return ResponseEntity.ok( + getFilterV1ApplicationService.execute( + GetFilterV1(id.toLong()), + oauth2CommandExecutorFactory.getCommandExecutor() + ) + ) } override suspend fun apiV1FiltersIdPut( @@ -45,7 +74,33 @@ class SpringFilterApi : FilterApi { } override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity { - return super.apiV1FiltersPost(v1FilterPostRequest) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + val filterMode = if (v1FilterPostRequest.wholeWord == true) { + FilterMode.WHOLE_WORD + } else { + FilterMode.NONE + } + val filterContext = v1FilterPostRequest.context.map { + when (it) { + home -> FilterContext.home + notifications -> FilterContext.notifications + public -> FilterContext.public + thread -> FilterContext.thread + account -> FilterContext.account + } + }.toSet() + val filter = userRegisterFilterApplicationService.execute( + RegisterFilter( + v1FilterPostRequest.phrase, filterContext, FilterAction.warn, + setOf(RegisterFilterKeyword(v1FilterPostRequest.phrase, filterMode)) + ), executor + ) + return ResponseEntity.ok( + getFilterV1ApplicationService.execute( + GetFilterV1(filter.filterKeywords.first().id), + executor + ) + ) } override suspend fun apiV2FiltersFilterIdKeywordsPost( @@ -63,13 +118,50 @@ class SpringFilterApi : FilterApi { } override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity { - return super.apiV2FiltersIdDelete(id) + userDeleteFilterApplicationService.execute( + DeleteFilter(id.toLong()), + oauth2CommandExecutorFactory.getCommandExecutor() + ) + return ResponseEntity.ok(Unit) } override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity { - return super.apiV2FiltersIdGet(id) + val filter = userGetFilterApplicationService.execute( + GetFilter(id.toLong()), + oauth2CommandExecutorFactory.getCommandExecutor() + ) + return ResponseEntity.ok( + filter(filter) + ) } + private fun filter(filter: dev.usbharu.hideout.core.application.filter.Filter) = Filter( + id = filter.filterId.toString(), + title = filter.name, + context = filter.filterContext.map { + when (it) { + FilterContext.home -> Filter.Context.home + FilterContext.notifications -> Filter.Context.notifications + FilterContext.public -> Filter.Context.public + FilterContext.thread -> Filter.Context.thread + FilterContext.account -> Filter.Context.account + } + }, + expiresAt = null, + filterAction = when (filter.filterAction) { + FilterAction.warn -> Filter.FilterAction.warn + FilterAction.hide -> Filter.FilterAction.hide + + }, + keywords = filter.filterKeywords.map { + FilterKeyword( + it.id.toString(), + it.keyword, + it.filterMode == FilterMode.WHOLE_WORD + ) + }, statuses = null + ) + override suspend fun apiV2FiltersIdPut( id: String, title: String?, @@ -99,14 +191,46 @@ class SpringFilterApi : FilterApi { } override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity { - return super.apiV2FiltersPost(filterPostRequest) + val executor = oauth2CommandExecutorFactory.getCommandExecutor() + val filter = userRegisterFilterApplicationService.execute( + RegisterFilter( + filterName = filterPostRequest.title, + filterContext = filterPostRequest.context.map { + when (it) { + Context.home -> FilterContext.home + Context.notifications -> FilterContext.notifications + Context.public -> FilterContext.public + Context.thread -> FilterContext.thread + Context.account -> FilterContext.account + } + }.toSet(), + filterAction = when (filterPostRequest.filterAction) { + FilterPostRequest.FilterAction.warn -> FilterAction.warn + FilterPostRequest.FilterAction.hide -> FilterAction.hide + null -> FilterAction.warn + }, + filterKeywords = filterPostRequest.keywordsAttributes.orEmpty().map { + RegisterFilterKeyword( + it.keyword, + if (it.regex == true) { + FilterMode.REGEX + } else if (it.wholeWord == true) { + FilterMode.WHOLE_WORD + } else { + FilterMode.NONE + } + ) + }.toSet() + ), executor + ) + return ResponseEntity.ok(filter(filter)) } override suspend fun apiV2FiltersStatusesIdDelete(id: String): ResponseEntity { - return super.apiV2FiltersStatusesIdDelete(id) + return ResponseEntity.notFound().build() } override suspend fun apiV2FiltersStatusesIdGet(id: String): ResponseEntity { - return super.apiV2FiltersStatusesIdGet(id) + return ResponseEntity.notFound().build() } } \ No newline at end of file From 21ab0e0dfd7efa3570ecdce4dfeb2c545fdb3603 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:54:18 +0900 Subject: [PATCH 46/54] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=A4=E3=82=B3?= =?UTF-8?q?=E3=83=B3=E3=81=A8=E3=83=90=E3=83=8A=E3=83=BC=E3=82=92=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/domain/model/actor/Actor.kt | 19 +++++++++++++++++++ .../exposed/ActorResultRowMapper.kt | 5 ++++- .../ExposedActorRepository.kt | 5 +++++ .../factory/ActorFactoryImpl.kt | 4 +++- .../domain/model/actor/TestActorFactory.kt | 4 +++- 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index cbbb29db..73f5d15c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -20,6 +20,7 @@ import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory 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.shared.domainevent.DomainEventStorable import java.net.URI @@ -53,8 +54,26 @@ class Actor( emojiIds: Set, deleted: Boolean, roles: Set, + icon: MediaId?, + banner: MediaId?, ) : DomainEventStorable() { + var banner = banner + private set + + fun setBannerUrl(banner: MediaId?, actor: Actor) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + this.banner = banner + } + + var icon = icon + private set + + fun setIconUrl(icon: MediaId?, actor: Actor) { + addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + this.icon = icon + } + var roles = roles private set diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt index 3b26139e..3bb58e43 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/ActorResultRowMapper.kt @@ -19,6 +19,7 @@ package dev.usbharu.hideout.core.infrastructure.exposed 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.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.sql.ResultRow @@ -59,7 +60,9 @@ class ActorResultRowMapper : ResultRowMapper { .map { EmojiId(it.toLong()) } .toSet(), deleted = resultRow[Actors.deleted], - roles = emptySet() + roles = emptySet(), + icon = resultRow[Actors.icon]?.let { MediaId(it) }, + banner = resultRow[Actors.banner]?.let { MediaId(it) } ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index d8f560c9..fbdc1d62 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -53,6 +53,9 @@ class ExposedActorRepository( it[suspend] = actor.suspend it[moveTo] = actor.moveTo?.id it[emojis] = actor.emojis.joinToString(",") + it[icon] = actor.icon?.id + it[banner] = actor.banner?.id + } ActorsAlsoKnownAs.deleteWhere { actorId eq actor.id.id @@ -127,6 +130,8 @@ object Actors : Table("actors") { val moveTo = long("move_to").references(id).nullable() val emojis = varchar("emojis", 3000) val deleted = bool("deleted") + val banner = long("banner").references(Media.id).nullable() + val icon = long("icon").references(Media.id).nullable() override val primaryKey = PrimaryKey(id) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt index 09ba2411..0fb961c5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/factory/ActorFactoryImpl.kt @@ -61,7 +61,9 @@ class ActorFactoryImpl( suspend = false, emojiIds = emptySet(), deleted = false, - roles = emptySet() + roles = emptySet(), + banner = null, + icon = null ) } } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt index 4bb92910..0fb9087d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/TestActorFactory.kt @@ -65,7 +65,9 @@ object TestActorFactory { moveTo = moveTo, emojiIds = emojiIds, deleted = deleted, - roles = emptySet() + roles = emptySet(), + icon = null, + banner = null ) } } From bf8c643d8234eeb1eb61d414f6d05679306791eb Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 15 Jun 2024 01:56:06 +0900 Subject: [PATCH 47/54] wip --- gradle.properties | 4 +- hideout-core/build.gradle.kts | 108 +-- hideout-core/gradle.properties | 2 + .../src/e2eTest/kotlin/AssertionUtil.kt | 42 -- hideout-core/src/e2eTest/kotlin/KarateUtil.kt | 44 -- .../kotlin/federation/FollowAcceptTest.kt | 101 --- .../kotlin/federation/InboxCommonTest.kt | 146 ---- .../e2eTest/kotlin/oauth2/OAuth2LoginTest.kt | 70 -- .../src/e2eTest/resources/application.yml | 44 -- .../federation/FollowAcceptMockServer.feature | 140 ---- .../federation/FollowAcceptTest.feature | 29 - .../federation/InboxCommonTest.feature | 158 ---- .../InboxxCommonMockServerTest.feature | 136 ---- .../src/e2eTest/resources/karate-config.js | 30 - .../src/e2eTest/resources/logback.xml | 21 - .../resources/oauth2/Oauth2LoginTest.feature | 95 --- .../src/e2eTest/resources/oauth2/user.sql | 51 -- .../kotlin/activitypub/inbox/InboxTest.kt | 163 ---- .../kotlin/activitypub/note/NoteTest.kt | 243 ------ .../activitypub/webfinger/WebFingerTest.kt | 116 --- .../account/AccountApiPaginationTest.kt | 170 ----- .../kotlin/mastodon/account/AccountApiTest.kt | 477 ------------ .../intTest/kotlin/mastodon/apps/AppTest.kt | 120 --- .../kotlin/mastodon/filter/FilterTest.kt | 714 ------------------ .../kotlin/mastodon/media/MediaTest.kt | 145 ---- .../ExposedNotificationsApiPaginationTest.kt | 185 ----- .../MongodbNotificationsApiPaginationTest.kt | 219 ------ .../kotlin/mastodon/status/StatusTest.kt | 247 ------ .../mastodon/timelines/TimelineApiTest.kt | 136 ---- .../intTest/kotlin/util/WithHttpSignature.kt | 36 - ...WithHttpSignatureSecurityContextFactory.kt | 65 -- .../kotlin/util/WithMockHttpSignature.kt | 39 - ...MockHttpSignatureSecurityContextFactory.kt | 55 -- .../src/intTest/resources/application.yml | 40 - .../resources/junit-platform.properties | 2 - .../src/intTest/resources/logback.xml | 11 - .../src/intTest/resources/media/400x400.png | Bin 7227 -> 0 bytes ...iV1AccountsIdFollowPost フォローã§ãã‚‹.sql | 17 - .../sql/accounts/test-accounts-statuses.sql | 202 ----- .../resources/sql/filter/test-filter.sql | 4 - ...¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹.sql | 28 - ...証ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹.sql | 29 - ...¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹.sql | 29 - ...•ç¨¿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹.sql | 25 - ...ƒ©ã‚¤ã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹.sql | 20 - ...ã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404.sql | 17 - .../sql/note/匿åã§public投稿をå–å¾—ã§ãã‚‹.sql | 17 - .../note/匿åã§unlisted投稿をå–å¾—ã§ãã‚‹.sql | 17 - .../test-mastodon_notifications.sql | 66 -- .../sql/notification/test-notifications.sql | 66 -- .../resources/sql/test-custom-emoji.sql | 3 - .../src/intTest/resources/sql/test-post.sql | 4 - .../src/intTest/resources/sql/test-user.sql | 10 - .../src/intTest/resources/sql/test-user2.sql | 10 - .../hideout/core/application/media/Media.kt | 19 + .../core/application/media/UploadMedia.kt} | 7 +- .../media/UploadMediaApplicationService.kt | 34 + .../core/external/media/MediaProcessor.kt} | 12 +- .../core/external/media/ProcessedMedia.kt | 29 + owl/gradle.properties | 4 +- .../build.gradle.kts | 2 +- 61 files changed, 111 insertions(+), 4964 deletions(-) delete mode 100644 hideout-core/src/e2eTest/kotlin/AssertionUtil.kt delete mode 100644 hideout-core/src/e2eTest/kotlin/KarateUtil.kt delete mode 100644 hideout-core/src/e2eTest/kotlin/federation/FollowAcceptTest.kt delete mode 100644 hideout-core/src/e2eTest/kotlin/federation/InboxCommonTest.kt delete mode 100644 hideout-core/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt delete mode 100644 hideout-core/src/e2eTest/resources/application.yml delete mode 100644 hideout-core/src/e2eTest/resources/federation/FollowAcceptMockServer.feature delete mode 100644 hideout-core/src/e2eTest/resources/federation/FollowAcceptTest.feature delete mode 100644 hideout-core/src/e2eTest/resources/federation/InboxCommonTest.feature delete mode 100644 hideout-core/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature delete mode 100644 hideout-core/src/e2eTest/resources/karate-config.js delete mode 100644 hideout-core/src/e2eTest/resources/logback.xml delete mode 100644 hideout-core/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature delete mode 100644 hideout-core/src/e2eTest/resources/oauth2/user.sql delete mode 100644 hideout-core/src/intTest/kotlin/activitypub/inbox/InboxTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/activitypub/note/NoteTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/media/MediaTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/mastodon/timelines/TimelineApiTest.kt delete mode 100644 hideout-core/src/intTest/kotlin/util/WithHttpSignature.kt delete mode 100644 hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt delete mode 100644 hideout-core/src/intTest/kotlin/util/WithMockHttpSignature.kt delete mode 100644 hideout-core/src/intTest/kotlin/util/WithMockHttpSignatureSecurityContextFactory.kt delete mode 100644 hideout-core/src/intTest/resources/application.yml delete mode 100644 hideout-core/src/intTest/resources/junit-platform.properties delete mode 100644 hideout-core/src/intTest/resources/logback.xml delete mode 100644 hideout-core/src/intTest/resources/media/400x400.png delete mode 100644 hideout-core/src/intTest/resources/sql/accounts/apiV1AccountsIdFollowPost フォローã§ãã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/accounts/test-accounts-statuses.sql delete mode 100644 hideout-core/src/intTest/resources/sql/filter/test-filter.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/メディア付ã投稿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/リプライã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/匿åã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/匿åã§public投稿をå–å¾—ã§ãã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/note/匿åã§unlisted投稿をå–å¾—ã§ãã‚‹.sql delete mode 100644 hideout-core/src/intTest/resources/sql/notification/test-mastodon_notifications.sql delete mode 100644 hideout-core/src/intTest/resources/sql/notification/test-notifications.sql delete mode 100644 hideout-core/src/intTest/resources/sql/test-custom-emoji.sql delete mode 100644 hideout-core/src/intTest/resources/sql/test-post.sql delete mode 100644 hideout-core/src/intTest/resources/sql/test-user.sql delete mode 100644 hideout-core/src/intTest/resources/sql/test-user2.sql create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt rename hideout-core/src/{intTest/kotlin/util/SpringApplicationTestBase.kt => main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt} (82%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt rename hideout-core/src/{intTest/kotlin/util/TestTransaction.kt => main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt} (66%) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt diff --git a/gradle.properties b/gradle.properties index 89909840..483b2ee6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,6 @@ org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true -org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC \ No newline at end of file +org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn \ No newline at end of file diff --git a/hideout-core/build.gradle.kts b/hideout-core/build.gradle.kts index ca9a8cc9..fe0e1d5e 100644 --- a/hideout-core/build.gradle.kts +++ b/hideout-core/build.gradle.kts @@ -3,7 +3,7 @@ import com.github.jk1.license.filter.LicenseBundleNormalizer import com.github.jk1.license.importer.DependencyDataImporter import com.github.jk1.license.importer.XmlReportImporter import com.github.jk1.license.render.* -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.kotlin.jvm) @@ -22,58 +22,6 @@ apply { group = "dev.usbharu" version = "0.0.1" -sourceSets { - create("intTest") { - compileClasspath += sourceSets.main.get().output - runtimeClasspath += sourceSets.main.get().output - } - create("e2eTest") { - compileClasspath += sourceSets.main.get().output - runtimeClasspath += sourceSets.main.get().output - } -} - -val intTestImplementation by configurations.getting { - extendsFrom(configurations.implementation.get()) -} -val intTestRuntimeOnly by configurations.getting { - extendsFrom(configurations.runtimeOnly.get()) -} - -val e2eTestImplementation by configurations.getting { - extendsFrom(configurations.implementation.get()) -} - -val e2eTestRuntimeOnly by configurations.getting { - extendsFrom(configurations.runtimeOnly.get()) -} - -val integrationTest = task("integrationTest") { - description = "Runs integration tests." - group = "verification" - - testClassesDirs = sourceSets["intTest"].output.classesDirs - classpath = sourceSets["intTest"].runtimeClasspath - shouldRunAfter("test") - - useJUnitPlatform() -} - -val e2eTest = task("e2eTest") { - description = "Runs e2e tests." - group = "verification" - - testClassesDirs = sourceSets["e2eTest"].output.classesDirs - classpath = sourceSets["e2eTest"].runtimeClasspath - shouldRunAfter("test") - - useJUnitPlatform() -} - -tasks.check { - dependsOn(integrationTest) - dependsOn(e2eTest) -} tasks.withType { useJUnitPlatform() @@ -82,24 +30,20 @@ tasks.withType { "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.naming/javax.naming=ALL-UNNAMED", + "--add-opens", "java.base/java.util.concurrent.locks=ALL-UNNAMED" ).toMutableList() } } - -tasks.withType { - kotlinOptions { - freeCompilerArgs += "-Xjsr305=strict" +kotlin { + jvmToolchain(21) + compilerOptions { + freeCompilerArgs.add("-Xjsr305=strict") + jvmTarget = JvmTarget.JVM_21 } -// dependsOn("openApiGenerateMastodonCompatibleApi") -// mustRunAfter("openApiGenerateMastodonCompatibleApi") } -tasks.clean { - delete += listOf("$rootDir/src/main/resources/static") -} - repositories { mavenCentral() maven { @@ -125,21 +69,6 @@ repositories { } } -kotlin { - target { - compilations.all { - kotlinOptions.jvmTarget = JavaVersion.VERSION_21.toString() - } - } -} - -sourceSets.main { - kotlin.srcDirs( - "$buildDir/generated/ksp/main", - "$buildDir/generated/sources/openapi/src/main/kotlin", - "$buildDir/generated/sources/mastodon/src/main/kotlin" - ) -} val os = org.gradle.nativeplatform.platform.internal .DefaultNativePlatform.getCurrentOperatingSystem() @@ -201,30 +130,15 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") - implementation(libs.kotlin.junit) - implementation(libs.coroutines.test) - + testImplementation(libs.kotlin.junit) + testImplementation(libs.coroutines.test) testImplementation(libs.ktor.client.mock) testImplementation(libs.h2db) - testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") testImplementation("org.mockito:mockito-inline:5.2.0") testImplementation("nl.jqno.equalsverifier:equalsverifier:3.16.1") testImplementation("com.jparams:to-string-verifier:1.4.8") - intTestImplementation("org.springframework.boot:spring-boot-starter-test") - intTestImplementation("org.springframework.security:spring-security-test") - intTestImplementation(libs.kotlin.junit) - intTestImplementation(libs.coroutines.test) - intTestImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") - intTestImplementation(libs.h2db) - - e2eTestImplementation("org.springframework.boot:spring-boot-starter-test") - e2eTestImplementation("org.springframework.security:spring-security-test") - e2eTestImplementation("org.springframework.boot:spring-boot-starter-webflux") - e2eTestImplementation("com.intuit.karate:karate-junit5:1.4.1") - e2eTestImplementation(libs.h2db) - } detekt { @@ -312,7 +226,9 @@ kover { } springBoot { - buildInfo() + buildInfo { + + } } licenseReport { diff --git a/hideout-core/gradle.properties b/hideout-core/gradle.properties index 29566cd4..f3b8b6f9 100644 --- a/hideout-core/gradle.properties +++ b/hideout-core/gradle.properties @@ -18,3 +18,5 @@ org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn \ No newline at end of file diff --git a/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt b/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt deleted file mode 100644 index a93ba706..00000000 --- a/hideout-core/src/e2eTest/kotlin/AssertionUtil.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.selectAll -import java.net.MalformedURLException -import java.net.URL - -object AssertionUtil { - - @JvmStatic - fun assertUserExist(username: String, domain: String): Boolean { - val s = try { - val url = URL(domain) - url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty() - } catch (e: MalformedURLException) { - domain - } - - val selectAll = Actors.selectAll() - println(selectAll.fetchSize) - - println(selectAll.toList().size) - - selectAll.map { "@${it[Actors.name]}@${it[Actors.domain]}" }.forEach { println(it) } - - return Actors.selectAll().where { Actors.name eq username and (Actors.domain eq s) }.empty().not() - } -} diff --git a/hideout-core/src/e2eTest/kotlin/KarateUtil.kt b/hideout-core/src/e2eTest/kotlin/KarateUtil.kt deleted file mode 100644 index 3d2b5604..00000000 --- a/hideout-core/src/e2eTest/kotlin/KarateUtil.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import com.intuit.karate.junit5.Karate - -object KarateUtil { - fun springBootKarateTest(path: String, scenario: String, clazz: Class<*>, port: String): Karate { - if (scenario.isEmpty()) { - return Karate.run(path).relativeTo(clazz).systemProperty("karate.port", port).karateEnv("dev") - } else { - return Karate.run(path).scenarioName(scenario).relativeTo(clazz).systemProperty("karate.port", port) - .karateEnv("dev") - } - } - - fun e2eTest(path: String, scenario: String = "", properties: Map, clazz: Class<*>): Karate { - val run = Karate.run(path) - - val karate = if (scenario.isEmpty()) { - run - } else { - run.scenarioName(scenario) - } - - var relativeTo = karate.relativeTo(clazz) - - properties.map { relativeTo = relativeTo.systemProperty(it.key, it.value) } - - return relativeTo.karateEnv("dev") - } -} diff --git a/hideout-core/src/e2eTest/kotlin/federation/FollowAcceptTest.kt b/hideout-core/src/e2eTest/kotlin/federation/FollowAcceptTest.kt deleted file mode 100644 index 765556cf..00000000 --- a/hideout-core/src/e2eTest/kotlin/federation/FollowAcceptTest.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package federation - -import AssertionUtil -import KarateUtil -import com.intuit.karate.core.MockServer -import com.intuit.karate.junit5.Karate -import dev.usbharu.hideout.SpringApplication -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.* -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.web.server.LocalServerPort -import org.springframework.transaction.annotation.Transactional -import java.net.MalformedURLException -import java.net.URL - -@SpringBootTest( - classes = [SpringApplication::class], - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT -) -@Transactional -class FollowAcceptTest { - @LocalServerPort - private var port = "" - - @Karate.Test - @TestFactory - @Disabled - fun `FollowAcceptTest`(): Karate { - return KarateUtil.e2eTest( - "FollowAcceptTest", "Follow Accept Test", - mapOf("karate.port" to port), - javaClass - ) - } - - companion object { - lateinit var server: MockServer - - lateinit var _remotePort: String - - @JvmStatic - fun assertUserExist(username: String, domain: String) = runBlocking { - val s = try { - val url = URL(domain) - url.host + ":" + url.port.toString().takeIf { it != "-1" }.orEmpty() - } catch (e: MalformedURLException) { - domain - } - - var check = false - - repeat(10) { - delay(1000) - check = AssertionUtil.assertUserExist(username, s) or check - if (check) { - return@repeat - } - } - - Assertions.assertTrue(check, "User @$username@$s not exist.") - } - - @JvmStatic - fun getRemotePort(): String = _remotePort - - @BeforeAll - @JvmStatic - fun beforeAll(@Autowired flyway: Flyway) { - server = MockServer.feature("classpath:federation/FollowAcceptMockServer.feature").http(0).build() - _remotePort = server.port.toString() - - flyway.clean() - flyway.migrate() - } - - @AfterAll - @JvmStatic - fun afterAll() { - server.stop() - } - } -} diff --git a/hideout-core/src/e2eTest/kotlin/federation/InboxCommonTest.kt b/hideout-core/src/e2eTest/kotlin/federation/InboxCommonTest.kt deleted file mode 100644 index 2e1ece7f..00000000 --- a/hideout-core/src/e2eTest/kotlin/federation/InboxCommonTest.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package federation - -import AssertionUtil -import KarateUtil -import com.intuit.karate.core.MockServer -import com.intuit.karate.junit5.Karate -import dev.usbharu.hideout.SpringApplication -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.TestFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.web.server.LocalServerPort -import org.springframework.transaction.annotation.Transactional - -@SpringBootTest( - classes = [SpringApplication::class], - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT -) -@Transactional -@Disabled -class InboxCommonTest { - @LocalServerPort - private var port = "" - - @Karate.Test - @TestFactory - fun `inboxã«HTTP Signature付ãã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã£ãŸã‚‰ãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«è¡Œã`(): Karate { - return KarateUtil.e2eTest( - "InboxCommonTest", - "inboxã«HTTP Signature付ãã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã£ãŸã‚‰ãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«è¡Œã", - mapOf( - "karate.port" to port, - "karate.remotePort" to _remotePort - ), - javaClass - ) - } - - @Karate.Test - @TestFactory - fun `user-inboxã«HTTP Signature付ãã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã£ãŸã‚‰ãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«è¡Œã`(): Karate { - return KarateUtil.e2eTest( - "InboxCommonTest", - "user-inboxã«HTTP Signature付ãã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã£ãŸã‚‰ãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«è¡Œã", - mapOf( - "karate.port" to port, - "karate.remotePort" to _remotePort - ), - javaClass - ) - } - - @Karate.Test - @TestFactory - fun `inboxã«HTTP SignatureãŒãªã„リクエストãŒããŸã‚‰401ã‚’è¿”ã™`(): Karate { - return KarateUtil.e2eTest( - "InboxCommonTest", - "inboxã«HTTP SignatureãŒãªã„リクエストãŒããŸã‚‰401ã‚’è¿”ã™", - mapOf("karate.port" to port), - javaClass - ) - } - - @Karate.Test - @TestFactory - fun `user-inboxã«HTTP SignatureãŒãªã„リクエストãŒããŸã‚‰401ã‚’è¿”ã™`(): Karate { - return KarateUtil.e2eTest( - "InboxCommonTest", - "user-inboxã«HTTP SignatureãŒãªã„リクエストãŒããŸã‚‰401ã‚’è¿”ã™", - mapOf("karate.port" to port), - javaClass - ) - } - - @Karate.Test - @TestFactory - fun `inboxã«Conetnt-Type application *+json以外ãŒæ¥ãŸã‚‰415ã‚’è¿”ã™`(): Karate { - return KarateUtil.e2eTest( - "InboxCommonTest", - "inboxã«Content-Type application/json以外ãŒæ¥ãŸã‚‰415ã‚’è¿”ã™", - mapOf("karate.port" to port), - javaClass - ) - } - - companion object { - lateinit var server: MockServer - - lateinit var _remotePort: String - - @JvmStatic - fun assertUserExist(username: String, domain: String) = runBlocking { - var check = false - - repeat(10) { - delay(1000) - check = AssertionUtil.assertUserExist(username, domain) or check - if (check) { - return@repeat - } - } - - assertTrue(check, "User @$username@$domain not exist.") - } - - @JvmStatic - fun getRemotePort(): String = _remotePort - - @BeforeAll - @JvmStatic - fun beforeAll() { - server = MockServer.feature("classpath:federation/InboxxCommonMockServerTest.feature").http(0).build() - _remotePort = server.port.toString() - } - - @AfterAll - @JvmStatic - fun afterAll(@Autowired flyway: Flyway) { - server.stop() - flyway.clean() - flyway.migrate() - } - } -} diff --git a/hideout-core/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt b/hideout-core/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt deleted file mode 100644 index fd248df1..00000000 --- a/hideout-core/src/e2eTest/kotlin/oauth2/OAuth2LoginTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package oauth2 - -import KarateUtil -import com.intuit.karate.junit5.Karate -import dev.usbharu.hideout.SpringApplication -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.TestFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.web.server.LocalServerPort -import org.springframework.test.context.jdbc.Sql - -@SpringBootTest( - classes = [SpringApplication::class], - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, -) -@Sql("/oauth2/user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class OAuth2LoginTest { - - @LocalServerPort - private var port = "" - - @Karate.Test - @TestFactory - fun `スコープwrite readã‚’æŒã£ãŸãƒˆãƒ¼ã‚¯ãƒ³ã®ä½œæˆ`(): Karate { - return KarateUtil.springBootKarateTest( - "Oauth2LoginTest", - "スコープwrite readã‚’æŒã£ãŸãƒˆãƒ¼ã‚¯ãƒ³ã®ä½œæˆ", - javaClass, - port - ) - } - - @Karate.Test - @TestFactory - fun `スコープread_statuses write_statusesã‚’æŒã£ãŸãƒˆãƒ¼ã‚¯ãƒ³ã®ä½œæˆ`(): Karate { - return KarateUtil.springBootKarateTest( - "Oauth2LoginTest", - "スコープread:statuses write:statusesã‚’æŒã£ãŸãƒˆãƒ¼ã‚¯ãƒ³ã®ä½œæˆ", - javaClass, - port - ) - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway) { - flyway.clean() - flyway.migrate() - } - } -} diff --git a/hideout-core/src/e2eTest/resources/application.yml b/hideout-core/src/e2eTest/resources/application.yml deleted file mode 100644 index 778035ba..00000000 --- a/hideout-core/src/e2eTest/resources/application.yml +++ /dev/null @@ -1,44 +0,0 @@ -hideout: - url: "https://localhost:8080" - use-mongodb: false - security: - jwt: - generate: true - key-id: a - private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ==" - public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" - storage: - type: local - debug: - trace-query-exception: true - trace-query-call: true - private: false - -spring: - flyway: - enabled: true - clean-disabled: false - datasource: - driver-class-name: org.h2.Driver - url: "jdbc:h2:mem:e2e-test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4" - username: "" - password: - data: - mongodb: - auto-index-creation: true - host: localhost - port: 27017 - database: hideout - h2: - console: - enabled: true - -# exposed: -# generate-ddl: true -# excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed -server: - port: 8080 - tomcat: - basedir: tomcat-e2e - accesslog: - enabled: true diff --git a/hideout-core/src/e2eTest/resources/federation/FollowAcceptMockServer.feature b/hideout-core/src/e2eTest/resources/federation/FollowAcceptMockServer.feature deleted file mode 100644 index 60793fde..00000000 --- a/hideout-core/src/e2eTest/resources/federation/FollowAcceptMockServer.feature +++ /dev/null @@ -1,140 +0,0 @@ -Feature: Follow Accept Mock Server - - Background: - * def assertInbox = Java.type(`federation.FollowAcceptTest`) - * def req = {req: []} - - Scenario: pathMatches('/users/test-follower') && methodIs('get') - * def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort() - * def username = 'test-follower' - * def userUrl = remoteUrl + '/users/' + username - - - * def person = - """ - { - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "toot": "http://joinmastodon.org/ns#", - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "featuredTags": { - "@id": "toot:featuredTags", - "@type": "@id" - }, - "alsoKnownAs": { - "@id": "as:alsoKnownAs", - "@type": "@id" - }, - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "discoverable": "toot:discoverable", - "Device": "toot:Device", - "Ed25519Signature": "toot:Ed25519Signature", - "Ed25519Key": "toot:Ed25519Key", - "Curve25519Key": "toot:Curve25519Key", - "EncryptedMessage": "toot:EncryptedMessage", - "publicKeyBase64": "toot:publicKeyBase64", - "deviceId": "toot:deviceId", - "claim": { - "@type": "@id", - "@id": "toot:claim" - }, - "fingerprintKey": { - "@type": "@id", - "@id": "toot:fingerprintKey" - }, - "identityKey": { - "@type": "@id", - "@id": "toot:identityKey" - }, - "devices": { - "@type": "@id", - "@id": "toot:devices" - }, - "messageFranking": "toot:messageFranking", - "messageType": "toot:messageType", - "cipherText": "toot:cipherText", - "suspended": "toot:suspended", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - } - } - ], - "id": #(userUrl), - "type": "Person", - "following": #(userUrl + '/following'), - "followers": #(userUrl + '/followers'), - "inbox": #(userUrl + '/inbox'), - "outbox": #(userUrl + '/outbox'), - "featured": #(userUrl + '/collections/featured'), - "featuredTags": #(userUrl + '/collections/tags'), - "preferredUsername": #(username), - "name": #(username), - "summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n", - "url": #(userUrl + '/@' + username), - "manuallyApprovesFollowers": false, - "discoverable": true, - "published": "2016-03-16T00:00:00Z", - "devices": #(userUrl + '/collections/devices'), - "alsoKnownAs": [ - #( 'https://example.com/users/' + username) - ], - "publicKey": { - "id": #(userUrl + '#main-key'), - "owner": #(userUrl), - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmtKo0xYGXR4M0LQQhK4\nBkKpRvvUxrqGV6Ew4CBHSyzdnbFsiBqHUWz4JvRiQvAPqQ4jQFpxVZCPr9xx6lJp\nx7EAAKIdVTnBBV4CYfu7QPsRqtjbB5408q+mo5oUXNs8xg2tcC42p2SJ5CRJX/fr\nOgCZwo3LC9pOBdCQZ+tiiPmWNBTNby99JZn4D/xNcwuhV04qcPoHYD9OPuxxGyzc\naVJ2mqJmvi/lewQuR8qnUIbz+Gik+xvyG6LmyuDoa1H2LDQfQXYb62G70HsYdu7a\ndObvZovytp+kkjP/cUaIYkhhOAYqAA4zCwVRY4NHK0MAMq9sMoUfNJa8U+zR9NvD\noQIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "tag": [], - "attachment": [ - { - "type": "PropertyValue", - "name": "Pixib Fan-Bridge", - "value": "\u003ca href=\"https://example.com/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eexample.com/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - }, - { - "type": "PropertyValue", - "name": "GitHub", - "value": "\u003ca href=\"https://github.com/usbharu/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/usbharu/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - } - ], - "endpoints": { - "sharedInbox": #(remoteUrl + '/inbox') - }, - "icon": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Destroyer_Vozbuzhdenyy.jpg/320px-Destroyer_Vozbuzhdenyy.jpg" - }, - "image": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg/320px-Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg" - } -} - """ - - * set req.req[] = '/users/' + username - * def response = person - - Scenario: pathMatches('/inbox') && methodIs('post') - * set req.req[] = '/inbox' - * def responseStatus = 202 - - Scenario: pathMatches('/internal-assertion-api/requests') && methodIs('get') - * def response = req - - Scenario: pathMatches('/internal-assertion-api/requests/deleteAll') && methodIs('post') - * set req.req = [] - * def responseStatus = 200 diff --git a/hideout-core/src/e2eTest/resources/federation/FollowAcceptTest.feature b/hideout-core/src/e2eTest/resources/federation/FollowAcceptTest.feature deleted file mode 100644 index 7cdb39e5..00000000 --- a/hideout-core/src/e2eTest/resources/federation/FollowAcceptTest.feature +++ /dev/null @@ -1,29 +0,0 @@ -Feature: Follow Accept Test - - Background: - * url baseUrl - * def assertionUtil = Java.type('AssertionUtil') - - Scenario: Follow Accept Test - - * def follow = - """ - {"type": "Follow","actor": #(remoteUrl + '/users/test-follower'),"object": #(baseUrl + '/users/test-user')} - """ - - Given path '/inbox' - And header Signature = 'keyId="https://test-hideout.usbharu.dev/users/c#pubkey", algorithm="rsa-sha256", headers="x-request-id tpp-redirect-uri digest psu-id", signature="e/91pFiI5bRffP33EMrqoI5A0xjkg3Ar0kzRGHC/1RsLrDW0zV50dHly/qJJ5xrYHRlss3+vd0mznTLBs1X0hx0uXjpfvCvwclpSi8u+sqn+Y2bcQKzf7ah0vAONQd6zeTYW7e/1kDJreP43PsJyz29KZD16Yop8nM++YeEs6C5eWiyYXKoQozXnfmTOX3y6bhxfKKQWVcxA5aLOTmTZRYTsBsTy9zn8NjDQaRI/0gcyYPqpq+2g8j2DbyJu3Z6zP6VmwbGGlQU/s9Pa7G5LqUPH/sBMSlIeqh+Hvm2pL7B3/BMFvGtTD+e2mR60BFnLIxMYx+oX4o33J2XkFIODLQ=="' - And request follow - When method post - Then status 202 - - And retry until assertionUtil.assertUserExist('test-follower',remoteUrl) - - * url remoteUrl - - Given path '/internal-assertion-api/requests' - When method get - Then status 200 - And match response.req contains ['/users/test-follower'] - - * url baseUrl diff --git a/hideout-core/src/e2eTest/resources/federation/InboxCommonTest.feature b/hideout-core/src/e2eTest/resources/federation/InboxCommonTest.feature deleted file mode 100644 index 848e5630..00000000 --- a/hideout-core/src/e2eTest/resources/federation/InboxCommonTest.feature +++ /dev/null @@ -1,158 +0,0 @@ -Feature: Inbox Common Test - - Background: - * url baseUrl - - Scenario: inboxã«HTTP Signature付ãã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã£ãŸã‚‰ãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«è¡Œã - - * url remoteUrl - - Given path '/internal-assertion-api/requests/deleteAll' - When method post - Then status 200 - - * url baseUrl - - * def inbox = - """ - { "type": "Follow" } - """ - - Given path `/inbox` - And request inbox -# And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target)", signature="a"' - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - When method post - Then status 202 - - * def assertInbox = Java.type(`federation.InboxCommonTest`) - - And assertInbox.assertUserExist('test-user',remoteUrl) - - * url remoteUrl - - Given path '/internal-assertion-api/requests' - When method get - Then status 200 - - * url baseUrl - - * print response - Then match response.req == ['/users/test-user'] - - - Scenario: inboxã«HTTP SignatureãŒãªã„リクエストãŒããŸã‚‰401を返㙠- - * def inbox = - """ - {"type": "Follow"} - """ - - Given path '/inbox' - And request inbox - When method post - Then status 401 - - - Scenario: user-inboxã«HTTP Signature付ãã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã£ãŸã‚‰ãƒªãƒ¢ãƒ¼ãƒˆã«å–å¾—ã—ã«è¡Œã - - * url remoteUrl - - Given path '/internal-assertion-api/requests/deleteAll' - When method post - Then status 200 - - * url baseUrl - - * def inbox = - """ - { "type": "Follow" } - """ - - Given path `/inbox` - And request inbox -# And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target)", signature="a"' - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user2#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - When method post - Then status 202 - - * def assertInbox = Java.type(`federation.InboxCommonTest`) - - And assertInbox.assertUserExist('test-user2',remoteUrl) - - - * url remoteUrl - - Given path '/internal-assertion-api/requests' - When method get - Then status 200 - - * url baseUrl - - * print response - Then match response.req == ['/users/test-user2'] - - Scenario: user-inboxã«HTTP SignatureãŒãªã„リクエストãŒããŸã‚‰401を返㙠- - * def inbox = - """ - {"type": "Follow"} - """ - - Given path '/inbox' - And request inbox - When method post - Then status 401 - - - Scenario: inboxã«Content-Type application/json以外ãŒæ¥ãŸã‚‰415を返㙠- - * def inbox = - """ - {"type": "Follow"} - """ - - Given path '/inbox' - And request inbox - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - And header Accept = 'application/activity+json' - And header Content-Type = 'application/json' - When method post - Then status 202 - - Given path '/inbox' - And request inbox - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - And header Accept = 'application/activity+json' - And header Content-Type = 'application/activity+json' - When method post - Then status 202 - - Given path '/inbox' - And request inbox - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - And header Accept = 'application/activity+json' - And header Content-Type = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' - When method post - Then status 202 - - Given path '/inbox' - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - And header Accept = 'application/activity+json' - When method post - Then status 415 - - * def html = - """ - - - -""" - - Given path '/inbox' - And header Signature = 'keyId="'+ remoteUrl +'/users/test-user#pubkey", algorithm="rsa-sha256", headers="(request-target) date host digest", signature="FfpkmBogW70FMo94yovGpl15L/m4bDjVIFb9mSZUstPE3H00nHiqNsjAq671qFMJsGOO1uWfLEExcdvzwTiC3wuHShzingvxQUbTgcgRTRZcHbtrOZxT8hYHGndpCXGv/NOLkfXDtZO9v5u0fnA2yJFokzyPHOPJ1cJliWlXP38Bl/pO4H5rBLQBZKpM2jYIjMyI78G2rDXNHEeGrGiyfB5SKb3H6zFQL+X9QpXUI4n0f07VsnwaDyp63oUopmzNUyBEuSqB+8va/lbfcWwrxpZnKGzQRZ+VBcV7jDoKGNOP9/O1xEI2CwB8sh+h6KVHdX3EQEvO1slaaLzcwRRqrQ=="' - And header Accept = 'application/activity+json' - And header Content-Type = 'text/html' - And request html - When method post - Then status 415 diff --git a/hideout-core/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature b/hideout-core/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature deleted file mode 100644 index 6d114c04..00000000 --- a/hideout-core/src/e2eTest/resources/federation/InboxxCommonMockServerTest.feature +++ /dev/null @@ -1,136 +0,0 @@ -Feature: InboxCommonMockServer - - Background: - * def assertInbox = Java.type(`federation.InboxCommonTest`) - * def req = {req: []} - - Scenario: pathMatches('/users/{username}') && methodIs('get') - * def remoteUrl = 'http://localhost:' + assertInbox.getRemotePort() - * def username = pathParams.username - * def userUrl = remoteUrl + '/users/' + username - - - * def person = - """ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "toot": "http://joinmastodon.org/ns#", - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "featuredTags": { - "@id": "toot:featuredTags", - "@type": "@id" - }, - "alsoKnownAs": { - "@id": "as:alsoKnownAs", - "@type": "@id" - }, - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - "discoverable": "toot:discoverable", - "Device": "toot:Device", - "Ed25519Signature": "toot:Ed25519Signature", - "Ed25519Key": "toot:Ed25519Key", - "Curve25519Key": "toot:Curve25519Key", - "EncryptedMessage": "toot:EncryptedMessage", - "publicKeyBase64": "toot:publicKeyBase64", - "deviceId": "toot:deviceId", - "claim": { - "@type": "@id", - "@id": "toot:claim" - }, - "fingerprintKey": { - "@type": "@id", - "@id": "toot:fingerprintKey" - }, - "identityKey": { - "@type": "@id", - "@id": "toot:identityKey" - }, - "devices": { - "@type": "@id", - "@id": "toot:devices" - }, - "messageFranking": "toot:messageFranking", - "messageType": "toot:messageType", - "cipherText": "toot:cipherText", - "suspended": "toot:suspended", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - } - } - ], - "id": #(userUrl), - "type": "Person", - "following": #(userUrl + '/following'), - "followers": #(userUrl + '/followers'), - "inbox": #(userUrl + '/inbox'), - "outbox": #(userUrl + '/outbox'), - "featured": #(userUrl + '/collections/featured'), - "featuredTags": #(userUrl + '/collections/tags'), - "preferredUsername": #(username), - "name": #(username), - "summary": "E2E Test User Jaga/Cotlin/Winter Boot/Ktol\nYonTude: https://example.com\nY(Tvvitter): https://example.com\n", - "url": #(userUrl + '/@' + username), - "manuallyApprovesFollowers": false, - "discoverable": true, - "published": "2016-03-16T00:00:00Z", - "devices": #(userUrl + '/collections/devices'), - "alsoKnownAs": [ - #( 'https://example.com/users/' + username) - ], - "publicKey": { - "id": #(userUrl + '#main-key'), - "owner": #(userUrl), - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmtKo0xYGXR4M0LQQhK4\nBkKpRvvUxrqGV6Ew4CBHSyzdnbFsiBqHUWz4JvRiQvAPqQ4jQFpxVZCPr9xx6lJp\nx7EAAKIdVTnBBV4CYfu7QPsRqtjbB5408q+mo5oUXNs8xg2tcC42p2SJ5CRJX/fr\nOgCZwo3LC9pOBdCQZ+tiiPmWNBTNby99JZn4D/xNcwuhV04qcPoHYD9OPuxxGyzc\naVJ2mqJmvi/lewQuR8qnUIbz+Gik+xvyG6LmyuDoa1H2LDQfQXYb62G70HsYdu7a\ndObvZovytp+kkjP/cUaIYkhhOAYqAA4zCwVRY4NHK0MAMq9sMoUfNJa8U+zR9NvD\noQIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "tag": [], - "attachment": [ - { - "type": "PropertyValue", - "name": "Pixib Fan-Bridge", - "value": "\u003ca href=\"https://example.com/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eexample.com/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - }, - { - "type": "PropertyValue", - "name": "GitHub", - "value": "\u003ca href=\"https://github.com/usbharu/hideout\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/usbharu/hideout\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - } - ], - "endpoints": { - "sharedInbox": #(remoteUrl + '/inbox') - }, - "icon": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Destroyer_Vozbuzhdenyy.jpg/320px-Destroyer_Vozbuzhdenyy.jpg" - }, - "image": { - "type": "Image", - "mediaType": "image/jpeg", - "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg/320px-Views_of_Mount_Fuji_from_%C5%8Cwakudani_20211202.jpg" - } -} - - """ - * set req.req[] = '/users/' + username - * def response = person - - Scenario: pathMatches('/internal-assertion-api/requests') && methodIs('get') - * def response = req - - Scenario: pathMatches('/internal-assertion-api/requests/deleteAll') && methodIs('post') - * set req.req = [] - * def responseStatus = 200 diff --git a/hideout-core/src/e2eTest/resources/karate-config.js b/hideout-core/src/e2eTest/resources/karate-config.js deleted file mode 100644 index a83c2bb4..00000000 --- a/hideout-core/src/e2eTest/resources/karate-config.js +++ /dev/null @@ -1,30 +0,0 @@ -function fn() { - var env = karate.env; // get java system property 'karate.env' - karate.log('karate.env system property was:', env); - if (!env) { - env = 'dev'; // a custom 'intelligent' default - karate.log('karate.env set to "dev" as default.'); - } - let config; - if (env === 'test') { - let remotePort = karate.properties['karate.remotePort'] || '8081' - config = { - baseUrl: 'https://test-hideout.usbharu.dev', - remoteUrl: 'http://localhost:' + remotePort - } - } else if (env === 'dev') { - let port = karate.properties['karate.port'] || '8080' - let remotePort = karate.properties['karate.remotePort'] || '8081' - config = { - baseUrl: 'http://localhost:' + port, - remoteUrl: 'http://localhost:' + remotePort - } - } else { - throw 'Unknown environment [' + env + '].' - } - // don't waste time waiting for a connection or if servers don't respond within 0,3 seconds - - karate.configure('connectTimeout', 1000); - karate.configure('readTimeout', 1000); - return config; -} diff --git a/hideout-core/src/e2eTest/resources/logback.xml b/hideout-core/src/e2eTest/resources/logback.xml deleted file mode 100644 index c21752ee..00000000 --- a/hideout-core/src/e2eTest/resources/logback.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - ./e2eTest.log - - UTF-8 - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n - - - - - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n - - - - - - - - - - diff --git a/hideout-core/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature b/hideout-core/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature deleted file mode 100644 index af203344..00000000 --- a/hideout-core/src/e2eTest/resources/oauth2/Oauth2LoginTest.feature +++ /dev/null @@ -1,95 +0,0 @@ -Feature: OAuth2 Login Test - - Background: - * url baseUrl - * configure driver = { type: 'chrome',start: true, headless: true, showDriverLog: true, addOptions: [ '--headless=new' ] } - - Scenario: スコープwrite readã‚’æŒã£ãŸãƒˆãƒ¼ã‚¯ãƒ³ã®ä½œæˆ - - * def apps = - """ - { - "client_name": "oauth2-test-client-1", - "redirect_uris": "https://usbharu.dev", - "scopes": "write read" - } - """ - - Given path '/api/v1/apps' - And request apps - When method post - Then status 200 - - * def client_id = response.client_id - * def client_secret = response.client_secret - - * def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://usbharu.dev&client_id=' + client_id + '&scope=write%20read' - - Given driver authorizeEndpoint - And driver.input('#username','test-user') - And driver.input('#password','password') - - When driver.submit().click('body > div > form > button') - Then driver.waitForUrl(authorizeEndpoint + "&continue") - And driver.click('#read') - And driver.click('#write') - - When driver.submit().click('#submit-consent') - Then driver.waitUntil("location.host == 'usbharu.dev'") - - * def code = script("new URLSearchParams(document.location.search).get('code')") - - Given path '/oauth/token' - And form field client_id = client_id - And form field client_secret = client_secret - And form field redirect_uri = 'https://usbharu.dev' - And form field grant_type = 'authorization_code' - And form field code = code - And form field scope = 'write read' - When method post - Then status 200 - - Scenario: スコープread:statuses write:statusesã‚’æŒã£ãŸãƒˆãƒ¼ã‚¯ãƒ³ã®ä½œæˆ - - * def apps = - """ - { - "client_name": "oauth2-test-client-2", - "redirect_uris": "https://usbharu.dev", - "scopes": "read:statuses write:statuses" - } - """ - - Given path '/api/v1/apps' - And request apps - When method post - Then status 200 - - * def client_id = response.client_id - * def client_secret = response.client_secret - - * def authorizeEndpoint = baseUrl + '/oauth/authorize?response_type=code&redirect_uri=https://usbharu.dev&client_id=' + client_id + '&scope=read:statuses+write:statuses' - - Given driver authorizeEndpoint - And driver.input('#username','test-user') - And driver.input('#password','password') - - When driver.submit().click('body > div > form > button') - Then driver.waitForUrl(authorizeEndpoint + "&continue") - And driver.click('/html/body/div/div[4]/div/form/div[1]/input') - And driver.click('/html/body/div/div[4]/div/form/div[2]/input') - - When driver.submit().click('#submit-consent') - Then driver.waitUntil("location.host == 'usbharu.dev'") - - * def code = script("new URLSearchParams(document.location.search).get('code')") - - Given path '/oauth/token' - And form field client_id = client_id - And form field client_secret = client_secret - And form field redirect_uri = 'https://usbharu.dev' - And form field grant_type = 'authorization_code' - And form field code = code - And form field scope = 'write read' - When method post - Then status 200 diff --git a/hideout-core/src/e2eTest/resources/oauth2/user.sql b/hideout-core/src/e2eTest/resources/oauth2/user.sql deleted file mode 100644 index 4184f284..00000000 --- a/hideout-core/src/e2eTest/resources/oauth2/user.sql +++ /dev/null @@ -1,51 +0,0 @@ -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) -VALUES (1730415786666758144, 'test-user', 'localhost', 'Im test user.', 'THis account is test user.', - 'http://localhost/users/test-user/inbox', - 'http://localhost/users/test-user/outbox', 'http://localhost/users/test-user', - '-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi4mifRg6huAIn6DXk3Vn -5tkRC0AO32ZJvczwXr9xDj4HJvrSUHBAxIwwIeuCceAYtiuZk4JmEKydeB6SRkoO -Nty93XZXS1SMmiHCvWOY5YlpnfFU1kLqW3fkXcLNls4XmzujLt1i2sT8mYkENAsP -h6K4SRtmktOVYZOWcVEcfLGKbJvaDD/+lKikNC1XCouylfGV/bA/FPY5vuI+7cdM -Mjana28JdiWlPWSdzcxtCSgN+nGWPjk2WWm8K+wK2zXqMxA0U0p4odyyILBGALxX -zMqObIQvpwPh/t+b6ohem4eq70/0/SwDhd+IzHkT3x4UzG1oxSQS/juPkO7uuS8p -uwIDAQAB ------END PUBLIC KEY----- -', - '-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLiaJ9GDqG4Aif -oNeTdWfm2RELQA7fZkm9zPBev3EOPgcm+tJQcEDEjDAh64Jx4Bi2K5mTgmYQrJ14 -HpJGSg423L3ddldLVIyaIcK9Y5jliWmd8VTWQupbd+Rdws2WzhebO6Mu3WLaxPyZ -iQQ0Cw+HorhJG2aS05Vhk5ZxURx8sYpsm9oMP/6UqKQ0LVcKi7KV8ZX9sD8U9jm+ -4j7tx0wyNqdrbwl2JaU9ZJ3NzG0JKA36cZY+OTZZabwr7ArbNeozEDRTSnih3LIg -sEYAvFfMyo5shC+nA+H+35vqiF6bh6rvT/T9LAOF34jMeRPfHhTMbWjFJBL+O4+Q -7u65Lym7AgMBAAECggEADJLa7v3LbFLsxAGY22NFeRJPTF252VycwXshn9ANbnSd -bWBFqlTrKSrevXe82ekRIP09ygKCkvcS+3t5v9a1gDEU9MtQo2ubfdoT87/xS6G9 -wCs6c1I1Twe3LtG6d9/bVbQiiLsPSNpeTrF/jPcAL780bvYGoK1rNQ85C7383Kl6 -1nwZCD0itjkmzbO0nGMRCduW46OdQKiOMuEC7z0zwynH3cK3wGvdlKyLG4L3pPZm -1/Uz7AZTieqSCjSgcgmaut7dmS49e3j8ujfb3wcKscfHoofyqNWsW1xyU1WytO9a -QLh9wlqfvGlfwQWkY6z6uFmc4XfRVZSC8nic4cAW3QKBgQC4PYbR5AuylDcfc6Am -jpL5mcF6qEMnEPgnL9z5VvuLs1f/JEyx5VgzQreDOKc1KOxDX7Xhok4gpvIJv1fi -zimviszEmIpHdPvgS7mP2hu42bSIjwVaXpny5aEEZbB6HQ9pGDW/MSsgmb6x31Kx -o+sslpqf9cpalI35UPtkNaEJNwKBgQDB4tVUQ5gGPKllEfCN64B/B7wodWr5cUNU -UpUXdFPCu+HXnRen6GKLo+25wmCUGtcIuvCY1Xm+tL0Z7jrI+oOD4CL9ob7BJrPF -XCq0jUhaEzWFGp1SOa6n+35fWPkCfG4EwfsK8+PWoZsZc1eykMxIJmBln3vufuHz -qybfhy0VnQKBgD2tAxvyXmQar9VMjLk7k0IRUa6w80H5sUjVAgFKOA0NLZEQ4sfO -wdbvJ6W66mamW2k2ehmdjs/pcy8GKfKYF2ZXbbMGaYwAQm1UjDr2xb78yi3Iyv70 -mk6wxlVFgW1vmwAQhbWKTSitryO2YeVrvUeA5yRTULk/78Mdc/qY5V7DAoGAAu3I -RzOWMlHsRSiWN66dDE4zm3DaotYBLF7q/aW2NjTcXoNy/ghWpMFfL/UtvE8DfJBG -XiirZCQazy94F90g63cRUD+HQCezg4G2629O7n1ny5DxW3Kfns3/xLT1XgI/Lzc2 -8Z1pja53R1Ukt//T9isOPbrBBoNIKoQlXC8QkUkCgYEAsib3uOMAIOJab5jc8FSj -VG+Cg2H63J5DgUUwx2Y0DPENugdGyYzCDMVPBNaB0Ru1SpqbUjgqh+YHynunSVeu -hDXMOteeyeVHUGw8mvcCEt53uRYVNW/rzXTMqfLVxbsJZHCsJBtFpwcgD2w4NjS2 -Ja15+ZWbOA4vJA9pOh3x4XM= ------END PRIVATE KEY----- -', 1701398248417, - 'http://localhost/users/test-user#pubkey', 'http://localhost/users/test-user/following', - 'http://localhost/users/test-users/followers', 0, false, 0, 0, 0, null); - -insert into user_details (actor_id, password, auto_accept_followee_follow_request) -values ( 1730415786666758144 - , '$2a$10$/mWC/n7nC7X3l9qCEOKnredxne2zewoqEsJWTOdlKfg2zXKJ0F9Em', true) diff --git a/hideout-core/src/intTest/kotlin/activitypub/inbox/InboxTest.kt b/hideout-core/src/intTest/kotlin/activitypub/inbox/InboxTest.kt deleted file mode 100644 index 40f9ffdf..00000000 --- a/hideout-core/src/intTest/kotlin/activitypub/inbox/InboxTest.kt +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package activitypub.inbox - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.util.Base64Util -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.TestConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.http.MediaType -import org.springframework.security.test.context.support.WithAnonymousUser -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext -import util.TestTransaction -import util.WithMockHttpSignature -import java.security.MessageDigest -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -class InboxTest { - - @Autowired - @Qualifier("http") - private lateinit var dateTimeFormatter: DateTimeFormatter - - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(springSecurity()) - .build() - } - - @Test - @WithAnonymousUser - fun `匿åã§inboxã«POSTã—ãŸã‚‰401`() { - mockMvc - .post("/inbox") { - content = "{}" - contentType = MediaType.APPLICATION_JSON - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header( - "Digest", - "SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray())) - ) - } - .asyncDispatch() - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithMockHttpSignature - fun 有効ãªHttpSignatureã§POSTã—ãŸã‚‰202() { - mockMvc - .post("/inbox") { - content = "{}" - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header( - "Digest", - "SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray())) - ) - } - .asyncDispatch() - .andExpect { status { isAccepted() } } - } - - @Test - @WithAnonymousUser - fun `匿åã§user-inboxã«POSTã—ãŸã‚‰401`() { - mockMvc - .post("/users/hoge/inbox") { - content = "{}" - contentType = MediaType.APPLICATION_JSON - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header( - "Digest", - "SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray())) - ) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithMockHttpSignature - fun 有効ãªHttpSignaturesã§POSTã—ãŸã‚‰202() { - mockMvc - .post("/users/hoge/inbox") { - content = "{}" - contentType = MediaType.APPLICATION_JSON - header("Signature", "a") - header("Host", "example.com") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - header( - "Digest", - "SHA-256=" + Base64Util.encode(MessageDigest.getInstance("SHA-256").digest("{}".toByteArray())) - ) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isAccepted() } } - } - - @TestConfiguration - class Configuration { - @Bean - fun testTransaction() = TestTransaction - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/activitypub/note/NoteTest.kt b/hideout-core/src/intTest/kotlin/activitypub/note/NoteTest.kt deleted file mode 100644 index 62d7d3ce..00000000 --- a/hideout-core/src/intTest/kotlin/activitypub/note/NoteTest.kt +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package activitypub.note - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType -import org.springframework.security.test.context.support.WithAnonymousUser -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext -import util.WithHttpSignature -import util.WithMockHttpSignature -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -class NoteTest { - private lateinit var mockMvc: MockMvc - - @Autowired - private lateinit var context: WebApplicationContext - - @Autowired - @Qualifier("http") - private lateinit var dateTimeFormatter: DateTimeFormatter - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build() - } - - @Test - @WithAnonymousUser - @Sql("/sql/note/匿åã§public投稿をå–å¾—ã§ãã‚‹.sql") - fun `匿åã§public投稿をå–å¾—ã§ãã‚‹`() { - mockMvc - .get("/users/test-user/posts/1234") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.to") { value("https://www.w3.org/ns/activitystreams#Public") } } - .andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } } - } - - @Test - @Sql("/sql/note/匿åã§unlisted投稿をå–å¾—ã§ãã‚‹.sql") - @WithAnonymousUser - fun 匿åã§unlisted投稿をå–å¾—ã§ãã‚‹() { - mockMvc - .get("/users/test-user2/posts/1235") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user2/followers") } } - .andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } } - } - - @Test - @Transactional - @WithAnonymousUser - @Sql("/sql/note/匿åã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404.sql") - fun 匿åã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404() { - mockMvc - .get("/users/test-user2/posts/1236") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andExpect { status { isNotFound() } } - } - - @Test - @WithAnonymousUser - fun 匿åã§direct投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404() { - mockMvc - .get("/users/test-user2/posts/1236") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andExpect { status { isNotFound() } } - } - - @Test - @Sql("/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹.sql") - @WithHttpSignature(keyId = "https://follower.example.com/users/test-user5#pubkey") - fun HttpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹() { - mockMvc - .get("/users/test-user4/posts/1237") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.to") { value("https://www.w3.org/ns/activitystreams#Public") } } - .andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } } - } - - @Test - @Sql("/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹.sql") - @WithHttpSignature(keyId = "https://follower.example.com/users/test-user7#pubkey") - fun httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹() { - mockMvc - .get("/users/test-user6/posts/1238") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user6/followers") } } - .andExpect { jsonPath("\$.cc") { value("https://www.w3.org/ns/activitystreams#Public") } } - } - - @Test - @Sql("/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹.sql") - @WithHttpSignature(keyId = "https://follower.example.com/users/test-user9#pubkey") - fun httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹() { - mockMvc - .get("/users/test-user8/posts/1239") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.to") { value("https://example.com/users/test-user8/followers") } } - .andExpect { jsonPath("\$.cc") { value("https://example.com/users/test-user8/followers") } } - - } - - @Test - @Sql("/sql/note/リプライã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹.sql") - @WithMockHttpSignature - fun リプライã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹() { - mockMvc - .get("/users/test-user10/posts/1241") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.inReplyTo") { value("https://example.com/users/test-user10/posts/1240") } } - } - - @Test - @Sql("/sql/note/メディア付ã投稿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹.sql") - @WithMockHttpSignature - fun メディア付ã投稿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹() { - mockMvc - .get("/users/test-user10/posts/1242") { - accept(MediaType("application", "activity+json")) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { content { contentType("application/activity+json") } } - .andExpect { jsonPath("\$.type") { value("Note") } } - .andExpect { jsonPath("\$.attachment") { isArray() } } - .andExpect { jsonPath("\$.attachment[0].type") { value("Document") } } - .andExpect { jsonPath("\$.attachment[0].url") { value("https://example.com/media/test-media.png") } } - .andExpect { jsonPath("\$.attachment[1].type") { value("Document") } } - .andExpect { jsonPath("\$.attachment[1].url") { value("https://example.com/media/test-media2.png") } } - } - - @Test - fun signatureヘッダーãŒã‚ã‚‹ã®ã«hostヘッダーãŒãªã„ã¨401() { - mockMvc - .get("/users/test-user10/posts/9999") { - accept(MediaType("application", "activity+json")) - header("Signature", "a") - header("Date", ZonedDateTime.now().format(dateTimeFormatter)) - - } - .andExpect { status { isUnauthorized() } } - } - - @Test - fun signatureヘッダーãŒã‚ã‚‹ã®ã«dateヘッダーãŒãªã„ã¨401() { - mockMvc - .get("/users/test-user10/posts/9999") { - accept(MediaType("application", "activity+json")) - header("Signature", "a") - header("Host", "example.com") - } - .andExpect { status { isUnauthorized() } } - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt b/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt deleted file mode 100644 index e7532b18..00000000 --- a/hideout-core/src/intTest/kotlin/activitypub/webfinger/WebFingerTest.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package activitypub.webfinger - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.TestConfiguration -import org.springframework.context.annotation.Bean -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.transaction.annotation.Transactional -import util.TestTransaction -import java.net.URL - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -class WebFingerTest { - @Autowired - private lateinit var mockMvc: MockMvc - - @Test - @Sql("/sql/test-user.sql") - - fun `webfinger 存在ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’å–å¾—`() { - mockMvc - .get("/.well-known/webfinger?resource=acct:test-user@example.com") - .andExpect { status { isOk() } } - .andExpect { header { string("Content-Type", "application/json") } } - .andExpect { - jsonPath("\$.subject") { - value("acct:test-user@example.com") - } - } - .andExpect { - jsonPath("\$.links[0].rel") { - value("self") - } - } - .andExpect { - jsonPath("\$.links[0].href") { value("https://example.com/users/test-user") } - } - .andExpect { - jsonPath("\$.links[0].type") { - value("application/activity+json") - } - } - } - - @Test - fun `webfinger 存在ã—ãªã„ユーザーã«404`() { - mockMvc - .get("/.well-known/webfinger?resource=acct:invalid-user-notfound-afdjashfal@example.com") - .andExpect { status { isNotFound() } } - } - - @Test - fun `webfinger ä¸æ­£ãªãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯400`() { - mockMvc - .get("/.well-known/webfinger?res=acct:test") - .andExpect { status { isBadRequest() } } - } - - @Test - fun `webfinger acctã®ãƒ‘ースãŒå‡ºæ¥ãªãã¦ã‚‚400`() { - mockMvc - .get("/.well-known/webfinger?resource=acct:@a@b@c@d") - .andExpect { status { isBadRequest() } } - } - - @TestConfiguration - class Configuration { - @Bean - fun url(): URL { - return URL("https://example.com") - } - - @Bean - fun testTransaction(): Transaction = TestTransaction - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt deleted file mode 100644 index baf0a42a..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("SpringJavaInjectionPointsAutowiringInspection") - -package mastodon.account - -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.domain.mastodon.model.generated.Status -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@Suppress("NonAsciiCharacters") -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/accounts/test-accounts-statuses.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class AccountApiPaginationTest { - @Suppress("SpringJavaInjectionPointsAutowiringInspection") - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - @Test - fun `apiV1AccountsIdStatusesGet 投稿をå–å¾—ã§ãã‚‹`() { - val content = mockMvc - .get("/api/v1/accounts/1/statuses"){ - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { header { string("Link","; rel=\"next\", ; rel=\"prev\"") } } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value.first().id).isEqualTo("100") - assertThat(value.last().id).isEqualTo("81") - assertThat(value).size().isEqualTo(20) - } - - @Test - fun `apiV1AccountsIdStatusesGet çµæžœãŒ0件ã®ã¨ãã¯LinkヘッダーãŒãªã„`() { - val content = mockMvc - .get("/api/v1/accounts/1/statuses?min_id=100"){ - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { header { doesNotExist("Link") } } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - - assertThat(value).isEmpty() - } - - @Test - fun `apiV1AccountsIdStatusesGet maxIdを指定ã—ã¦å–å¾—`() { - val content = mockMvc - .get("/api/v1/accounts/1/statuses?max_id=100"){ - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { header { string("Link","; rel=\"next\", ; rel=\"prev\"") } } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value.first().id).isEqualTo("99") - assertThat(value.last().id).isEqualTo("80") - assertThat(value).size().isEqualTo(20) - } - - @Test - fun `apiV1AccountsIdStatusesGet minIdを指定ã—ã¦å–å¾—`() { - val content = mockMvc - .get("/api/v1/accounts/1/statuses?min_id=1"){ - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { header { string("Link","; rel=\"next\", ; rel=\"prev\"") } } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value.first().id).isEqualTo("21") - assertThat(value.last().id).isEqualTo("2") - assertThat(value).size().isEqualTo(20) - } - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - - } -} \ No newline at end of file diff --git a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt b/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt deleted file mode 100644 index d48c72d8..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/account/AccountApiTest.kt +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.account - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.context.support.WithAnonymousUser -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class AccountApiTest { - - @Autowired - private lateinit var followerQueryServiceImpl: FollowerQueryServiceImpl - - @Autowired - private lateinit var actorRepository: ActorRepository - - - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(springSecurity()) - .build() - } - - @Test - fun `apiV1AccountsVerifyCredentialsGetã«readã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/verify_credentials") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsVerifyCredentialsGetã«read_accountsã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/verify_credentials") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:accounts"))) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - } - - @Test - @WithAnonymousUser - fun apiV1AccountsVerifyCredentialsGetã«åŒ¿åã§ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã¨401() { - mockMvc - .get("/api/v1/accounts/verify_credentials") - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithAnonymousUser - fun apiV1AccountsPostã«åŒ¿åã§POSTã—ãŸã‚‰ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’作æˆã§ãã‚‹() = runTest { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("username", "api-test-user-1") - param("password", "very-secure-password") - param("email", "test@example.com") - param("agreement", "true") - param("locale", "") - with(jwt()) - with(csrf()) - } - .asyncDispatch() - .andExpect { status { isFound() } } - - actorRepository.findByNameAndDomain("api-test-user-1", "example.com") - } - - @Test - @WithAnonymousUser - fun apiV1AccountsPostã§å¿…須パラメーター以外をçœç•¥ã—ã¦ã‚‚作æˆã§ãã‚‹() = runTest { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("username", "api-test-user-2") - param("password", "very-secure-password") - with(jwt()) - with(csrf()) - } - .asyncDispatch() - .andExpect { status { isFound() } } - - actorRepository.findByNameAndDomain("api-test-user-2", "example.com") - } - - @Test - @WithAnonymousUser - fun apiV1AccountsPostã§usernameパラメーターをçœç•¥ã—ãŸã‚‰400() = runTest { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("password", "api-test-user-3") - with(csrf()) - with(jwt()) - } - .andDo { print() } - .andExpect { status { isUnprocessableEntity() } } - } - - @Test - @WithAnonymousUser - fun apiV1AccountsPostã§passwordパラメーターをçœç•¥ã—ãŸã‚‰400() = runTest { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("username", "api-test-user-4") - with(csrf()) - with(jwt()) - } - .andExpect { status { isUnprocessableEntity() } } - } - - @Test - @Disabled("JSONã§ã‚‚作れるよã†ã«ã™ã‚‹ãŸã‚") - @WithAnonymousUser - fun apiV1AccountsPostã§JSONã§ä½œã‚ã†ã¨ã—ã¦ã‚‚400() { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_JSON - content = """{"username":"api-test-user-5","password":"very-very-secure-password"}""" - with(csrf()) - } - .andExpect { status { isUnsupportedMediaType() } } - } - - @Test - @WithAnonymousUser - fun apiV1AccountsPostã«CSRFトークンã¯å¿…è¦() { - mockMvc - .post("/api/v1/accounts") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("username", "api-test-user-2") - param("password", "very-secure-password") - } - .andExpect { status { isForbidden() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AccountsIdGet 匿åã§ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…報をå–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/1") - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdFollowPost write_follows権é™ã§POSTã§ãƒ•ã‚©ãƒ­ãƒ¼ã§ãã‚‹`() { - mockMvc - .post("/api/v1/accounts/2/follow") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:follows"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdFollowPost write権é™ã§POSTã§ãƒ•ã‚©ãƒ­ãƒ¼ã§ãã‚‹`() { - mockMvc - .post("/api/v1/accounts/2/follow") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdFollowPost read権é™ã§ã ã¨403`() { - mockMvc - .post("/api/v1/accounts/2/follow") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .andExpect { status { isForbidden() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AAccountsIdFollowPost 匿åã ã¨401`() { - mockMvc - .post("/api/v1/accounts/2/follow") { - contentType = MediaType.APPLICATION_JSON - with(csrf()) - } - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AAccountsIdFollowPost 匿åã®å ´åˆé€šå¸¸csrfトークンã¯æŒã£ã¦ãªã„ã®ã§403`() { - mockMvc - .post("/api/v1/accounts/2/follow") { - contentType = MediaType.APPLICATION_JSON - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1AccountsRelationshipsGet 匿åã ã¨401`() { - mockMvc - .get("/api/v1/accounts/relationships") - .andExpect { status { isUnauthorized() } } - } - - @Test - fun `apiV1AccountsRelationshipsGet read_follows権é™ã‚’æŒã£ã¦ã„ãŸã‚‰å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/relationships") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:follows"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsRelationshipsGet read権é™ã‚’æŒã£ã¦ã„ãŸã‚‰å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/relationships") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsRelationshipsGet write権é™ã ã¨403`() { - mockMvc - .get("/api/v1/accounts/relationships") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .andExpect { status { isForbidden() } } - } - - @Test - @Sql("/sql/accounts/apiV1AccountsIdFollowPost フォローã§ãã‚‹.sql") - fun `apiV1AccountsIdFollowPost フォローã§ãã‚‹`() = runTest { - mockMvc - .post("/api/v1/accounts/3733363/follow") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "37335363") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - - val alreadyFollow = followerQueryServiceImpl.alreadyFollow(3733363, 37335363) - - assertThat(alreadyFollow).isTrue() - } - - @Test - fun `apiV1AccountsIdMutePost write権é™ã§ãƒŸãƒ¥ãƒ¼ãƒˆã§ãã‚‹`() { - mockMvc - .post("/api/v1/accounts/2/mute") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdMutePost write_mutes権é™ã§ãƒŸãƒ¥ãƒ¼ãƒˆã§ãã‚‹`() { - mockMvc - .post("/api/v1/accounts/2/mute") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:mutes"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdMutePost read権é™ã ã¨403`() = runTest { - mockMvc - .post("/api/v1/accounts/2/mute") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .andExpect { status { isForbidden() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AccountsIdMutePost 匿åã ã¨401`() = runTest { - mockMvc - .post("/api/v1/accounts/2/mute") { - contentType = MediaType.APPLICATION_JSON - with(csrf()) - } - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AccountsIdMutePost csrfトークンãŒãªã„ã¨403`() = runTest { - mockMvc - .post("/api/v1/accounts/2/mute") { - contentType = MediaType.APPLICATION_JSON - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1AccountsIdUnmutePost write権é™ã§ã‚¢ãƒ³ãƒŸãƒ¥ãƒ¼ãƒˆã§ãã‚‹`() { - mockMvc - .post("/api/v1/accounts/2/unmute") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdUnmutePost write_mutes権é™ã§ã‚¢ãƒ³ãƒŸãƒ¥ãƒ¼ãƒˆã§ãã‚‹`() { - mockMvc - .post("/api/v1/accounts/2/unmute") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:mutes"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1AccountsIdUnmutePost read権é™ã ã¨403`() = runTest { - mockMvc - .post("/api/v1/accounts/2/unmute") { - contentType = MediaType.APPLICATION_JSON - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .andExpect { status { isForbidden() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AccountsIdUnmutePost 匿åã ã¨401`() = runTest { - mockMvc - .post("/api/v1/accounts/2/unmute") { - contentType = MediaType.APPLICATION_JSON - with(csrf()) - } - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AccountsIdUnmutePost csrfトークンãŒãªã„ã¨403`() = runTest { - mockMvc - .post("/api/v1/accounts/2/unmute") { - contentType = MediaType.APPLICATION_JSON - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1MutesGet read権é™ã§ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るアカウント一覧をå–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/mutes") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1MutesGet read_mutes権é™ã§ãƒŸãƒ¥ãƒ¼ãƒˆã—ã¦ã„るアカウント一覧をå–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/mutes") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:mutes"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1MutesGet write権é™ã ã¨403`() { - mockMvc - .get("/api/v1/mutes") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .andExpect { status { isForbidden() } } - } - - @Test - @WithAnonymousUser - fun `apiV1MutesGet 匿åã ã¨401`() { - mockMvc - .get("/api/v1/mutes") - .andExpect { status { isUnauthorized() } } - } - - @Test - fun `apiV1AccountsIdStatusesGet read権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/1/statuses") - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - @WithAnonymousUser - fun `apiV1AccountsIdStatusesGet 匿åã§ã‚‚public投稿をå–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/accounts/1/statuses") - .asyncDispatch() - .andExpect { status { isOk() } } - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt b/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt deleted file mode 100644 index 8b8ff123..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/apps/AppTest.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.apps - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.flywaydb.core.Flyway -import org.jetbrains.exposed.sql.selectAll -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient -import org.springframework.security.test.context.support.WithAnonymousUser -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -class AppTest { - - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - @Test - @WithAnonymousUser - fun apiV1AppsPostã«formã§åŒ¿åã§appを作æˆã§ãã‚‹() { - mockMvc - .post("/api/v1/apps") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("client_name", "test-client") - param("redirect_uris", "https://example.com") - param("scopes", "write read") - param("website", "https://example.com") - } - .asyncDispatch() - .andExpect { status { isOk() } } - - - val app = RegisteredClient - .selectAll().where { RegisteredClient.clientName eq "test-client" } - .single() - - assertThat(app[RegisteredClient.clientName]).isEqualTo("test-client") - assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com") - assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write") - } - - @Test - @WithAnonymousUser - fun apiV1AppsPostã«jsonã§åŒ¿åã§appを作æˆã§ãã‚‹() { - mockMvc - .post("/api/v1/apps") { - contentType = MediaType.APPLICATION_JSON - content = """{ - "client_name": "test-client-2", - "redirect_uris": "https://example.com", - "scopes": "write read", - "website": "https;//example.com" -}""" - } - .asyncDispatch() - .andExpect { status { isOk() } } - - val app = RegisteredClient - .selectAll().where { RegisteredClient.clientName eq "test-client-2" } - .single() - - assertThat(app[RegisteredClient.clientName]).isEqualTo("test-client-2") - assertThat(app[RegisteredClient.redirectUris]).isEqualTo("https://example.com") - assertThat(app[RegisteredClient.scopes]).isEqualTo("read,write") - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt b/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt deleted file mode 100644 index 2732663e..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/filter/FilterTest.kt +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.filter - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.domain.mastodon.model.generated.FilterKeywordsPostRequest -import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequest -import dev.usbharu.hideout.domain.mastodon.model.generated.FilterPostRequestKeyword -import dev.usbharu.hideout.domain.mastodon.model.generated.V1FilterPostRequest -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.delete -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", "/sql/filter/test-filter.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class FilterTest { - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - @Test - fun `apiV2FiltersPost write権é™ã§è¿½åŠ ã§ãã‚‹`() { - mockMvc - .post("/api/v2/filters") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - FilterPostRequest( - title = "mute test", - context = listOf(FilterPostRequest.Context.home, FilterPostRequest.Context.public), - filterAction = FilterPostRequest.FilterAction.warn, - expiresIn = null, - keywordsAttributes = listOf( - FilterPostRequestKeyword( - keyword = "hoge", - wholeWord = false, - regex = true - ) - ) - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { - content { - jsonPath("$.keywords[0].keyword") { - value("hoge") - } - } - } - } - - @Test - fun `apiV2FiltersPost write_filters権é™ã§è¿½åŠ ã§ãã‚‹`() { - mockMvc - .post("/api/v2/filters") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - FilterPostRequest( - title = "mute test", - context = listOf(FilterPostRequest.Context.home, FilterPostRequest.Context.public), - filterAction = FilterPostRequest.FilterAction.warn, - expiresIn = null, - keywordsAttributes = listOf( - FilterPostRequestKeyword( - keyword = "fuga", - wholeWord = true, - regex = false - ) - ) - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - .andExpect { - content { - jsonPath("$.keywords[0].keyword") { - value("fuga") - } - } - } - } - - @Test - fun `apiV2FiltersPost read権é™ã§401`() { - mockMvc - .post("/api/v2/filters") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - FilterPostRequest( - title = "mute test", - context = listOf(FilterPostRequest.Context.home, FilterPostRequest.Context.public), - filterAction = FilterPostRequest.FilterAction.warn, - expiresIn = null, - keywordsAttributes = listOf( - FilterPostRequestKeyword( - keyword = "fuga", - wholeWord = true, - regex = false - ) - ) - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersGet read権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersGet read_filters権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersGet write権é™ã§401`() { - mockMvc - .get("/api/v2/filters") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersIdGet read権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - - @Test - fun `apiV2FiltersIdGet read_filters権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersIdGet write権é™ã§401`() { - mockMvc - .get("/api/v2/filters/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersFilterIdKeywordsGet read権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/1/keywords") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersFilterIdKeywordsGet read_filters権é™ã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/1/keywords") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersFilterIdKeywordsGet writeã§403`() { - mockMvc - .get("/api/v2/filters/1/keywords") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersFilterIdKeywordsPost writeã§è¿½åŠ ã§ãã‚‹`() { - mockMvc - .post("/api/v2/filters/1/keywords") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - FilterKeywordsPostRequest( - "hage", false, false - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersFilterIdKeywordsPost write_filtersã§è¿½åŠ ã§ãã‚‹`() { - mockMvc - .post("/api/v2/filters/1/keywords") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - FilterKeywordsPostRequest( - "hage", false, false - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersFilterIdKeywordsPost readã§403`() { - mockMvc - .post("/api/v2/filters/1/keywords") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - FilterKeywordsPostRequest( - "hage", false, false - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersKeywordsIdGet readã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/keywords/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersKeywordsIdGet read_filtersã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/keywords/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersKeywordsIdGet writeã ã¨403`() { - mockMvc - .get("/api/v2/filters/keywords/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersKeyowrdsIdDelete writeã§å‰Šé™¤ã§ãã‚‹`() = runTest { - mockMvc - .delete("/api/v2/filters/keywords/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersKeyowrdsIdDelete write_filtersã§å‰Šé™¤ã§ãã‚‹`() = runTest { - mockMvc - .delete("/api/v2/filters/keywords/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersKeyowrdsIdDelete readã§403`() = runTest { - mockMvc - .delete("/api/v2/filters/keywords/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersFilterIdStatuses readã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/1/statuses") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersFilterIdStatuses read_filtersã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/1/statuses") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersFilterIdStatuses writeã§403`() { - mockMvc - .get("/api/v2/filters/1/statuses") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersStatusesIdGet readã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/statuses/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersStatusesIdGet read_filtersã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v2/filters/statuses/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersStatusesIdGet writeã§403`() { - mockMvc - .get("/api/v2/filters/statuses/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV2FiltersStatusesIdDelete writeã§å‰Šé™¤ã§ãã‚‹`() { - mockMvc - .delete("/api/v2/filters/statuses/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersStatusesIdDelete write_filtersã§å‰Šé™¤ã§ãã‚‹`() { - mockMvc - .delete("/api/v2/filters/statuses/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV2FiltersStatusesIdDelete readã§403`() { - mockMvc - .delete("/api/v2/filters/statuses/1") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1FiltersGet readã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/filters") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersGet read_filtersã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/filters") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersGet writeã§403`() { - mockMvc - .get("/api/v1/filters") { - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1FiltersPost writeã§æ–°è¦ä½œæˆ`() { - mockMvc - .post("/api/v1/filters") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - V1FilterPostRequest( - phrase = "hoge", - context = listOf(V1FilterPostRequest.Context.home), - irreversible = false, - wholeWord = false, - expiresIn = null - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersPost write_filtersã§æ–°è¦ä½œæˆ`() { - mockMvc - .post("/api/v1/filters") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - V1FilterPostRequest( - phrase = "hoge", - context = listOf(V1FilterPostRequest.Context.home), - irreversible = false, - wholeWord = false, - expiresIn = null - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersPost readã§403`() { - mockMvc - .post("/api/v1/filters") { - contentType = MediaType.APPLICATION_JSON - content = ActivityPubConfig().objectMapper().writeValueAsString( - V1FilterPostRequest( - phrase = "hoge", - context = listOf(V1FilterPostRequest.Context.home), - irreversible = false, - wholeWord = false, - expiresIn = null - ) - ) - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1FiltersIdGet readã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/filters/1") { - with( - jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersIdGet read_filtersã§å–å¾—ã§ãã‚‹`() { - mockMvc - .get("/api/v1/filters/1") { - with( - jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersIdGet writeã§403`() { - mockMvc - .get("/api/v1/filters/1") { - with( - jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - fun `apiV1FiltersIdDelete writeã§å‰Šé™¤ã§ãã‚‹`() { - mockMvc - .delete("/api/v1/filters/1") { - with( - jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersIdDelete write_filtersã§å‰Šé™¤ã§ãã‚‹`() { - mockMvc - .delete("/api/v1/filters/1") { - with( - jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:filters")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1FiltersIdDelete readã§403`() { - mockMvc - .delete("/api/v1/filters/1") { - with( - jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} \ No newline at end of file diff --git a/hideout-core/src/intTest/kotlin/mastodon/media/MediaTest.kt b/hideout-core/src/intTest/kotlin/mastodon/media/MediaTest.kt deleted file mode 100644 index 77f23281..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/media/MediaTest.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.media - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.service.media.MediaDataStore -import dev.usbharu.hideout.core.service.media.MediaSaveRequest -import dev.usbharu.hideout.core.service.media.SuccessSavedMedia -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.whenever -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.mock.mockito.MockBean -import org.springframework.mock.web.MockMultipartFile -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.multipart -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class MediaTest { - - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - - @MockBean - private lateinit var mediaDataStore: MediaDataStore - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - @Test - fun メディアをアップロードã§ãã‚‹() = runTest { - whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "a", "a")) - - mockMvc - .multipart("/api/v1/media") { - - file( - MockMultipartFile( - "file", - "400x400.png", - "image/png", - String.javaClass.classLoader.getResourceAsStream("media/400x400.png") - ) - ) - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun write_mediaスコープã§ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’アップロードã§ãã‚‹() = runTest { - whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "b", "b")) - - mockMvc - .multipart("/api/v1/media") { - - file( - MockMultipartFile( - "file", - "400x400.png", - "image/png", - String.javaClass.classLoader.getResourceAsStream("media/400x400.png") - ) - ) - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:media"))) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun 権é™ãŒãªã„ã¨403() = runTest { - whenever(mediaDataStore.save(any())).doReturn(SuccessSavedMedia("", "", "")) - - mockMvc - .multipart("/api/v1/media") { - - file( - MockMultipartFile( - "file", - "400x400.png", - "image/png", - String.javaClass.classLoader.getResourceAsStream("media/400x400.png") - ) - ) - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read"))) - } - .andExpect { status { isForbidden() } } - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } - -} diff --git a/hideout-core/src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt b/hideout-core/src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt deleted file mode 100644 index 7405caf6..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.notifications - -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@SpringBootTest(classes = [SpringApplication::class], properties = ["hideout.use-mongodb=false"]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/notification/test-mastodon_notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class ExposedNotificationsApiPaginationTest { - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - @Test - fun `通知をå–å¾—ã§ãã‚‹`() = runTest { - val content = mockMvc - .get("/api/v1/notifications") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - string( - "Link", - "; rel=\"next\", ; rel=\"prev\"" - ) - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value.first().id).isEqualTo("65") - assertThat(value.last().id).isEqualTo("26") - } - - @Test - fun maxIdを指定ã—ã¦é€šçŸ¥ã‚’å–å¾—ã§ãã‚‹() = runTest { - val content = mockMvc - .get("/api/v1/notifications?max_id=26") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - string( - "Link", - "; rel=\"next\", ; rel=\"prev\"" - ) - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value.first().id).isEqualTo("25") - assertThat(value.last().id).isEqualTo("1") - - } - - @Test - fun minIdを指定ã—ã¦é€šçŸ¥ã‚’å–å¾—ã§ãã‚‹() = runTest { - val content = mockMvc - .get("/api/v1/notifications?min_id=25") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - string( - "Link", - "; rel=\"next\", ; rel=\"prev\"" - ) - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value.first().id).isEqualTo("65") - assertThat(value.last().id).isEqualTo("26") - } - - @Test - fun çµæžœãŒ0件ã®ã¨ãã¯ãƒšãƒ¼ã‚¸ãƒãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ˜ãƒƒãƒ€ãƒ¼ãŒãªã„() = runTest { - val content = mockMvc - .get("/api/v1/notifications?max_id=1") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - doesNotExist("Link") - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - assertThat(value).size().isZero() - } - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} \ No newline at end of file diff --git a/hideout-core/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt b/hideout-core/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt deleted file mode 100644 index 0de15332..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.notifications - -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.domain.mastodon.model.generated.Notification -import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification -import dev.usbharu.hideout.mastodon.domain.model.NotificationType -import dev.usbharu.hideout.mastodon.infrastructure.mongorepository.MongoMastodonNotificationRepository -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext -import java.time.Instant - -@SpringBootTest(classes = [SpringApplication::class], properties = ["hideout.use-mongodb=true"]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class MongodbNotificationsApiPaginationTest { - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - @Test - fun `通知をå–å¾—ã§ãã‚‹`() = runTest { - val content = mockMvc - .get("/api/v1/notifications") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andDo { print() } - .andExpect { - header { - string( - "Link", - "; rel=\"next\", ; rel=\"prev\"" - ) - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - Assertions.assertThat(value.first().id).isEqualTo("65") - Assertions.assertThat(value.last().id).isEqualTo("26") - } - - @Test - fun maxIdを指定ã—ã¦é€šçŸ¥ã‚’å–å¾—ã§ãã‚‹() = runTest { - val content = mockMvc - .get("/api/v1/notifications?max_id=26") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - string( - "Link", - "; rel=\"next\", ; rel=\"prev\"" - ) - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - Assertions.assertThat(value.first().id).isEqualTo("25") - Assertions.assertThat(value.last().id).isEqualTo("1") - - } - - @Test - fun minIdを指定ã—ã¦é€šçŸ¥ã‚’å–å¾—ã§ãã‚‹() = runTest { - val content = mockMvc - .get("/api/v1/notifications?min_id=25") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - string( - "Link", - "; rel=\"next\", ; rel=\"prev\"" - ) - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - Assertions.assertThat(value.first().id).isEqualTo("65") - Assertions.assertThat(value.last().id).isEqualTo("26") - } - - @Test - fun çµæžœãŒ0件ã®ã¨ãã¯ãƒšãƒ¼ã‚¸ãƒãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ˜ãƒƒãƒ€ãƒ¼ãŒãªã„() = runTest { - val content = mockMvc - .get("/api/v1/notifications?max_id=1") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { - header { - doesNotExist("Link") - } - } - .andReturn() - .response - .contentAsString - - val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - - Assertions.assertThat(value).size().isZero() - } - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - companion object { - @JvmStatic - @BeforeAll - fun setupMongodb( - @Autowired mongoMastodonNotificationRepository: MongoMastodonNotificationRepository, - ) { - - mongoMastodonNotificationRepository.deleteAll() - - val notifications = (1..65).map { - MastodonNotification( - it.toLong(), - 1, - NotificationType.follow, - Instant.now(), - 2, - null, - null, - null - ) - } - - mongoMastodonNotificationRepository.saveAll(notifications) - } - - @JvmStatic - @AfterAll - fun dropDatabase( - @Autowired flyway: Flyway, - @Autowired mongodbMastodonNotificationRepository: MongoMastodonNotificationRepository, - @Autowired owlProducer: OwlProducer, - ) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - mongodbMastodonNotificationRepository.deleteAll() - } - } -} \ No newline at end of file diff --git a/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt b/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt deleted file mode 100644 index d3028a0d..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/status/StatusTest.kt +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.status - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji -import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji -import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.flywaydb.core.Flyway -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.selectAll -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.context.support.WithAnonymousUser -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.post -import org.springframework.test.web.servlet.put -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext -import java.time.Instant - -@SpringBootTest(classes = [SpringApplication::class]) -@AutoConfigureMockMvc -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/test-post.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -@Sql("/sql/test-custom-emoji.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class StatusTest { - - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - - @BeforeEach - fun setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - @Test - fun 投稿ã§ãã‚‹() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - content = """{"status":"hello"}""" - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun write_statusesスコープã§æŠ•ç¨¿ã§ãã‚‹() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - content = """{"status":"hello"}""" - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:statuses")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun 権é™ãŒãªã„ã¨403() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - content = """{"status":"hello"}""" - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .andExpect { status { isForbidden() } } - } - - @Test - @WithAnonymousUser - fun 匿åã ã¨401() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - content = """{"status":"hello"}""" - with(csrf()) - } - .andExpect { status { isUnauthorized() } } - } - - @Test - @WithAnonymousUser - fun 匿åã®å ´åˆé€šå¸¸ã¯csrfãŒç„¡ã„ã®ã§403() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - content = """{"status":"hello"}""" - } - .andExpect { status { isForbidden() } } - } - - @Test - fun formã§ã‚‚投稿ã§ãã‚‹() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_FORM_URLENCODED - param("status", "hello") - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write:statuses")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun in_reply_to_idを指定ã—ãŸã‚‰è¿”ä¿¡ã¨ã—ã¦å‡¦ç†ã•ã‚Œã‚‹() { - mockMvc - .post("/api/v1/statuses") { - contentType = MediaType.APPLICATION_JSON - //language=JSON - content = """{ - "status": "hello", - "in_reply_to_id": "1" -}""" - with( - jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")) - ) - } - .asyncDispatch() - .andDo { print() } - .andExpect { status { isOk() } } - .andExpect { jsonPath("\$.in_reply_to_id") { value("1") } } - } - - @Test - fun ユニコード絵文字をリアクションã§ãã‚‹() { - mockMvc - .put("/api/v1/statuses/1/emoji_reactions/😭") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .andDo { print() } - .asyncDispatch() - .andExpect { status { isOk() } } - - val reaction = - Reactions.selectAll().where { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single().toReaction() - assertThat(reaction.emoji).isEqualTo(UnicodeEmoji("😭")) - assertThat(reaction.postId).isEqualTo(1) - assertThat(reaction.actorId).isEqualTo(1) - } - - @Test - fun 存在ã—ãªã„絵文字ã¯ãƒ•ã‚©ãƒ¼ãƒ«ãƒãƒƒã‚¯ã•ã‚Œã‚‹() { - mockMvc - .put("/api/v1/statuses/1/emoji_reactions/hoge") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .andDo { print() } - .asyncDispatch() - .andExpect { status { isOk() } } - - val reaction = - Reactions.selectAll().where { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single().toReaction() - assertThat(reaction.emoji).isEqualTo(UnicodeEmoji("â¤")) - assertThat(reaction.postId).isEqualTo(1) - assertThat(reaction.actorId).isEqualTo(1) - } - - @Test - fun カスタム絵文字をリアクションã§ãã‚‹() { - mockMvc - .put("/api/v1/statuses/1/emoji_reactions/kotlin") { - with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write"))) - } - .andDo { print() } - .asyncDispatch() - .andExpect { status { isOk() } } - - val reaction = - Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.postId eq 1 and (Reactions.actorId eq 1) } - .single() - .toReaction() - assertThat(reaction.emoji).isEqualTo( - CustomEmoji( - 1, - "kotlin", - "example.com", - null, - "https://example.com/emojis/kotlin", - null, - Instant.ofEpochMilli(1704700290036) - ) - ) - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/mastodon/timelines/TimelineApiTest.kt b/hideout-core/src/intTest/kotlin/mastodon/timelines/TimelineApiTest.kt deleted file mode 100644 index 21719e85..00000000 --- a/hideout-core/src/intTest/kotlin/mastodon/timelines/TimelineApiTest.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mastodon.timelines - -import dev.usbharu.hideout.SpringApplication -import dev.usbharu.owl.producer.api.OwlProducer -import kotlinx.coroutines.runBlocking -import org.flywaydb.core.Flyway -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.test.context.support.WithAnonymousUser -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors -import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers -import org.springframework.test.context.jdbc.Sql -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder -import org.springframework.test.web.servlet.setup.MockMvcBuilders -import org.springframework.transaction.annotation.Transactional -import org.springframework.web.context.WebApplicationContext - -@SpringBootTest(classes = [SpringApplication::class]) -@Transactional -@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class TimelineApiTest { - @Autowired - private lateinit var context: WebApplicationContext - - private lateinit var mockMvc: MockMvc - - @BeforeEach - fun beforeEach() { - mockMvc = MockMvcBuilders.webAppContextSetup(context) - .apply(SecurityMockMvcConfigurers.springSecurity()) - .build() - } - - @Test - fun `apiV1TimelinesHomeGetã«readã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹`() { - mockMvc - .get("/api/v1/timelines/home") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1TimelinesHomeGetã«read statusesã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹`() { - mockMvc - .get("/api/v1/timelines/home") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:statuses")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - @WithAnonymousUser - fun apiV1TimelineHomeGetã«åŒ¿åã§ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã¨401() { - mockMvc - .get("/api/v1/timelines/home") - .andExpect { status { isUnauthorized() } } - } - - @Test - fun apiV1TimelinesPublicGetã«readã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹() { - mockMvc - .get("/api/v1/timelines/public") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - fun `apiV1TimelinesPublicGetã«read statusesã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹`() { - mockMvc - .get("/api/v1/timelines/public") { - with( - SecurityMockMvcRequestPostProcessors.jwt() - .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read:statuses")) - ) - } - .asyncDispatch() - .andExpect { status { isOk() } } - } - - @Test - @WithAnonymousUser - fun apiV1TimeinesPublicGetã«åŒ¿åã§ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹() { - mockMvc - .get("/api/v1/timelines/public") - .asyncDispatch() - .andExpect { status { isOk() } } - } - - companion object { - @JvmStatic - @AfterAll - fun dropDatabase(@Autowired flyway: Flyway, @Autowired owlProducer: OwlProducer) { - flyway.clean() - flyway.migrate() - runBlocking { - owlProducer.stop() - } - } - } -} diff --git a/hideout-core/src/intTest/kotlin/util/WithHttpSignature.kt b/hideout-core/src/intTest/kotlin/util/WithHttpSignature.kt deleted file mode 100644 index 64fd643f..00000000 --- a/hideout-core/src/intTest/kotlin/util/WithHttpSignature.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import org.springframework.core.annotation.AliasFor -import org.springframework.security.test.context.support.TestExecutionEvent -import org.springframework.security.test.context.support.WithSecurityContext -import java.lang.annotation.Inherited - -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) -@Retention(AnnotationRetention.RUNTIME) -@Inherited -@MustBeDocumented -@WithSecurityContext(factory = WithHttpSignatureSecurityContextFactory::class) -annotation class WithHttpSignature( - @get:AliasFor( - annotation = WithSecurityContext::class - ) val setupBefore: TestExecutionEvent = TestExecutionEvent.TEST_METHOD, - val keyId: String = "https://example.com/users/test-user#pubkey", - val url: String = "https://example.com/inbox", - val method: String = "GET" -) diff --git a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt b/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt deleted file mode 100644 index 7ec4aaa5..00000000 --- a/hideout-core/src/intTest/kotlin/util/WithHttpSignatureSecurityContextFactory.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import kotlinx.coroutines.runBlocking -import org.springframework.security.core.context.SecurityContext -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.test.context.support.WithSecurityContextFactory -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken -import java.net.URL - -class WithHttpSignatureSecurityContextFactory( - private val actorRepository: ActorRepository, - private val transaction: Transaction -) : WithSecurityContextFactory { - - private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy() - - override fun createSecurityContext(annotation: WithHttpSignature): SecurityContext = runBlocking { - val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken( - annotation.keyId, HttpRequest( - URL("https://example.com/inbox"), - HttpHeaders(mapOf()), HttpMethod.GET - ) - ) - val httpSignatureUser = transaction.transaction { - val findByKeyId = - actorRepository.findByKeyId(annotation.keyId) ?: throw UserNotFoundException.withKeyId(annotation.keyId) - HttpSignatureUser( - findByKeyId.name, - findByKeyId.domain, - findByKeyId.id, - true, - true, - mutableListOf() - ) - } - preAuthenticatedAuthenticationToken.details = httpSignatureUser - preAuthenticatedAuthenticationToken.isAuthenticated = true - val emptyContext = securityContextStrategy.createEmptyContext() - emptyContext.authentication = preAuthenticatedAuthenticationToken - return@runBlocking emptyContext - } - -} diff --git a/hideout-core/src/intTest/kotlin/util/WithMockHttpSignature.kt b/hideout-core/src/intTest/kotlin/util/WithMockHttpSignature.kt deleted file mode 100644 index 86392316..00000000 --- a/hideout-core/src/intTest/kotlin/util/WithMockHttpSignature.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import org.springframework.core.annotation.AliasFor -import org.springframework.security.test.context.support.TestExecutionEvent -import org.springframework.security.test.context.support.WithSecurityContext -import java.lang.annotation.Inherited - -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) -@Retention(AnnotationRetention.RUNTIME) -@Inherited -@MustBeDocumented -@WithSecurityContext(factory = WithMockHttpSignatureSecurityContextFactory::class) -annotation class WithMockHttpSignature( - @get:AliasFor( - annotation = WithSecurityContext::class - ) val setupBefore: TestExecutionEvent = TestExecutionEvent.TEST_METHOD, - val username: String = "test-user", - val domain: String = "example.com", - val keyId: String = "https://example.com/users/test-user#pubkey", - val id: Long = 1234L, - val url: String = "https://example.com/inbox", - val method: String = "GET" -) diff --git a/hideout-core/src/intTest/kotlin/util/WithMockHttpSignatureSecurityContextFactory.kt b/hideout-core/src/intTest/kotlin/util/WithMockHttpSignatureSecurityContextFactory.kt deleted file mode 100644 index 1114bdcd..00000000 --- a/hideout-core/src/intTest/kotlin/util/WithMockHttpSignatureSecurityContextFactory.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser -import dev.usbharu.httpsignature.common.HttpHeaders -import dev.usbharu.httpsignature.common.HttpMethod -import dev.usbharu.httpsignature.common.HttpRequest -import org.springframework.security.core.context.SecurityContext -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.test.context.support.WithSecurityContextFactory -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken -import java.net.URL - -class WithMockHttpSignatureSecurityContextFactory : - WithSecurityContextFactory { - - private val securityContextStrategy = SecurityContextHolder.getContextHolderStrategy() - - override fun createSecurityContext(annotation: WithMockHttpSignature): SecurityContext { - val preAuthenticatedAuthenticationToken = PreAuthenticatedAuthenticationToken( - annotation.keyId, HttpRequest( - URL(annotation.url), - HttpHeaders(mapOf()), HttpMethod.valueOf(annotation.method.uppercase()) - ) - ) - val httpSignatureUser = HttpSignatureUser( - annotation.username, - annotation.domain, - annotation.id, - true, - true, - mutableListOf() - ) - preAuthenticatedAuthenticationToken.details = httpSignatureUser - preAuthenticatedAuthenticationToken.isAuthenticated = true - val emptyContext = securityContextStrategy.createEmptyContext() - emptyContext.authentication = preAuthenticatedAuthenticationToken - return emptyContext - } -} diff --git a/hideout-core/src/intTest/resources/application.yml b/hideout-core/src/intTest/resources/application.yml deleted file mode 100644 index 57ab70fa..00000000 --- a/hideout-core/src/intTest/resources/application.yml +++ /dev/null @@ -1,40 +0,0 @@ -hideout: - debug: - trace-query-exception: true - trace-query-call: true - url: "https://example.com" - use-mongodb: true - security: - jwt: - generate: true - key-id: a - private-key: "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ==" - public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB" - storage: - type: local - private: false - -spring: - flyway: - enabled: true - clean-disabled: false - datasource: - driver-class-name: org.h2.Driver - url: "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;" - username: "" - password: - data: - mongodb: - auto-index-creation: true - host: localhost - port: 27017 - database: hideout-integration-test - h2: - console: - enabled: true - -# exposed: -# generate-ddl: true -# excluded-packages: dev.usbharu.hideout.core.infrastructure.kjobexposed -server: - port: 8080 diff --git a/hideout-core/src/intTest/resources/junit-platform.properties b/hideout-core/src/intTest/resources/junit-platform.properties deleted file mode 100644 index acfa9e5a..00000000 --- a/hideout-core/src/intTest/resources/junit-platform.properties +++ /dev/null @@ -1,2 +0,0 @@ -junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random -junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random diff --git a/hideout-core/src/intTest/resources/logback.xml b/hideout-core/src/intTest/resources/logback.xml deleted file mode 100644 index a8bb21c4..00000000 --- a/hideout-core/src/intTest/resources/logback.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{x-request-id}] %logger{36} - %msg%n - - - - - - - diff --git a/hideout-core/src/intTest/resources/media/400x400.png b/hideout-core/src/intTest/resources/media/400x400.png deleted file mode 100644 index 0d2e71bee323fed58e3aab0a839c0c2044bba54f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7227 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 diff --git a/hideout-core/src/intTest/resources/sql/accounts/apiV1AccountsIdFollowPost フォローã§ãã‚‹.sql b/hideout-core/src/intTest/resources/sql/accounts/apiV1AccountsIdFollowPost フォローã§ãã‚‹.sql deleted file mode 100644 index a2b01c22..00000000 --- a/hideout-core/src/intTest/resources/sql/accounts/apiV1AccountsIdFollowPost フォローã§ãã‚‹.sql +++ /dev/null @@ -1,17 +0,0 @@ -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) -VALUES (3733363, 'follow-test-user-1', 'example.com', 'follow-test-user-1-name', '', - 'https://example.com/users/follow-test-user-1/inbox', - 'https://example.com/users/follow-test-user-1/outbox', 'https://example.com/users/follow-test-user-1', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/follow-test-user-1#pubkey', 'https://example.com/users/follow-test-user-1/following', - 'https://example.com/users/follow-test-user-1/followers', 0, false, 0, 0, 0, null), - (37335363, 'follow-test-user-2', 'example.com', 'follow-test-user-2-name', '', - 'https://example.com/users/follow-test-user-2/inbox', - 'https://example.com/users/follow-test-user-2/outbox', 'https://example.com/users/follow-test-user-2', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/follow-test-user-2#pubkey', 'https://example.com/users/follow-test-user-2/following', - 'https://example.com/users/follow-test-user-2/followers', 0, false, 0, 0, 0, null); diff --git a/hideout-core/src/intTest/resources/sql/accounts/test-accounts-statuses.sql b/hideout-core/src/intTest/resources/sql/accounts/test-accounts-statuses.sql deleted file mode 100644 index 10352e07..00000000 --- a/hideout-core/src/intTest/resources/sql/accounts/test-accounts-statuses.sql +++ /dev/null @@ -1,202 +0,0 @@ -insert into posts (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id, deleted) -VALUES (1, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/1', - null, null, false, 'https://example.com/users/1/posts/1', false), - (2, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/2', - null, 1, false, 'https://example.com/users/1/posts/2', false), - (3, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/3', - null, null, false, 'https://example.com/users/1/posts/3', false), - (4, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/4', - null, 3, false, 'https://example.com/users/1/posts/4', false), - (5, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/5', - null, null, false, 'https://example.com/users/1/posts/5', false), - (6, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/6', - null, null, false, 'https://example.com/users/1/posts/6', false), - (7, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/7', - null, null, false, 'https://example.com/users/1/posts/7', false), - (8, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/8', - null, 7, false, 'https://example.com/users/1/posts/8', false), - (9, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/9', - null, null, false, 'https://example.com/users/1/posts/9', false), - (10, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/10', - null, 9, false, 'https://example.com/users/1/posts/10', false), - (11, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/11', - null, null, false, 'https://example.com/users/1/posts/11', false), - (12, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/12', - null, null, false, 'https://example.com/users/1/posts/12', false), - (13, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/13', - null, null, false, 'https://example.com/users/1/posts/13', false), - (14, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/14', - null, 13, false, 'https://example.com/users/1/posts/14', false), - (15, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/15', - null, null, false, 'https://example.com/users/1/posts/15', false), - (16, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/16', - null, 15, false, 'https://example.com/users/1/posts/16', false), - (17, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/17', - null, null, false, 'https://example.com/users/1/posts/17', false), - (18, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/18', - null, null, false, 'https://example.com/users/1/posts/18', false), - (19, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/19', - null, null, false, 'https://example.com/users/1/posts/19', false), - (20, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/20', - null, 19, false, 'https://example.com/users/1/posts/20', false), - (21, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/21', - null, null, false, 'https://example.com/users/1/posts/21', false), - (22, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/22', - null, 21, false, 'https://example.com/users/1/posts/22', false), - (23, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/23', - null, null, false, 'https://example.com/users/1/posts/23', false), - (24, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/24', - null, null, false, 'https://example.com/users/1/posts/24', false), - (25, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/25', - null, null, false, 'https://example.com/users/1/posts/25', false), - (26, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/26', - null, 25, false, 'https://example.com/users/1/posts/26', false), - (27, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/27', - null, null, false, 'https://example.com/users/1/posts/27', false), - (28, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/28', - null, 27, false, 'https://example.com/users/1/posts/28', false), - (29, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/29', - null, null, false, 'https://example.com/users/1/posts/29', false), - (30, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/30', - null, null, false, 'https://example.com/users/1/posts/30', false), - (31, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/31', - null, null, false, 'https://example.com/users/1/posts/31', false), - (32, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/32', - null, 31, false, 'https://example.com/users/1/posts/32', false), - (33, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/33', - null, null, false, 'https://example.com/users/1/posts/33', false), - (34, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/34', - null, 33, false, 'https://example.com/users/1/posts/34', false), - (35, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/35', - null, null, false, 'https://example.com/users/1/posts/35', false), - (36, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/36', - null, null, false, 'https://example.com/users/1/posts/36', false), - (37, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/37', - null, null, false, 'https://example.com/users/1/posts/37', false), - (38, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/38', - null, 37, false, 'https://example.com/users/1/posts/38', false), - (39, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/39', - null, null, false, 'https://example.com/users/1/posts/39', false), - (40, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/40', - null, 39, false, 'https://example.com/users/1/posts/40', false), - (41, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/41', - null, null, false, 'https://example.com/users/1/posts/41', false), - (42, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/42', - null, null, false, 'https://example.com/users/1/posts/42', false), - (43, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/43', - null, null, false, 'https://example.com/users/1/posts/43', false), - (44, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/44', - null, 43, false, 'https://example.com/users/1/posts/44', false), - (45, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/45', - null, null, false, 'https://example.com/users/1/posts/45', false), - (46, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/46', - null, 45, false, 'https://example.com/users/1/posts/46', false), - (47, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/47', - null, null, false, 'https://example.com/users/1/posts/47', false), - (48, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/48', - null, null, false, 'https://example.com/users/1/posts/48', false), - (49, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/49', - null, null, false, 'https://example.com/users/1/posts/49', false), - (50, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/50', - null, 49, false, 'https://example.com/users/1/posts/50', false), - (51, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/51', - null, null, false, 'https://example.com/users/1/posts/51', false), - (52, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/52', - null, 51, false, 'https://example.com/users/1/posts/52', false), - (53, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/53', - null, null, false, 'https://example.com/users/1/posts/53', false), - (54, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/54', - null, null, false, 'https://example.com/users/1/posts/54', false), - (55, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/55', - null, null, false, 'https://example.com/users/1/posts/55', false), - (56, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/56', - null, 55, false, 'https://example.com/users/1/posts/56', false), - (57, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/57', - null, null, false, 'https://example.com/users/1/posts/57', false), - (58, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/58', - null, 57, false, 'https://example.com/users/1/posts/58', false), - (59, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/59', - null, null, false, 'https://example.com/users/1/posts/59', false), - (60, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/60', - null, null, false, 'https://example.com/users/1/posts/60', false), - (61, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/61', - null, null, false, 'https://example.com/users/1/posts/61', false), - (62, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/62', - null, 61, false, 'https://example.com/users/1/posts/62', false), - (63, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/63', - null, null, false, 'https://example.com/users/1/posts/63', false), - (64, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/64', - null, 63, false, 'https://example.com/users/1/posts/64', false), - (65, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/65', - null, null, false, 'https://example.com/users/1/posts/65', false), - (66, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/66', - null, null, false, 'https://example.com/users/1/posts/66', false), - (67, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/67', - null, null, false, 'https://example.com/users/1/posts/67', false), - (68, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/68', - null, 67, false, 'https://example.com/users/1/posts/68', false), - (69, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/69', - null, null, false, 'https://example.com/users/1/posts/69', false), - (70, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/70', - null, 69, false, 'https://example.com/users/1/posts/70', false), - (71, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/71', - null, null, false, 'https://example.com/users/1/posts/71', false), - (72, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/72', - null, null, false, 'https://example.com/users/1/posts/72', false), - (73, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/73', - null, null, false, 'https://example.com/users/1/posts/73', false), - (74, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/74', - null, 73, false, 'https://example.com/users/1/posts/74', false), - (75, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/75', - null, null, false, 'https://example.com/users/1/posts/75', false), - (76, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/76', - null, 75, false, 'https://example.com/users/1/posts/76', false), - (77, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/77', - null, null, false, 'https://example.com/users/1/posts/77', false), - (78, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/78', - null, null, false, 'https://example.com/users/1/posts/78', false), - (79, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/79', - null, null, false, 'https://example.com/users/1/posts/79', false), - (80, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/80', - null, 79, false, 'https://example.com/users/1/posts/80', false), - (81, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/81', - null, null, false, 'https://example.com/users/1/posts/81', false), - (82, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/82', - null, 81, false, 'https://example.com/users/1/posts/82', false), - (83, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/83', - null, null, false, 'https://example.com/users/1/posts/83', false), - (84, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/84', - null, null, false, 'https://example.com/users/1/posts/84', false), - (85, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/85', - null, null, false, 'https://example.com/users/1/posts/85', false), - (86, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/86', - null, 85, false, 'https://example.com/users/1/posts/86', false), - (87, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/87', - null, null, false, 'https://example.com/users/1/posts/87', false), - (88, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/88', - null, 87, false, 'https://example.com/users/1/posts/88', false), - (89, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/89', - null, null, false, 'https://example.com/users/1/posts/89', false), - (90, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/90', - null, null, false, 'https://example.com/users/1/posts/90', false), - (91, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/91', - null, null, false, 'https://example.com/users/1/posts/91', false), - (92, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/92', - null, 91, false, 'https://example.com/users/1/posts/92', false), - (93, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/93', - null, null, false, 'https://example.com/users/1/posts/93', false), - (94, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/94', - null, 93, false, 'https://example.com/users/1/posts/94', false), - (95, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/95', - null, null, false, 'https://example.com/users/1/posts/95', false), - (96, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/96', - null, null, false, 'https://example.com/users/1/posts/96', false), - (97, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/97', - null, null, false, 'https://example.com/users/1/posts/97', false), - (98, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/98', - null, 97, false, 'https://example.com/users/1/posts/98', false), - (99, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/99', - null, null, false, 'https://example.com/users/1/posts/99', false), - (100, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/100', - null, 99, false, 'https://example.com/users/1/posts/100', false); \ No newline at end of file diff --git a/hideout-core/src/intTest/resources/sql/filter/test-filter.sql b/hideout-core/src/intTest/resources/sql/filter/test-filter.sql deleted file mode 100644 index d06d6bc0..00000000 --- a/hideout-core/src/intTest/resources/sql/filter/test-filter.sql +++ /dev/null @@ -1,4 +0,0 @@ -insert into filters (id, user_id, name, context, action) -VALUES (1, 1, 'test filter', 'home', 'warn'); -insert into filter_keywords(id, filter_id, keyword, mode) -VALUES (1, 1, 'hoge', 'NONE') \ No newline at end of file diff --git a/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹.sql deleted file mode 100644 index dc2ab9f1..00000000 --- a/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒfollowers投稿をå–å¾—ã§ãã‚‹.sql +++ /dev/null @@ -1,28 +0,0 @@ -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) -VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test-user8.', - 'https://example.com/users/test-user8/inbox', - 'https://example.com/users/test-user8/outbox', 'https://example.com/users/test-user8', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user8#pubkey', 'https://example.com/users/test-user8/following', - 'https://example.com/users/test-user8/followers', 0, false, 0, 0, 0, null), - (9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account is test-user9.', - 'https://follower.example.com/users/test-user9/inbox', - 'https://follower.example.com/users/test-user9/outbox', 'https://follower.example.com/users/test-user9', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - null, 12345678, - 'https://follower.example.com/users/test-user9#pubkey', - 'https://follower.example.com/users/test-user9/following', - 'https://follower.example.com/users/test-user9/followers', 0, false, 0, 0, 0, null); - -insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request, - ignore_follow_request) -VALUES (9, 8, true, false, false, false, false); - -insert into POSTS (ID, ACTOR_ID, OVERVIEW, CONTENT, TEXT, CREATED_AT, VISIBILITY, URL, REPLY_ID, REPOST_ID, SENSITIVE, - AP_ID) -VALUES (1239, 8, null, '

test post

', 'test post', 12345680, 2, 'https://example.com/users/test-user8/posts/1239', - null, null, false, - 'https://example.com/users/test-user8/posts/1239'); diff --git a/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹.sql deleted file mode 100644 index a44861f4..00000000 --- a/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒpublic投稿をå–å¾—ã§ãã‚‹.sql +++ /dev/null @@ -1,29 +0,0 @@ -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) -VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test user4.', - 'https://example.com/users/test-user4/inbox', - 'https://example.com/users/test-user4/outbox', 'https://example.com/users/test-user4', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user4#pubkey', 'https://example.com/users/test-user4/following', - 'https://example.com/users/test-user4/followers', 0, false, 0, 0, 0, null), - (5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account is test user5.', - 'https://follower.example.com/users/test-user5/inbox', - 'https://follower.example.com/users/test-user5/outbox', 'https://follower.example.com/users/test-user5', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - null, 12345678, - 'https://follower.example.com/users/test-user5#pubkey', - 'https://follower.example.com/users/test-user5/following', - 'https://follower.example.com/users/test-user5/followers', 0, false, 0, 0, 0, null); - -insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request, - ignore_follow_request) -VALUES (5, 4, true, false, false, false, false); - -insert into POSTS (ID, "actor_id", OVERVIEW, CONTENT, TEXT, "created_at", VISIBILITY, URL, "repost_id", "reply_id", - SENSITIVE, - AP_ID) -VALUES (1237, 4, null, '

test post

', 'test post', 12345680, 0, 'https://example.com/users/test-user4/posts/1237', - null, null, false, - 'https://example.com/users/test-user4/posts/1237'); diff --git a/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹.sql deleted file mode 100644 index 77cb3395..00000000 --- a/hideout-core/src/intTest/resources/sql/note/httpSignatureèªè¨¼ã§ãƒ•ã‚©ãƒ­ãƒ¯ãƒ¼ãŒunlisted投稿をå–å¾—ã§ãã‚‹.sql +++ /dev/null @@ -1,29 +0,0 @@ -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) -VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test-user6.', - 'https://example.com/users/test-user6/inbox', - 'https://example.com/users/test-user6/outbox', 'https://example.com/users/test-user6', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user6#pubkey', 'https://example.com/users/test-user6/following', - 'https://example.com/users/test-user6/followers', 0, false, 0, 0, 0, null), - (7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account is test-user7.', - 'https://follower.example.com/users/test-user7/inbox', - 'https://follower.example.com/users/test-user7/outbox', 'https://follower.example.com/users/test-user7', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - null, 12345678, - 'https://follower.example.com/users/test-user7#pubkey', - 'https://follower.example.com/users/test-user7/following', - 'https://follower.example.com/users/test-user7/followers', 0, false, 0, 0, 0, null); - -insert into relationships (actor_id, target_actor_id, following, blocking, muting, follow_request, - ignore_follow_request) -VALUES (7, 6, true, false, false, false, false); - -insert into POSTS (ID, "actor_ID", OVERVIEW, CONTENT, TEXT, "CREATED_AT", VISIBILITY, URL, "REPOST_ID", "REPLY_ID", - SENSITIVE, - AP_ID) -VALUES (1238, 6, null, '

test post

', 'test post', 12345680, 1, 'https://example.com/users/test-user6/posts/1238', - null, null, false, - 'https://example.com/users/test-user6/posts/1238'); diff --git a/hideout-core/src/intTest/resources/sql/note/メディア付ã投稿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/メディア付ã投稿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹.sql deleted file mode 100644 index 6cc9db8c..00000000 --- a/hideout-core/src/intTest/resources/sql/note/メディア付ã投稿ã¯attachmentã«Documentã¨ã—ã¦ç”»åƒãŒå­˜åœ¨ã™ã‚‹.sql +++ /dev/null @@ -1,25 +0,0 @@ -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) -VALUES (11, 'test-user11', 'example.com', 'Im test-user11.', 'THis account is test-user11.', - 'https://example.com/users/test-user11/inbox', - 'https://example.com/users/test-user11/outbox', 'https://example.com/users/test-user11', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user11#pubkey', 'https://example.com/users/test-user11/following', - 'https://example.com/users/test-user11/followers', 0, false, 0, 0, 0, null); - -insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id, - deleted) -VALUES (1242, 11, null, '

test post

', 'test post', 12345680, 0, - 'https://example.com/users/test-user11/posts/1242', null, null, false, - 'https://example.com/users/test-user11/posts/1242', false); - -insert into MEDIA (ID, NAME, URL, REMOTE_URL, THUMBNAIL_URL, TYPE, BLURHASH, MIME_TYPE, DESCRIPTION) -VALUES (1, 'test-media', 'https://example.com/media/test-media.png', null, null, 0, null, 'image/png', null), - (2, 'test-media2', 'https://example.com/media/test-media2.png', null, null, 0, null, 'image/png', null); - -insert into POSTS_MEDIA(POST_ID, MEDIA_ID) -VALUES (1242, 1), - (1242, 2); diff --git a/hideout-core/src/intTest/resources/sql/note/リプライã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/リプライã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹.sql deleted file mode 100644 index 41ac73a4..00000000 --- a/hideout-core/src/intTest/resources/sql/note/リプライã«ãªã£ã¦ã„る投稿ã¯inReplyToãŒå­˜åœ¨ã™ã‚‹.sql +++ /dev/null @@ -1,20 +0,0 @@ -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) -VALUES (10, 'test-user10', 'example.com', 'Im test-user10.', 'THis account is test-user10.', - 'https://example.com/users/test-user10/inbox', - 'https://example.com/users/test-user10/outbox', 'https://example.com/users/test-user10', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user10#pubkey', 'https://example.com/users/test-user10/following', - 'https://example.com/users/test-user10/followers', 0, false, 0, 0, 0, null); - -insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id, - deleted) -VALUES (1240, 10, null, '

test post

', 'test post', 12345680, 0, - 'https://example.com/users/test-user10/posts/1240', null, null, false, - 'https://example.com/users/test-user10/posts/1240', false), - (1241, 10, null, '

test post

', 'test post', 12345680, 0, - 'https://example.com/users/test-user10/posts/1241', null, 1240, false, - 'https://example.com/users/test-user10/posts/1241', false); diff --git a/hideout-core/src/intTest/resources/sql/note/匿åã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404.sql b/hideout-core/src/intTest/resources/sql/note/匿åã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404.sql deleted file mode 100644 index 250cfb5a..00000000 --- a/hideout-core/src/intTest/resources/sql/note/匿åã§followers投稿をå–å¾—ã—よã†ã¨ã™ã‚‹ã¨404.sql +++ /dev/null @@ -1,17 +0,0 @@ -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) -VALUES (3, 'test-user3', 'example.com', 'Im test user3.', 'THis account is test user3.', - 'https://example.com/users/test-user3/inbox', - 'https://example.com/users/test-user3/outbox', 'https://example.com/users/test-user3', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user3#pubkey', 'https://example.com/users/test-user3/following', - 'https://example.com/users/test-user3/followers', 0, false, 0, 0, 0, null); - -insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id, - deleted) -VALUES (1236, 3, null, '

test post

', 'test post', 12345680, 2, 'https://example.com/users/test-user3/posts/1236', - null, null, false, - 'https://example.com/users/test-user3/posts/1236', false) diff --git a/hideout-core/src/intTest/resources/sql/note/匿åã§public投稿をå–å¾—ã§ãã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/匿åã§public投稿をå–å¾—ã§ãã‚‹.sql deleted file mode 100644 index 777f9244..00000000 --- a/hideout-core/src/intTest/resources/sql/note/匿åã§public投稿をå–å¾—ã§ãã‚‹.sql +++ /dev/null @@ -1,17 +0,0 @@ -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) -VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.', - 'https://example.com/users/test-user/inbox', - 'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following', - 'https://example.com/users/test-users/followers', 0, false, 0, 0, 0, null); - -insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id, - deleted) -VALUES (1234, 1, null, '

test post

', 'test post', 12345680, 0, 'https://example.com/users/test-user/posts/1234', - null, null, false, - 'https://example.com/users/test-user/posts/1234', false) diff --git a/hideout-core/src/intTest/resources/sql/note/匿åã§unlisted投稿をå–å¾—ã§ãã‚‹.sql b/hideout-core/src/intTest/resources/sql/note/匿åã§unlisted投稿をå–å¾—ã§ãã‚‹.sql deleted file mode 100644 index b132734d..00000000 --- a/hideout-core/src/intTest/resources/sql/note/匿åã§unlisted投稿をå–å¾—ã§ãã‚‹.sql +++ /dev/null @@ -1,17 +0,0 @@ -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) -VALUES (2, 'test-user2', 'example.com', 'Im test user2.', 'THis account is test user2.', - 'https://example.com/users/test-user2/inbox', - 'https://example.com/users/test-user2/outbox', 'https://example.com/users/test-user2', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following', - 'https://example.com/users/test-user2/followers', 0, false, 0, 0, 0, null); - -insert into POSTS (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id, - deleted) -VALUES (1235, 2, null, '

test post

', 'test post', 12345680, 1, 'https://example.com/users/test-user2/posts/1235', - null, null, false, - 'https://example.com/users/test-user2/posts/1235', false) diff --git a/hideout-core/src/intTest/resources/sql/notification/test-mastodon_notifications.sql b/hideout-core/src/intTest/resources/sql/notification/test-mastodon_notifications.sql deleted file mode 100644 index c97a25a7..00000000 --- a/hideout-core/src/intTest/resources/sql/notification/test-mastodon_notifications.sql +++ /dev/null @@ -1,66 +0,0 @@ -insert into mastodon_notifications (id, user_id, type, created_at, account_id, status_id, report_id, relationship_serverance_event_id) -values (1, 1, 'follow', current_timestamp, 2, null, null, null), - (2, 1, 'follow', current_timestamp, 2, null, null, null), - (3, 1, 'follow', current_timestamp, 2, null, null, null), - (4, 1, 'follow', current_timestamp, 2, null, null, null), - (5, 1, 'follow', current_timestamp, 2, null, null, null), - (6, 1, 'follow', current_timestamp, 2, null, null, null), - (7, 1, 'follow', current_timestamp, 2, null, null, null), - (8, 1, 'follow', current_timestamp, 2, null, null, null), - (9, 1, 'follow', current_timestamp, 2, null, null, null), - (10, 1, 'follow', current_timestamp, 2, null, null, null), - (11, 1, 'follow', current_timestamp, 2, null, null, null), - (12, 1, 'follow', current_timestamp, 2, null, null, null), - (13, 1, 'follow', current_timestamp, 2, null, null, null), - (14, 1, 'follow', current_timestamp, 2, null, null, null), - (15, 1, 'follow', current_timestamp, 2, null, null, null), - (16, 1, 'follow', current_timestamp, 2, null, null, null), - (17, 1, 'follow', current_timestamp, 2, null, null, null), - (18, 1, 'follow', current_timestamp, 2, null, null, null), - (19, 1, 'follow', current_timestamp, 2, null, null, null), - (20, 1, 'follow', current_timestamp, 2, null, null, null), - (21, 1, 'follow', current_timestamp, 2, null, null, null), - (22, 1, 'follow', current_timestamp, 2, null, null, null), - (23, 1, 'follow', current_timestamp, 2, null, null, null), - (24, 1, 'follow', current_timestamp, 2, null, null, null), - (25, 1, 'follow', current_timestamp, 2, null, null, null), - (26, 1, 'follow', current_timestamp, 2, null, null, null), - (27, 1, 'follow', current_timestamp, 2, null, null, null), - (28, 1, 'follow', current_timestamp, 2, null, null, null), - (29, 1, 'follow', current_timestamp, 2, null, null, null), - (30, 1, 'follow', current_timestamp, 2, null, null, null), - (31, 1, 'follow', current_timestamp, 2, null, null, null), - (32, 1, 'follow', current_timestamp, 2, null, null, null), - (33, 1, 'follow', current_timestamp, 2, null, null, null), - (34, 1, 'follow', current_timestamp, 2, null, null, null), - (35, 1, 'follow', current_timestamp, 2, null, null, null), - (36, 1, 'follow', current_timestamp, 2, null, null, null), - (37, 1, 'follow', current_timestamp, 2, null, null, null), - (38, 1, 'follow', current_timestamp, 2, null, null, null), - (39, 1, 'follow', current_timestamp, 2, null, null, null), - (40, 1, 'follow', current_timestamp, 2, null, null, null), - (41, 1, 'follow', current_timestamp, 2, null, null, null), - (42, 1, 'follow', current_timestamp, 2, null, null, null), - (43, 1, 'follow', current_timestamp, 2, null, null, null), - (44, 1, 'follow', current_timestamp, 2, null, null, null), - (45, 1, 'follow', current_timestamp, 2, null, null, null), - (46, 1, 'follow', current_timestamp, 2, null, null, null), - (47, 1, 'follow', current_timestamp, 2, null, null, null), - (48, 1, 'follow', current_timestamp, 2, null, null, null), - (49, 1, 'follow', current_timestamp, 2, null, null, null), - (50, 1, 'follow', current_timestamp, 2, null, null, null), - (51, 1, 'follow', current_timestamp, 2, null, null, null), - (52, 1, 'follow', current_timestamp, 2, null, null, null), - (53, 1, 'follow', current_timestamp, 2, null, null, null), - (54, 1, 'follow', current_timestamp, 2, null, null, null), - (55, 1, 'follow', current_timestamp, 2, null, null, null), - (56, 1, 'follow', current_timestamp, 2, null, null, null), - (57, 1, 'follow', current_timestamp, 2, null, null, null), - (58, 1, 'follow', current_timestamp, 2, null, null, null), - (59, 1, 'follow', current_timestamp, 2, null, null, null), - (60, 1, 'follow', current_timestamp, 2, null, null, null), - (61, 1, 'follow', current_timestamp, 2, null, null, null), - (62, 1, 'follow', current_timestamp, 2, null, null, null), - (63, 1, 'follow', current_timestamp, 2, null, null, null), - (64, 1, 'follow', current_timestamp, 2, null, null, null), - (65, 1, 'follow', current_timestamp, 2, null, null, null); \ No newline at end of file diff --git a/hideout-core/src/intTest/resources/sql/notification/test-notifications.sql b/hideout-core/src/intTest/resources/sql/notification/test-notifications.sql deleted file mode 100644 index 38982603..00000000 --- a/hideout-core/src/intTest/resources/sql/notification/test-notifications.sql +++ /dev/null @@ -1,66 +0,0 @@ -insert into notifications(id, type, user_id, source_actor_id, post_id, text, reaction_id, created_at) -VALUES (1, 'follow', 1, 2, null, null, null, current_timestamp), - (2, 'follow', 1, 2, null, null, null, current_timestamp), - (3, 'follow', 1, 2, null, null, null, current_timestamp), - (4, 'follow', 1, 2, null, null, null, current_timestamp), - (5, 'follow', 1, 2, null, null, null, current_timestamp), - (6, 'follow', 1, 2, null, null, null, current_timestamp), - (7, 'follow', 1, 2, null, null, null, current_timestamp), - (8, 'follow', 1, 2, null, null, null, current_timestamp), - (9, 'follow', 1, 2, null, null, null, current_timestamp), - (10, 'follow', 1, 2, null, null, null, current_timestamp), - (11, 'follow', 1, 2, null, null, null, current_timestamp), - (12, 'follow', 1, 2, null, null, null, current_timestamp), - (13, 'follow', 1, 2, null, null, null, current_timestamp), - (14, 'follow', 1, 2, null, null, null, current_timestamp), - (15, 'follow', 1, 2, null, null, null, current_timestamp), - (16, 'follow', 1, 2, null, null, null, current_timestamp), - (17, 'follow', 1, 2, null, null, null, current_timestamp), - (18, 'follow', 1, 2, null, null, null, current_timestamp), - (19, 'follow', 1, 2, null, null, null, current_timestamp), - (20, 'follow', 1, 2, null, null, null, current_timestamp), - (21, 'follow', 1, 2, null, null, null, current_timestamp), - (22, 'follow', 1, 2, null, null, null, current_timestamp), - (23, 'follow', 1, 2, null, null, null, current_timestamp), - (24, 'follow', 1, 2, null, null, null, current_timestamp), - (25, 'follow', 1, 2, null, null, null, current_timestamp), - (26, 'follow', 1, 2, null, null, null, current_timestamp), - (27, 'follow', 1, 2, null, null, null, current_timestamp), - (28, 'follow', 1, 2, null, null, null, current_timestamp), - (29, 'follow', 1, 2, null, null, null, current_timestamp), - (30, 'follow', 1, 2, null, null, null, current_timestamp), - (31, 'follow', 1, 2, null, null, null, current_timestamp), - (32, 'follow', 1, 2, null, null, null, current_timestamp), - (33, 'follow', 1, 2, null, null, null, current_timestamp), - (34, 'follow', 1, 2, null, null, null, current_timestamp), - (35, 'follow', 1, 2, null, null, null, current_timestamp), - (36, 'follow', 1, 2, null, null, null, current_timestamp), - (37, 'follow', 1, 2, null, null, null, current_timestamp), - (38, 'follow', 1, 2, null, null, null, current_timestamp), - (39, 'follow', 1, 2, null, null, null, current_timestamp), - (40, 'follow', 1, 2, null, null, null, current_timestamp), - (41, 'follow', 1, 2, null, null, null, current_timestamp), - (42, 'follow', 1, 2, null, null, null, current_timestamp), - (43, 'follow', 1, 2, null, null, null, current_timestamp), - (44, 'follow', 1, 2, null, null, null, current_timestamp), - (45, 'follow', 1, 2, null, null, null, current_timestamp), - (46, 'follow', 1, 2, null, null, null, current_timestamp), - (47, 'follow', 1, 2, null, null, null, current_timestamp), - (48, 'follow', 1, 2, null, null, null, current_timestamp), - (49, 'follow', 1, 2, null, null, null, current_timestamp), - (50, 'follow', 1, 2, null, null, null, current_timestamp), - (51, 'follow', 1, 2, null, null, null, current_timestamp), - (52, 'follow', 1, 2, null, null, null, current_timestamp), - (53, 'follow', 1, 2, null, null, null, current_timestamp), - (54, 'follow', 1, 2, null, null, null, current_timestamp), - (55, 'follow', 1, 2, null, null, null, current_timestamp), - (56, 'follow', 1, 2, null, null, null, current_timestamp), - (57, 'follow', 1, 2, null, null, null, current_timestamp), - (58, 'follow', 1, 2, null, null, null, current_timestamp), - (59, 'follow', 1, 2, null, null, null, current_timestamp), - (60, 'follow', 1, 2, null, null, null, current_timestamp), - (61, 'follow', 1, 2, null, null, null, current_timestamp), - (62, 'follow', 1, 2, null, null, null, current_timestamp), - (63, 'follow', 1, 2, null, null, null, current_timestamp), - (64, 'follow', 1, 2, null, null, null, current_timestamp), - (65, 'follow', 1, 2, null, null, null, current_timestamp); \ No newline at end of file diff --git a/hideout-core/src/intTest/resources/sql/test-custom-emoji.sql b/hideout-core/src/intTest/resources/sql/test-custom-emoji.sql deleted file mode 100644 index 83c2747a..00000000 --- a/hideout-core/src/intTest/resources/sql/test-custom-emoji.sql +++ /dev/null @@ -1,3 +0,0 @@ -insert into emojis(id, name, domain, instance_id, url, category, created_at) -VALUES (1, 'kotlin', 'example.com', null, 'https://example.com/emojis/kotlin', null, - TIMESTAMP '2024-01-08 07:51:30.036Z'); diff --git a/hideout-core/src/intTest/resources/sql/test-post.sql b/hideout-core/src/intTest/resources/sql/test-post.sql deleted file mode 100644 index cd623188..00000000 --- a/hideout-core/src/intTest/resources/sql/test-post.sql +++ /dev/null @@ -1,4 +0,0 @@ -insert into posts (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, - ap_id) -VALUES (1, 1, null, '

test post

', 'hello', 1234455, 0, 'https://localhost/users/1/posts/1', null, null, false, - 'https://users/1/posts/1'); diff --git a/hideout-core/src/intTest/resources/sql/test-user.sql b/hideout-core/src/intTest/resources/sql/test-user.sql deleted file mode 100644 index d46e5280..00000000 --- a/hideout-core/src/intTest/resources/sql/test-user.sql +++ /dev/null @@ -1,10 +0,0 @@ -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) -VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.', - 'https://example.com/users/test-user/inbox', - 'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following', - 'https://example.com/users/test-users/followers', 0, false, 0, 0, 0, null); diff --git a/hideout-core/src/intTest/resources/sql/test-user2.sql b/hideout-core/src/intTest/resources/sql/test-user2.sql deleted file mode 100644 index 0f736704..00000000 --- a/hideout-core/src/intTest/resources/sql/test-user2.sql +++ /dev/null @@ -1,10 +0,0 @@ -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) -VALUES (2, 'test-user2', 'example.com', 'Im test user.', 'THis account is test user.', - 'https://example.com/users/test-user2/inbox', - 'https://example.com/users/test-user2/outbox', 'https://example.com/users/test-user2', - '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', - '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, - 'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following', - 'https://example.com/users/test-user2s/followers', 0, false, 0, 0, 0, null); diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt new file mode 100644 index 00000000..04d1b2ee --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.media + +class Media diff --git a/hideout-core/src/intTest/kotlin/util/SpringApplicationTestBase.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt similarity index 82% rename from hideout-core/src/intTest/kotlin/util/SpringApplicationTestBase.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt index 158f8c7a..9f409416 100644 --- a/hideout-core/src/intTest/kotlin/util/SpringApplicationTestBase.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package util +package dev.usbharu.hideout.core.application.media -import org.springframework.boot.test.context.SpringBootTest +import java.nio.file.Path -@SpringBootTest -abstract class SpringApplicationTestBase +data class UploadMedia(val path: Path) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt new file mode 100644 index 00000000..36c07865 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.media + +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 org.slf4j.LoggerFactory + +class UploadMediaApplicationService(transaction: Transaction) : AbstractApplicationService( + transaction, logger +) { + companion object { + private val logger = LoggerFactory.getLogger(UploadMediaApplicationService::class.java) + } + + override suspend fun internalExecute(command: UploadMedia, executor: CommandExecutor): Media { + TODO() + } +} \ No newline at end of file diff --git a/hideout-core/src/intTest/kotlin/util/TestTransaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt similarity index 66% rename from hideout-core/src/intTest/kotlin/util/TestTransaction.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt index 0f8b6317..d2657985 100644 --- a/hideout-core/src/intTest/kotlin/util/TestTransaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt @@ -14,12 +14,10 @@ * limitations under the License. */ -package util +package dev.usbharu.hideout.core.external.media -import dev.usbharu.hideout.core.application.shared.Transaction +import java.nio.file.Path -object TestTransaction : Transaction { - override suspend fun transaction(block: suspend () -> T): T = block() - - override suspend fun transaction(transactionLevel: Int, block: suspend () -> T): T = block() -} +interface MediaProcessor { + suspend fun process(path: Path): ProcessedMedia +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt new file mode 100644 index 00000000..ac136c3c --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.external.media + +import dev.usbharu.hideout.core.domain.model.media.FileType +import dev.usbharu.hideout.core.domain.model.media.MimeType +import java.nio.file.Path + +data class ProcessedMedia( + val path: Path, + val thumbnailPath: Path?, + val fileType: FileType, + val mimeType: MimeType, + val blurHash: String, +) diff --git a/owl/gradle.properties b/owl/gradle.properties index e981646f..1108ef87 100644 --- a/owl/gradle.properties +++ b/owl/gradle.properties @@ -2,4 +2,6 @@ kotlin.code.style=official org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true -#ksp.useKSP2=true \ No newline at end of file +#ksp.useKSP2=true +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn \ No newline at end of file diff --git a/owl/owl-common/owl-common-serialize-jackson/build.gradle.kts b/owl/owl-common/owl-common-serialize-jackson/build.gradle.kts index 5eed5b43..bb09a5e4 100644 --- a/owl/owl-common/owl-common-serialize-jackson/build.gradle.kts +++ b/owl/owl-common/owl-common-serialize-jackson/build.gradle.kts @@ -19,5 +19,5 @@ tasks.test { useJUnitPlatform() } kotlin { - jvmToolchain(21) + jvmToolchain(17) } \ No newline at end of file From d903d558c153c1ff908c471f8e4a838e07cef022 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 15 Jun 2024 02:02:07 +0900 Subject: [PATCH 48/54] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 215512d0..f078ee27 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ out/ *.log /hideout-core/files/ +/hideout-core/.kotlin/sessions/ From a93131b5dc3a74b1f0bbd2efd64b67eea39c3c86 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 16 Jun 2024 13:09:15 +0900 Subject: [PATCH 49/54] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/application/media/Media.kt | 29 ++++++++++++- .../core/application/media/UploadMedia.kt | 3 +- .../media/UploadMediaApplicationService.kt | 41 ++++++++++++++++++- .../hideout/core/domain/model/media/Media.kt | 10 ++++- .../core/external/mediastore/MediaStore.kt | 8 ++++ 5 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt index 04d1b2ee..99490b84 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt @@ -16,4 +16,31 @@ package dev.usbharu.hideout.core.application.media -class Media +import dev.usbharu.hideout.core.domain.model.media.FileType +import dev.usbharu.hideout.core.domain.model.media.Media +import dev.usbharu.hideout.core.domain.model.media.MimeType +import java.net.URI + +data class Media( + val name: String, + val url: URI, + val thumbprintURI: URI?, + val type: FileType, + val mimeType: MimeType, + val blurHash: String?, + val description: String? +) { + companion object { + fun of(media: Media): dev.usbharu.hideout.core.application.media.Media { + return Media( + media.name.name, + media.url, + media.thumbnailUrl, + media.type, + media.mimeType, + media.blurHash?.hash, + media.description?.description + ) + } + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt index 9f409416..4a8af8d3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMedia.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.application.media +import java.net.URI import java.nio.file.Path -data class UploadMedia(val path: Path) +data class UploadMedia(val path: Path, val name: String, val remoteUri: URI?, val description: String?) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt index 36c07865..4e9150c7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt @@ -19,9 +19,22 @@ package dev.usbharu.hideout.core.application.media 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.media.* +import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService +import dev.usbharu.hideout.core.external.media.MediaProcessor +import dev.usbharu.hideout.core.external.mediastore.MediaStore import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import dev.usbharu.hideout.core.domain.model.media.Media as MediaModel -class UploadMediaApplicationService(transaction: Transaction) : AbstractApplicationService( +@Service +class UploadMediaApplicationService( + private val mediaProcessor: MediaProcessor, + private val mediaStore: MediaStore, + private val mediaRepository: MediaRepository, + private val idGenerateService: IdGenerateService, + transaction: Transaction +) : AbstractApplicationService( transaction, logger ) { companion object { @@ -29,6 +42,30 @@ class UploadMediaApplicationService(transaction: Transaction) : AbstractApplicat } override suspend fun internalExecute(command: UploadMedia, executor: CommandExecutor): Media { - TODO() + val process = mediaProcessor.process(command.path) + val id = idGenerateService.generateId() + val thumbnailUri = if (process.thumbnailPath != null) { + mediaStore.upload(process.thumbnailPath, "thumbnail-$id") + } else { + null + } + val uri = mediaStore.upload(process.path, id.toString()) + + val media = MediaModel( + MediaId(id), + MediaName(command.name), + uri, + command.remoteUri, + thumbnailUri, + process.fileType, + process.mimeType, + MediaBlurHash(process.blurHash), + command.description?.let { MediaDescription(it) } + ) + + mediaRepository.save(media) + + return Media.of(media) + } } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt index 5c3eddf6..1d2823c4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/media/Media.kt @@ -18,10 +18,10 @@ package dev.usbharu.hideout.core.domain.model.media import java.net.URI -data class Media( +class Media( val id: MediaId, val name: MediaName, - val url: URI, + url: URI, val remoteUrl: URI?, val thumbnailUrl: URI?, val type: FileType, @@ -29,6 +29,12 @@ data class Media( val blurHash: MediaBlurHash?, val description: MediaDescription? = null, ) { + var url = url + private set + + fun setUrl(url: URI) { + this.url = url + } override fun toString(): String { return "Media(" + "id=$id, " + diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt new file mode 100644 index 00000000..dc34540e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.external.mediastore + +import java.net.URI +import java.nio.file.Path + +interface MediaStore { + suspend fun upload(path: Path, id: String): URI +} \ No newline at end of file From 43ef619eee7ef5ae8d23efc1a6d7355a9521b752 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 16 Jun 2024 15:45:41 +0900 Subject: [PATCH 50/54] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/UploadMediaApplicationService.kt | 7 +- .../hideout/core/config/FFmpegVideoConfig.kt | 17 +++ .../hideout/core/config/ImageIOImageConfig.kt | 10 ++ .../hideout/core/config/LocalStorageConfig.kt | 17 +++ .../hideout/core/config/S3StorageConfig.kt | 34 +++++ .../external/media/DelegateMediaProcessor.kt | 22 ++++ .../external/media/FileTypeDeterminator.kt | 8 ++ .../core/external/media/MediaProcessor.kt | 4 +- .../core/external/media/ProcessedMedia.kt | 2 +- .../external/media/TikaFileTypeDeterminer.kt | 51 ++++++++ .../infrastructure/awss3/AWSS3MediaStore.kt | 51 ++++++++ .../LocalFileSystemMediaStore.kt | 47 +++++++ .../media/common/GenerateBlurhash.kt | 7 ++ .../media/common/GenerateBlurhashImpl.kt | 12 ++ .../media/image/ImageIOImageProcessor.kt | 79 ++++++++++++ .../media/video/FFmpegVideoProcessor.kt | 117 ++++++++++++++++++ .../exposedquery/ExposedStatusQueryService.kt | 2 +- 17 files changed, 481 insertions(+), 6 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ImageIOImageConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt index 4e9150c7..f9d857e8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt @@ -24,12 +24,13 @@ import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.external.media.MediaProcessor import dev.usbharu.hideout.core.external.mediastore.MediaStore import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service import dev.usbharu.hideout.core.domain.model.media.Media as MediaModel @Service class UploadMediaApplicationService( - private val mediaProcessor: MediaProcessor, + @Qualifier("delegate") private val mediaProcessor: MediaProcessor, private val mediaStore: MediaStore, private val mediaRepository: MediaRepository, private val idGenerateService: IdGenerateService, @@ -42,7 +43,7 @@ class UploadMediaApplicationService( } override suspend fun internalExecute(command: UploadMedia, executor: CommandExecutor): Media { - val process = mediaProcessor.process(command.path) + val process = mediaProcessor.process(command.path, command.name, null) val id = idGenerateService.generateId() val thumbnailUri = if (process.thumbnailPath != null) { mediaStore.upload(process.thumbnailPath, "thumbnail-$id") @@ -59,7 +60,7 @@ class UploadMediaApplicationService( thumbnailUri, process.fileType, process.mimeType, - MediaBlurHash(process.blurHash), + process.blurHash?.let { MediaBlurHash(it) }, command.description?.let { MediaDescription(it) } ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt new file mode 100644 index 00000000..6f19a56b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt @@ -0,0 +1,17 @@ +package dev.usbharu.hideout.core.config + +import org.bytedeco.ffmpeg.global.avcodec +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties("hideout.media.video.ffmpeg") +data class FFmpegVideoConfig( + val frameRate: Int = 60, + val maxWidth: Int = 1920, + val maxHeight: Int = 1080, + val format: String = "mp4", + val videoCodec: Int = avcodec.AV_CODEC_ID_H264, + val audioCodec: Int = avcodec.AV_CODEC_ID_AAC, + val videoQuality: Double = 1.0, + val videoOption: List = listOf("preset", "ultrafast"), + val maxBitrate: Int = 1300000, +) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ImageIOImageConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ImageIOImageConfig.kt new file mode 100644 index 00000000..03e8bfcc --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/ImageIOImageConfig.kt @@ -0,0 +1,10 @@ +package dev.usbharu.hideout.core.config + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties("hideout.media.image.imageio") +data class ImageIOImageConfig( + val thumbnailsWidth: Int = 1000, + val thumbnailsHeight: Int = 1000, + val format: String = "jpeg" +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt new file mode 100644 index 00000000..a5cfc2e8 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt @@ -0,0 +1,17 @@ +package dev.usbharu.hideout.core.config + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.boot.context.properties.ConfigurationProperties + +/** + * メディアã®ä¿å­˜ã«ãƒ­ãƒ¼ã‚«ãƒ«ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã‚’使用ã™ã‚‹éš›ã®ã‚³ãƒ³ãƒ•ã‚£ã‚° + * + * @property path フォゾンã™ã‚‹å ´æ‰€ã¸ã®ãƒ‘ス。 /ã‹ã‚‰å§‹ã‚ã‚‹ã¨çµ¶å¯¾ãƒ‘スã¨ãªã‚Šã¾ã™ã€‚ + * @property publicUrl 公開用URL çœç•¥å¯èƒ½ 指定ã™ã‚‹ã¨HideoutãŒãƒ•ã‚¡ã‚¤ãƒ«ã‚’é…ä¿¡ã—ãªããªã‚Šã¾ã™ã€‚ + */ +@ConfigurationProperties("hideout.storage.local") +@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) +data class LocalStorageConfig( + val path: String = "files", + val publicUrl: String? +) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt new file mode 100644 index 00000000..fa523441 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt @@ -0,0 +1,34 @@ +package dev.usbharu.hideout.core.config + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.s3.S3Client +import java.net.URI + +@ConfigurationProperties("hideout.storage.s3") +@ConditionalOnProperty("hideout.storage.type", havingValue = "s3") +data class S3StorageConfig( + val endpoint: String, + val publicUrl: String, + val bucket: String, + val region: String, + val accessKey: String, + val secretKey: String +) + +@Configuration +class AwsConfig { + @Bean + @ConditionalOnProperty("hideout.storage.type", havingValue = "s3") + fun s3Client(awsConfig: S3StorageConfig): S3Client { + return S3Client.builder() + .endpointOverride(URI.create(awsConfig.endpoint)) + .region(Region.of(awsConfig.region)) + .credentialsProvider { AwsBasicCredentials.create(awsConfig.accessKey, awsConfig.secretKey) } + .build() + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt new file mode 100644 index 00000000..96607974 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.core.external.media + +import dev.usbharu.hideout.core.domain.model.media.MimeType +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component +import java.nio.file.Path + +@Component +@Qualifier("delegate") +class DelegateMediaProcessor( + private val fileTypeDeterminer: FileTypeDeterminer, + private val mediaProcessors: List +) : MediaProcessor { + override fun isSupported(mimeType: MimeType): Boolean { + return true + } + + override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia { + val fileType = fileTypeDeterminer.fileType(path, filename) + return mediaProcessors.first { it.isSupported(fileType) }.process(path, filename, fileType) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt new file mode 100644 index 00000000..199ac249 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.core.external.media + +import dev.usbharu.hideout.core.domain.model.media.MimeType +import java.nio.file.Path + +interface FileTypeDeterminer { + fun fileType(path: Path, filename: String): MimeType +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt index d2657985..35e0f060 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt @@ -16,8 +16,10 @@ package dev.usbharu.hideout.core.external.media +import dev.usbharu.hideout.core.domain.model.media.MimeType import java.nio.file.Path interface MediaProcessor { - suspend fun process(path: Path): ProcessedMedia + fun isSupported(mimeType: MimeType): Boolean + suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia } \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt index ac136c3c..b69ece17 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/ProcessedMedia.kt @@ -25,5 +25,5 @@ data class ProcessedMedia( val thumbnailPath: Path?, val fileType: FileType, val mimeType: MimeType, - val blurHash: String, + val blurHash: String?, ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt new file mode 100644 index 00000000..23ec5321 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt @@ -0,0 +1,51 @@ +package dev.usbharu.hideout.core.external.media + +import dev.usbharu.hideout.core.domain.model.media.FileType +import dev.usbharu.hideout.core.domain.model.media.MimeType +import org.apache.tika.Tika +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component +import java.nio.file.Path + +@Component +class TikaFileTypeDeterminer : FileTypeDeterminer { + override fun fileType(path: Path, filename: String): MimeType { + logger.info("START Detect file type name: {}", filename) + + val tika = Tika() + + val detect = try { + tika.detect(path) + } catch (e: IllegalStateException) { + logger.warn("FAILED Detect file type", e) + "application/octet-stream" + } + + val type = detect.substringBefore("/") + val fileType = when (type) { + "image" -> { + FileType.Image + } + + "video" -> { + FileType.Video + } + + "audio" -> { + FileType.Audio + } + + else -> { + FileType.Unknown + } + } + val mimeType = MimeType(type, detect.substringAfter("/"), fileType) + + logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType) + return mimeType + } + + companion object { + private val logger = LoggerFactory.getLogger(TikaFileTypeDeterminer::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt new file mode 100644 index 00000000..0e87f031 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt @@ -0,0 +1,51 @@ +package dev.usbharu.hideout.core.infrastructure.awss3 + +import dev.usbharu.hideout.core.config.S3StorageConfig +import dev.usbharu.hideout.core.external.mediastore.MediaStore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.stereotype.Component +import software.amazon.awssdk.core.sync.RequestBody +import software.amazon.awssdk.services.s3.S3Client +import software.amazon.awssdk.services.s3.model.PutObjectRequest +import java.net.URI +import java.nio.file.Path + +@Component +@ConditionalOnProperty("hideout.storage.type", havingValue = "s3") +class AWSS3MediaStore( + private val s3StorageConfig: S3StorageConfig, + private val s3Client: S3Client +) : MediaStore { + override suspend fun upload(path: Path, id: String): URI { + logger.info("MEDIA upload. {}", id) + + val fileUploadRequest = PutObjectRequest.builder() + .bucket(s3StorageConfig.bucket) + .key(id) + .build() + + logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, id) + + withContext(Dispatchers.IO) { + s3Client.putObject(fileUploadRequest, RequestBody.fromFile(path)) + } + val successSavedMedia = URI.create("${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${id}") + + + logger.info("SUCCESS Media upload. {}", id) + logger.debug( + "name: {} url: {}", + id, + successSavedMedia, + ) + + return successSavedMedia + } + + companion object { + private val logger = LoggerFactory.getLogger(AWSS3MediaStore::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt new file mode 100644 index 00000000..2c794504 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt @@ -0,0 +1,47 @@ +package dev.usbharu.hideout.core.infrastructure.localfilesystem + +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.config.LocalStorageConfig +import dev.usbharu.hideout.core.external.mediastore.MediaStore +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.stereotype.Component +import java.net.URI +import java.nio.file.Path +import kotlin.io.path.copyTo + +@Component +@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true) +class LocalFileSystemMediaStore( + localStorageConfig: LocalStorageConfig, + applicationConfig: ApplicationConfig +) : + MediaStore { + + private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/" + override suspend fun upload(path: Path, id: String): URI { + logger.info("START Media upload. {}", id) + val fileSavePath = buildSavePath(path, id) + + val fileSavePathString = fileSavePath.toAbsolutePath().toString() + logger.info("MEDIA save. path: {}", fileSavePathString) + + @Suppress("TooGenericExceptionCaught") try { + path.copyTo(fileSavePath) + } catch (e: Exception) { + logger.warn("FAILED to Save the media.", e) + throw e + } + + logger.info("SUCCESS Media upload. {}", id) + return URI.create(publicUrl).resolve(id) + } + + private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name) + + + companion object { + private val logger = LoggerFactory.getLogger(LocalFileSystemMediaStore::class.java) + } + +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt new file mode 100644 index 00000000..5febf86b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.infrastructure.media.common + +import java.awt.image.BufferedImage + +interface GenerateBlurhash { + fun generateBlurhash(bufferedImage: BufferedImage): String +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt new file mode 100644 index 00000000..332fa4e9 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt @@ -0,0 +1,12 @@ +package dev.usbharu.hideout.core.infrastructure.media.common + +import io.trbl.blurhash.BlurHash +import org.springframework.stereotype.Component +import java.awt.image.BufferedImage + +@Component +class GenerateBlurhashImpl : GenerateBlurhash { + override fun generateBlurhash(bufferedImage: BufferedImage): String { + return BlurHash.encode(bufferedImage) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt new file mode 100644 index 00000000..91c337af --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt @@ -0,0 +1,79 @@ +package dev.usbharu.hideout.core.infrastructure.media.image + +import dev.usbharu.hideout.core.config.ImageIOImageConfig +import dev.usbharu.hideout.core.domain.model.media.FileType +import dev.usbharu.hideout.core.domain.model.media.MimeType +import dev.usbharu.hideout.core.external.media.MediaProcessor +import dev.usbharu.hideout.core.external.media.ProcessedMedia +import dev.usbharu.hideout.core.infrastructure.media.common.GenerateBlurhash +import net.coobird.thumbnailator.Thumbnails +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component +import java.awt.Color +import java.awt.image.BufferedImage +import java.nio.file.Files +import java.nio.file.Path +import java.util.* +import javax.imageio.ImageIO +import kotlin.io.path.inputStream +import kotlin.io.path.outputStream + +@Component +@Qualifier("image") +class ImageIOImageProcessor( + private val imageIOImageConfig: ImageIOImageConfig, + private val blurhash: GenerateBlurhash +) : MediaProcessor { + override fun isSupported(mimeType: MimeType): Boolean { + return mimeType.fileType == FileType.Image || mimeType.type == "image" + } + + override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia { + val read = ImageIO.read(path.inputStream()) + + val bufferedImage = BufferedImage(read.width, read.height, BufferedImage.TYPE_INT_RGB) + + val graphics = bufferedImage.createGraphics() + + graphics.drawImage(read, 0, 0, Color.BLACK, null) + + val tempFileName = UUID.randomUUID().toString() + val tempFile = Files.createTempFile(tempFileName, "tmp") + + val thumbnailPath = run { + val tempThumbnailFile = Files.createTempFile("thumbnail-$tempFileName", ".tmp") + + tempThumbnailFile.outputStream().use { + val write = ImageIO.write( + Thumbnails.of(bufferedImage) + .size(imageIOImageConfig.thumbnailsWidth, imageIOImageConfig.thumbnailsHeight) + .imageType(BufferedImage.TYPE_INT_RGB) + .asBufferedImage(), + imageIOImageConfig.format, + it + ) + tempThumbnailFile.takeIf { write } + } + } + + tempFile.outputStream().use { + if (ImageIO.write(bufferedImage, imageIOImageConfig.format, it).not()) { + logger.warn("Failed to save a temporary file. type: {} ,path: {}", imageIOImageConfig.format, tempFile) + throw Exception("Failed to save a temporary file.") + } + } + + return ProcessedMedia( + tempFile, + thumbnailPath, + FileType.Image, + MimeType("image", imageIOImageConfig.format, FileType.Image), + blurhash.generateBlurhash(bufferedImage) + ) + } + + companion object { + private val logger = LoggerFactory.getLogger(ImageIOImageProcessor::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt new file mode 100644 index 00000000..ae1959dd --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt @@ -0,0 +1,117 @@ +package dev.usbharu.hideout.core.infrastructure.media.video + +import dev.usbharu.hideout.core.config.FFmpegVideoConfig +import dev.usbharu.hideout.core.domain.model.media.FileType +import dev.usbharu.hideout.core.domain.model.media.MimeType +import dev.usbharu.hideout.core.external.media.MediaProcessor +import dev.usbharu.hideout.core.external.media.ProcessedMedia +import dev.usbharu.hideout.core.infrastructure.media.common.GenerateBlurhash +import org.bytedeco.javacv.FFmpegFrameFilter +import org.bytedeco.javacv.FFmpegFrameGrabber +import org.bytedeco.javacv.FFmpegFrameRecorder +import org.bytedeco.javacv.Java2DFrameConverter +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component +import java.awt.image.BufferedImage +import java.nio.file.Files +import java.nio.file.Path +import javax.imageio.ImageIO +import kotlin.math.min + +@Component +@Qualifier("video") +class FFmpegVideoProcessor( + private val fFmpegVideoConfig: FFmpegVideoConfig, + private val generateBlurhash: GenerateBlurhash +) : MediaProcessor { + override fun isSupported(mimeType: MimeType): Boolean { + return mimeType.fileType == FileType.Video || mimeType.type == "video" + } + + override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia { + val tempFile = Files.createTempFile("hideout-movie-processor-", ".tmp") + val thumbnailFile = Files.createTempFile("hideout-movie-thumbnail-generate-", ".tmp") + logger.info("START Convert Movie Media {}", filename) + var bufferedImage: BufferedImage? = null + FFmpegFrameGrabber(path.toFile()).use { grabber -> + grabber.start() + val width = min(fFmpegVideoConfig.maxWidth, grabber.imageWidth) + val height = min(fFmpegVideoConfig.maxHeight, grabber.imageHeight) + val frameRate = fFmpegVideoConfig.frameRate + + logger.debug("Movie Media Width {}, Height {}", width, height) + + FFmpegFrameFilter( + "fps=fps=$frameRate", + "anull", + width, + height, + grabber.audioChannels + ).use { filter -> + + filter.sampleFormat = grabber.sampleFormat + filter.sampleRate = grabber.sampleRate + filter.pixelFormat = grabber.pixelFormat + filter.frameRate = grabber.frameRate + filter.start() + + val videoBitRate = min(fFmpegVideoConfig.maxBitrate, (width * height * frameRate * 1 * 0.07).toInt()) + + logger.debug("Movie Media BitRate {}", videoBitRate) + + FFmpegFrameRecorder(tempFile.toFile(), width, height, grabber.audioChannels).use { + it.sampleRate = grabber.sampleRate + it.format = fFmpegVideoConfig.format + it.videoCodec = fFmpegVideoConfig.videoCodec + it.audioCodec = fFmpegVideoConfig.audioCodec + it.audioChannels = grabber.audioChannels + it.videoQuality = fFmpegVideoConfig.videoQuality + it.frameRate = frameRate.toDouble() + it.setVideoOption("preset", "ultrafast") + it.timestamp = 0 + it.gopSize = frameRate + it.videoBitrate = videoBitRate + it.start() + + + val frameConverter = Java2DFrameConverter() + + while (true) { + val grab = grabber.grab() ?: break + + if (bufferedImage == null) { + bufferedImage = frameConverter.convert(grab) + } + + if (grab.image != null || grab.samples != null) { + filter.push(grab) + } + while (true) { + val frame = filter.pull() ?: break + it.record(frame) + } + } + + if (bufferedImage != null) { + ImageIO.write(bufferedImage, "jpeg", thumbnailFile.toFile()) + } + } + } + } + + logger.info("SUCCESS Convert Movie Media {}", filename) + + return ProcessedMedia( + tempFile, + thumbnailFile, + FileType.Video, + MimeType("video", fFmpegVideoConfig.format, FileType.Video), + bufferedImage?.let { generateBlurhash.generateBlurhash(it) } + ) + } + + companion object { + private val logger = LoggerFactory.getLogger(FFmpegVideoProcessor::class.java) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt index bfeca74c..09265d67 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedStatusQueryService.kt @@ -215,7 +215,7 @@ private fun toStatus(it: ResultRow) = Status( followingCount = it[Actors.followingCount], noindex = false, moved = false, - suspendex = false, + suspended = false, limited = false ), content = it[Posts.text], From 48a4874fc4bd0364b0cfbc94a9ebad66032f6ec9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:03:39 +0900 Subject: [PATCH 51/54] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?API=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../hideout/core/application/media/Media.kt | 18 +++++--- .../media/UploadMediaApplicationService.kt | 4 +- .../mastodon/interfaces/api/SpringMediaApi.kt | 46 ++++++++++++++++++- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index f078ee27..39e4144d 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ out/ *.log /hideout-core/files/ /hideout-core/.kotlin/sessions/ +/hideout-mastodon/.kotlin/sessions/ diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt index 99490b84..5ff3e4a8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/Media.kt @@ -22,9 +22,11 @@ import dev.usbharu.hideout.core.domain.model.media.MimeType import java.net.URI data class Media( + val id: Long, val name: String, val url: URI, val thumbprintURI: URI?, + val remoteURL: URI?, val type: FileType, val mimeType: MimeType, val blurHash: String?, @@ -33,13 +35,15 @@ data class Media( companion object { fun of(media: Media): dev.usbharu.hideout.core.application.media.Media { return Media( - media.name.name, - media.url, - media.thumbnailUrl, - media.type, - media.mimeType, - media.blurHash?.hash, - media.description?.description + id = media.id.id, + name = media.name.name, + url = media.url, + thumbprintURI = media.thumbnailUrl, + remoteURL = media.remoteUrl, + type = media.type, + mimeType = media.mimeType, + blurHash = media.blurHash?.hash, + description = media.description?.description ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt index f9d857e8..1ba8034a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt @@ -46,11 +46,11 @@ class UploadMediaApplicationService( val process = mediaProcessor.process(command.path, command.name, null) val id = idGenerateService.generateId() val thumbnailUri = if (process.thumbnailPath != null) { - mediaStore.upload(process.thumbnailPath, "thumbnail-$id") + mediaStore.upload(process.thumbnailPath, "thumbnail-$id.${process.mimeType.subtype}") } else { null } - val uri = mediaStore.upload(process.path, id.toString()) + val uri = mediaStore.upload(process.path, "$id.${process.mimeType.subtype}") val media = MediaModel( MediaId(id), diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt index 087bcb1c..a3a84a79 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt @@ -16,20 +16,62 @@ package dev.usbharu.hideout.mastodon.interfaces.api +import dev.usbharu.hideout.core.application.media.UploadMedia +import dev.usbharu.hideout.core.application.media.UploadMediaApplicationService +import dev.usbharu.hideout.core.domain.model.media.FileType.* +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory import dev.usbharu.hideout.mastodon.interfaces.api.generated.MediaApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller import org.springframework.web.multipart.MultipartFile +import java.nio.file.Files @Controller -class SpringMediaApi : MediaApi { +class SpringMediaApi( + private val uploadMediaApplicationService: UploadMediaApplicationService, + private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory +) : MediaApi { override suspend fun apiV1MediaPost( file: MultipartFile, thumbnail: MultipartFile?, description: String?, focus: String?, ): ResponseEntity { - return super.apiV1MediaPost(file, thumbnail, description, focus) + val tempFile = Files.createTempFile("hideout-tmp-file", ".tmp") + + Files.newOutputStream(tempFile).use { outputStream -> + file.inputStream.use { + it.transferTo(outputStream) + } + } + + val media = uploadMediaApplicationService.execute( + UploadMedia( + tempFile, + file.originalFilename ?: file.name, + null, + description + ), + oauth2CommandExecutorFactory.getCommandExecutor() + ) + + return ResponseEntity.ok( + MediaAttachment( + media.id.toString(), + when (media.type) { + Image -> MediaAttachment.Type.image + Video -> MediaAttachment.Type.video + Audio -> MediaAttachment.Type.audio + Unknown -> MediaAttachment.Type.unknown + }, + media.url.toString(), + media.thumbprintURI?.toString(), + media.remoteURL?.toString(), + media.description, + media.blurHash, + media.url.toASCIIString() + ) + ) } } \ No newline at end of file From 6b4c5e3567f30cbbb67971f867ba7507d6143465 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:30:29 +0900 Subject: [PATCH 52/54] =?UTF-8?q?chore:=20CI=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflows/pull-request-merge-check.yml | 152 +----------------- 1 file changed, 2 insertions(+), 150 deletions(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index a52f055e..d889b7e1 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -122,70 +122,6 @@ jobs: path: build/test-results key: unit-test-report-${{ github.sha }} - integration-test: - name: Integration Test - needs: [ setup ] - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Gradle Wrapper Cache - uses: actions/cache@v4.0.2 - with: - path: ~/.gradle/wrapper - key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} - - - name: Dependencies Cache - uses: actions/cache@v4.0.2 - with: - path: | - ~/.gradle/cache/jars-* - ~/.gradle/caches/transforms-* - ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies- - - - name: Cache - uses: actions/cache@v4.0.2 - with: - path: | - ~/.gradle/caches/build-cache-* - ~/.gradle/caches/[0-9]*.* - .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - - - name: Build Cache - uses: actions/cache@v4.0.2 - with: - path: | - build - key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} - - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - - - name: MongoDB in GitHub Actions - uses: supercharge/mongodb-github-action@1.11.0 - with: - mongodb-version: latest - - - name: Unit Test - uses: gradle/gradle-build-action@v3.3.2 - with: - arguments: :hideout-core:integrationTest - - - name: Save Test Report - if: always() - uses: actions/cache/save@v4 - with: - path: build/test-results - key: integration-test-report-${{ github.sha }} - coverage: name: Coverage needs: [ setup ] @@ -255,7 +191,7 @@ jobs: report-tests: name: Report Tests if: success() || failure() - needs: [ unit-test,integration-test,e2e-test ] + needs: [ unit-test ] runs-on: ubuntu-latest steps: - name: Restore Test Report @@ -264,18 +200,6 @@ jobs: path: build/test-results key: unit-test-report-${{ github.sha }} - - name: Restore Test Report - uses: actions/cache/restore@v4 - with: - path: build/test-results - key: integration-test-report-${{ github.sha }} - - - name: Restore Test Report - uses: actions/cache/restore@v4 - with: - path: build/test-results - key: e2e-test-report-${{ github.sha }} - - name: JUnit Test Report uses: mikepenz/action-junit-report@v4 with: @@ -337,76 +261,4 @@ jobs: if: ${{ always() }} uses: reviewdog/action-suggester@v1 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - - e2e-test: - name: E2E Test - needs: [ setup ] - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Gradle Wrapper Cache - uses: actions/cache@v4.0.2 - with: - path: ~/.gradle/wrapper - key: gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }} - - - name: Dependencies Cache - uses: actions/cache@v4.0.2 - with: - path: | - ~/.gradle/cache/jars-* - ~/.gradle/caches/transforms-* - ~/.gradle/caches/modules-* - key: gradle-dependencies-${{ hashFiles('**/*.gradle.kts') }} - restore-keys: gradle-dependencies- - - - name: Cache - uses: actions/cache@v4.0.2 - with: - path: | - ~/.gradle/caches/build-cache-* - ~/.gradle/caches/[0-9]*.* - .gradle - key: ${{ runner.os }}-gradle-build-${{ github.workflow }}-${{ github.sha }} - restore-keys: ${{ runner.os }}-gradle-build-${{ github.workflow }}- - - - name: Build Cache - uses: actions/cache@v4.0.2 - with: - path: | - build - key: gradle-build-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('src') }}-${{ github.sha }} - - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - - - name: MongoDB in GitHub Actions - uses: supercharge/mongodb-github-action@1.11.0 - with: - mongodb-version: latest - - - name: setup-chrome - id: setup-chrome - uses: browser-actions/setup-chrome@v1.7.1 - - - name: Add Path - run: echo ${{ steps.setup-chrome.outputs.chrome-path }} >> $GITHUB_PATH - - - name: E2E Test - uses: gradle/gradle-build-action@v3.3.2 - with: - arguments: :hideout-core:e2eTest - - - - name: Save Test Report - if: always() - uses: actions/cache/save@v4 - with: - path: build/test-results - key: e2e-test-report-${{ github.sha }} + github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 0b062b6a68f7b371bf455e9a0d22a437cf5151d8 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:38:29 +0900 Subject: [PATCH 53/54] =?UTF-8?q?chore:=20CI=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pull-request-merge-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-merge-check.yml b/.github/workflows/pull-request-merge-check.yml index 02337360..14021f90 100644 --- a/.github/workflows/pull-request-merge-check.yml +++ b/.github/workflows/pull-request-merge-check.yml @@ -172,7 +172,7 @@ jobs: - name: Run Kover uses: gradle/gradle-build-action@v3.3.2 with: - arguments: :hideout-core:koverXmlReport -x :hideout-core:integrationTest -x :hideout-core:e2eTest --rerun-tasks + arguments: :hideout-core:koverXmlReport --rerun-tasks - name: Add coverage report to PR if: always() From 446b87e3d90b1cd7aaa2197df9dc6ef2e5a6cf41 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:54:56 +0900 Subject: [PATCH 54/54] style: fix lint --- .../core/application/actor/UserDetail.kt | 34 +++++------ .../hideout/core/application/filter/Filter.kt | 2 +- .../UserDeleteFilterApplicationService.kt | 5 +- .../filter/UserGetFilterApplicationService.kt | 5 +- .../UserRegisterFilterApplicationService.kt | 18 +++--- .../InitLocalInstanceApplicationService.kt | 24 ++++---- .../media/UploadMediaApplicationService.kt | 24 ++++---- .../hideout/core/application/post/Post.kt | 26 ++++----- .../relationship/get/Relationship.kt | 24 ++++---- .../hideout/core/config/FFmpegVideoConfig.kt | 2 +- .../hideout/core/config/LocalStorageConfig.kt | 2 +- .../hideout/core/config/S3StorageConfig.kt | 2 +- .../core/domain/event/actor/ActorEvent.kt | 12 ++-- .../ActorInstanceRelationshipEvent.kt | 6 +- .../domain/event/instance/InstanceEvent.kt | 2 +- .../core/domain/event/post/PostEvent.kt | 8 +-- .../event/relationship/RelationshipEvent.kt | 16 +++--- .../hideout/core/domain/model/actor/Actor.kt | 19 ++++--- .../ActorInstanceRelationship.kt | 6 +- .../core/domain/model/filter/Filter.kt | 13 +++-- .../core/domain/model/filter/FilterAction.kt | 4 +- .../core/domain/model/filter/FilterContext.kt | 10 ++-- .../core/domain/model/instance/Instance.kt | 3 +- .../hideout/core/domain/model/post/Post.kt | 56 +++++++++---------- .../core/domain/model/post/PostContent.kt | 4 +- .../core/domain/model/post/PostOverview.kt | 2 +- .../domain/model/relationship/Relationship.kt | 16 +++--- .../core/domain/model/shared/Domain.kt | 4 +- .../domain/model/userdetails/UserDetail.kt | 24 ++++---- .../domain/shared/domainevent/DomainEvent.kt | 9 +-- .../shared/domainevent/DomainEventBody.kt | 5 +- .../shared/domainevent/DomainEventStorable.kt | 1 + .../external/media/DelegateMediaProcessor.kt | 6 +- ...eDeterminator.kt => FileTypeDeterminer.kt} | 2 +- .../core/external/media/MediaProcessor.kt | 2 +- .../external/media/TikaFileTypeDeterminer.kt | 2 +- .../core/external/mediastore/MediaStore.kt | 2 +- .../infrastructure/awss3/AWSS3MediaStore.kt | 5 +- .../exposed/FilterQueryMapper.kt | 25 +++++---- .../exposed/FilterResultRowMapper.kt | 2 +- ...osedActorInstanceRelationshipRepository.kt | 12 ++-- .../ExposedActorRepository.kt | 11 ++-- .../ExposedApplicationRepository.kt | 6 +- .../ExposedFilterRepository.kt | 8 +-- .../ExposedPostRepository.kt | 10 ++-- .../ExposedRelationshipRepository.kt | 6 +- .../exposedrepository/MediaRepositoryImpl.kt | 6 +- .../LocalFileSystemMediaStore.kt | 7 +-- .../media/common/GenerateBlurhash.kt | 2 +- .../media/common/GenerateBlurhashImpl.kt | 6 +- .../media/image/ImageIOImageProcessor.kt | 7 +-- .../media/video/FFmpegVideoProcessor.kt | 8 +-- .../springframework/HttpCommandExecutor.kt | 4 +- .../SpringSecurityPasswordEncoder.kt | 4 +- .../oauth2/HideoutUserDetails.kt | 19 +++---- .../interfaces/api/auth/AuthController.kt | 1 + .../core/domain/model/actor/ActorsTest.kt | 16 +++--- .../core/domain/model/post/PostTest.kt | 8 +-- .../filter/GetFilterV1ApplicationService.kt | 10 ++-- .../interfaces/api/SpringFilterApi.kt | 42 +++++++------- 60 files changed, 309 insertions(+), 318 deletions(-) rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/{FileTypeDeterminator.kt => FileTypeDeterminer.kt} (99%) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt index 6e066e3f..5b363296 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/UserDetail.kt @@ -47,23 +47,23 @@ data class UserDetail( customEmojis: List, ): dev.usbharu.hideout.core.application.actor.UserDetail { return UserDetail( - actor.id.id, - userDetail.id.id, - actor.name.name, - actor.domain.domain, - actor.screenName.screenName, - actor.url.toString(), - actor.url.toString(), - actor.description.description, - actor.locked, - customEmojis, - actor.createdAt, - actor.lastPostAt, - actor.postsCount.postsCount, - actor.followingCount?.relationshipCount, - actor.followersCount?.relationshipCount, - actor.moveTo?.id, - actor.suspend + id = actor.id.id, + userDetailId = userDetail.id.id, + name = actor.name.name, + domain = actor.domain.domain, + screenName = actor.screenName.screenName, + url = actor.url.toString(), + iconUrl = actor.url.toString(), + description = actor.description.description, + locked = actor.locked, + emojis = customEmojis, + createdAt = actor.createdAt, + lastPostAt = actor.lastPostAt, + postsCount = actor.postsCount.postsCount, + followingCount = actor.followingCount?.relationshipCount, + followersCount = actor.followersCount?.relationshipCount, + moveTo = actor.moveTo?.id, + suspend = actor.suspend ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt index 0231663e..e482991a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/Filter.kt @@ -46,4 +46,4 @@ data class Filter( ) } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt index 3b032fc4..2e28d5d0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt @@ -27,7 +27,8 @@ import org.springframework.stereotype.Service @Service class UserDeleteFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : AbstractApplicationService( - transaction, logger + transaction, + logger ) { companion object { private val logger = LoggerFactory.getLogger(UserDeleteFilterApplicationService::class.java) @@ -37,4 +38,4 @@ class UserDeleteFilterApplicationService(private val filterRepository: FilterRep val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("not found") filterRepository.delete(filter) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt index bf10cc6b..0ecf97f8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt @@ -27,7 +27,8 @@ import org.springframework.stereotype.Service @Service class UserGetFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : AbstractApplicationService( - transaction, logger + transaction, + logger ) { override suspend fun internalExecute(command: GetFilter, executor: CommandExecutor): Filter { val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("Not Found") @@ -38,4 +39,4 @@ class UserGetFilterApplicationService(private val filterRepository: FilterReposi companion object { private val logger = LoggerFactory.getLogger(UserGetFilterApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt index a1dc48a3..ac7a1309 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt @@ -34,7 +34,8 @@ class UserRegisterFilterApplicationService( transaction: Transaction, ) : AbstractApplicationService( - transaction, logger + transaction, + logger ) { companion object { @@ -44,14 +45,13 @@ class UserRegisterFilterApplicationService( override suspend fun internalExecute(command: RegisterFilter, executor: CommandExecutor): Filter { require(executor is UserDetailGettableCommandExecutor) - val filter = dev.usbharu.hideout.core.domain.model.filter.Filter.create( - FilterId(idGenerateService.generateId()), - UserDetailId(executor.userDetailId), - FilterName(command.filterName), - command.filterContext, - command.filterAction, - command.filterKeywords + id = FilterId(idGenerateService.generateId()), + userDetailId = UserDetailId(executor.userDetailId), + name = FilterName(command.filterName), + filterContext = command.filterContext, + filterAction = command.filterAction, + filterKeywords = command.filterKeywords .map { FilterKeyword( FilterKeywordId(idGenerateService.generateId()), @@ -64,4 +64,4 @@ class UserRegisterFilterApplicationService( filterRepository.save(filter) return Filter.of(filter) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt index 0a8a6e98..77d2e41b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/instance/InitLocalInstanceApplicationService.kt @@ -40,18 +40,18 @@ class InitLocalInstanceApplicationService( if (findByUrl == null) { val instance = Instance( - InstanceId(idGenerateService.generateId()), - InstanceName(applicationConfig.url.host), - InstanceDescription(""), - applicationConfig.url.toURI(), - applicationConfig.url.toURI(), - null, - InstanceSoftware("hideout"), - InstanceVersion(buildProperties.version), - false, - false, - InstanceModerationNote(""), - Instant.now(), + id = InstanceId(idGenerateService.generateId()), + name = InstanceName(applicationConfig.url.host), + description = InstanceDescription(""), + url = applicationConfig.url.toURI(), + iconUrl = applicationConfig.url.toURI(), + sharedInbox = null, + software = InstanceSoftware("hideout"), + version = InstanceVersion(buildProperties.version), + isBlocked = false, + isMuted = false, + moderationNote = InstanceModerationNote(""), + createdAt = Instant.now(), ) instanceRepository.save(instance) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt index 1ba8034a..e3ba618e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt @@ -36,7 +36,8 @@ class UploadMediaApplicationService( private val idGenerateService: IdGenerateService, transaction: Transaction ) : AbstractApplicationService( - transaction, logger + transaction, + logger ) { companion object { private val logger = LoggerFactory.getLogger(UploadMediaApplicationService::class.java) @@ -53,20 +54,19 @@ class UploadMediaApplicationService( val uri = mediaStore.upload(process.path, "$id.${process.mimeType.subtype}") val media = MediaModel( - MediaId(id), - MediaName(command.name), - uri, - command.remoteUri, - thumbnailUri, - process.fileType, - process.mimeType, - process.blurHash?.let { MediaBlurHash(it) }, - command.description?.let { MediaDescription(it) } + id = MediaId(id), + name = MediaName(command.name), + url = uri, + remoteUrl = command.remoteUri, + thumbnailUrl = thumbnailUri, + type = process.fileType, + mimeType = process.mimeType, + blurHash = process.blurHash?.let { MediaBlurHash(it) }, + description = command.description?.let { MediaDescription(it) } ) mediaRepository.save(media) return Media.of(media) - } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt index 538b8911..2ed11666 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/Post.kt @@ -39,19 +39,19 @@ data class Post( companion object { fun of(post: Post): dev.usbharu.hideout.core.application.post.Post { return Post( - post.id.id, - post.actorId.id, - post.overview?.overview, - post.text, - post.content.content, - post.createdAt, - post.visibility, - post.url, - post.repostId?.id, - post.replyId?.id, - post.sensitive, - post.mediaIds.map { it.id }, - post.moveTo?.id + id = post.id.id, + actorId = post.actorId.id, + overview = post.overview?.overview, + text = post.text, + content = post.content.content, + createdAt = post.createdAt, + visibility = post.visibility, + url = post.url, + repostId = post.repostId?.id, + replyId = post.replyId?.id, + sensitive = post.sensitive, + mediaIds = post.mediaIds.map { it.id }, + moveTo = post.moveTo?.id ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt index b14c78fd..4893d62a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/Relationship.kt @@ -40,18 +40,18 @@ data class Relationship( actorInstanceRelationship: ActorInstanceRelationship, ): dev.usbharu.hideout.core.application.relationship.get.Relationship { return Relationship( - relationship.actorId.id, - relationship.targetActorId.id, - relationship.following, - relationship2.following, - relationship.blocking, - relationship2.blocking, - relationship.muting, - relationship.followRequesting, - relationship2.followRequesting, - actorInstanceRelationship.isBlocking(), - actorInstanceRelationship.isMuting(), - actorInstanceRelationship.isDoNotSendPrivate() + actorId = relationship.actorId.id, + targetId = relationship.targetActorId.id, + following = relationship.following, + followedBy = relationship2.following, + blocking = relationship.blocking, + blockedBy = relationship2.blocking, + muting = relationship.muting, + followRequesting = relationship.followRequesting, + followRequestedBy = relationship2.followRequesting, + domainBlocking = actorInstanceRelationship.isBlocking(), + domainMuting = actorInstanceRelationship.isMuting(), + domainDoNotSendPrivate = actorInstanceRelationship.isDoNotSendPrivate() ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt index 6f19a56b..65e5f9be 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/FFmpegVideoConfig.kt @@ -14,4 +14,4 @@ data class FFmpegVideoConfig( val videoQuality: Double = 1.0, val videoOption: List = listOf("preset", "ultrafast"), val maxBitrate: Int = 1300000, -) \ No newline at end of file +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt index a5cfc2e8..48962c11 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/LocalStorageConfig.kt @@ -14,4 +14,4 @@ import org.springframework.boot.context.properties.ConfigurationProperties data class LocalStorageConfig( val path: String = "files", val publicUrl: String? -) \ No newline at end of file +) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt index fa523441..1a8b5677 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/S3StorageConfig.kt @@ -31,4 +31,4 @@ class AwsConfig { .credentialsProvider { AwsBasicCredentials.create(awsConfig.accessKey, awsConfig.secretKey) } .build() } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt index a03f92e6..69e154eb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actor/ActorEvent.kt @@ -37,10 +37,10 @@ class ActorEventBody(actor: Actor) : DomainEventBody( ) enum class ActorEvent(val eventName: String, val collectable: Boolean = true) { - update("ActorUpdate"), - delete("ActorDelete"), - checkUpdate("ActorCheckUpdate"), - move("ActorMove"), - actorSuspend("ActorSuspend"), - actorUnsuspend("ActorUnsuspend"), + UPDATE("ActorUpdate"), + DELETE("ActorDelete"), + CHECK_UPDATE("ActorCheckUpdate"), + MOVE("ActorMove"), + ACTOR_SUSPEND("ActorSuspend"), + ACTOR_UNSUSPEND("ActorUnsuspend"), } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt index c5c6daf7..5ff3fc5f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/actorinstancerelationship/ActorInstanceRelationshipEvent.kt @@ -41,7 +41,7 @@ class ActorInstanceRelationshipEventBody(actorInstanceRelationship: ActorInstanc ) enum class ActorInstanceRelationshipEvent(val eventName: String) { - block("ActorInstanceBlock"), - mute("ActorInstanceMute"), - unmute("ActorInstanceUnmute"), + BLOCK("ActorInstanceBlock"), + MUTE("ActorInstanceMute"), + UNMUTE("ActorInstanceUnmute"), } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt index 276d2f75..72f0941a 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/instance/InstanceEvent.kt @@ -32,5 +32,5 @@ class InstanceEventFactory(private val instance: Instance) { class InstanceEventBody(instance: Instance) : DomainEventBody(mapOf("instance" to instance)) enum class InstanceEvent(val eventName: String) { - update("InstanceUpdate") + UPDATE("InstanceUpdate") } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt index ad0fd36f..5020d056 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/post/PostEvent.kt @@ -33,8 +33,8 @@ class PostDomainEventFactory(private val post: Post, private val actor: Actor? = class PostEventBody(post: Post, actor: Actor?) : DomainEventBody(mapOf("post" to post, "actor" to actor)) enum class PostEvent(val eventName: String) { - delete("PostDelete"), - update("PostUpdate"), - create("PostCreate"), - checkUpdate("PostCheckUpdate"), + DELETE("PostDelete"), + UPDATE("PostUpdate"), + CREATE("PostCreate"), + CHECK_UPDATE("PostCheckUpdate"), } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt index 38183454..89adf3e8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/relationship/RelationshipEvent.kt @@ -29,12 +29,12 @@ class RelationshipEventFactory(private val relationship: Relationship) { class RelationshipEventBody(relationship: Relationship) : DomainEventBody(mapOf("relationship" to relationship)) enum class RelationshipEvent(val eventName: String) { - follow("RelationshipFollow"), - unfollow("RelationshipUnfollow"), - block("RelationshipBlock"), - unblock("RelationshipUnblock"), - mute("RelationshipMute"), - unmute("RelationshipUnmute"), - followRequest("RelationshipFollowRequest"), - unfollowRequest("RelationshipUnfollowRequest"), + FOLLOW("RelationshipFollow"), + UNFOLLOW("RelationshipUnfollow"), + BLOCK("RelationshipBlock"), + UNBLOCK("RelationshipUnblock"), + MUTE("RelationshipMute"), + UNMUTE("RelationshipUnmute"), + FOLLOW_REQUEST("RelationshipFollowRequest"), + UNFOLLOW_REQUEST("RelationshipUnfollowRequest"), } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 73f5d15c..45d34c4d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -26,6 +26,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant +@Suppress("LongParameterList") class Actor( val id: ActorId, val name: ActorName, @@ -62,7 +63,7 @@ class Actor( private set fun setBannerUrl(banner: MediaId?, actor: Actor) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(UPDATE)) this.banner = banner } @@ -70,7 +71,7 @@ class Actor( private set fun setIconUrl(icon: MediaId?, actor: Actor) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(UPDATE)) this.icon = icon } @@ -86,9 +87,9 @@ class Actor( var suspend = suspend set(value) { if (field != value && value) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(actorSuspend)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(ACTOR_SUSPEND)) } else if (field != value && !value) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(actorUnsuspend)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(ACTOR_UNSUSPEND)) } field = value } @@ -102,7 +103,7 @@ class Actor( var moveTo = moveTo set(value) { require(value != id) - addDomainEvent(ActorDomainEventFactory(this).createEvent(move)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(MOVE)) field = value } @@ -111,12 +112,12 @@ class Actor( var description = description set(value) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(UPDATE)) field = value } var screenName = screenName set(value) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(update)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(UPDATE)) field = value } @@ -125,7 +126,7 @@ class Actor( fun delete() { if (deleted.not()) { - addDomainEvent(ActorDomainEventFactory(this).createEvent(delete)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(DELETE)) screenName = ActorScreenName.empty description = ActorDescription.empty emojis = emptySet() @@ -142,6 +143,6 @@ class Actor( } fun checkUpdate() { - addDomainEvent(ActorDomainEventFactory(this).createEvent(checkUpdate)) + addDomainEvent(ActorDomainEventFactory(this).createEvent(CHECK_UPDATE)) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt index 3cef6c86..f4244ca5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actorinstancerelationship/ActorInstanceRelationship.kt @@ -30,7 +30,7 @@ data class ActorInstanceRelationship( private var doNotSendPrivate: Boolean = false, ) : DomainEventStorable() { fun block(): ActorInstanceRelationship { - addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(block)) + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(BLOCK)) blocking = true return this } @@ -41,13 +41,13 @@ data class ActorInstanceRelationship( } fun mute(): ActorInstanceRelationship { - addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(mute)) + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(MUTE)) muting = true return this } fun unmute(): ActorInstanceRelationship { - addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(unmute)) + addDomainEvent(ActorInstanceRelationshipDomainEventFactory(this).createEvent(UNMUTE)) muting = false return this } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt index e174ec3b..41a835b3 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -41,12 +41,12 @@ class Filter( fun reconstructWith(filterKeywords: Set): Filter { return Filter( - this.id, - this.userDetailId, - this.name, - this.filterContext, - this.filterAction, - filterKeywords + id = this.id, + userDetailId = this.userDetailId, + name = this.name, + filterContext = this.filterContext, + filterAction = this.filterAction, + filterKeywords = filterKeywords ) } @@ -61,6 +61,7 @@ class Filter( SET_KEYWORDS } + @Suppress("LongParameterList") fun create( id: FilterId, userDetailId: UserDetailId, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt index ac3e8b88..d19c8db8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterAction.kt @@ -1,6 +1,6 @@ package dev.usbharu.hideout.core.domain.model.filter enum class FilterAction { - warn, - hide + WARN, + HIDE } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt index 418f9573..df987e3d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/FilterContext.kt @@ -1,9 +1,9 @@ package dev.usbharu.hideout.core.domain.model.filter enum class FilterContext { - home, - notifications, - public, - thread, - account + HOME, + NOTIFICATION, + PUBLIC, + THREAD, + ACCOUNT } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index 28e1f6d4..4bda1989 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -22,6 +22,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant +@Suppress("LongParameterList") class Instance( val id: InstanceId, var name: InstanceName, @@ -39,7 +40,7 @@ class Instance( var iconUrl = iconUrl set(value) { - addDomainEvent(InstanceEventFactory(this).createEvent(InstanceEvent.update)) + addDomainEvent(InstanceEventFactory(this).createEvent(InstanceEvent.UPDATE)) field = value } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt index db7f1693..9bf9211c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/Post.kt @@ -28,6 +28,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable import java.net.URI import java.time.Instant +@Suppress("LongParameterList", "TooManyFunctions") class Post( val id: PostId, actorId: ActorId, @@ -67,7 +68,7 @@ class Post( require(deleted.not()) if (this.visibility != visibility) { - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.UPDATE)) } this.visibility = visibility } @@ -79,7 +80,7 @@ class Post( require(isAllow(actor, UPDATE, this)) require(deleted.not()) if (visibility == Visibility.DIRECT) { - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.UPDATE)) this.visibleActors = this.visibleActors.plus(visibleActors) } } @@ -97,7 +98,7 @@ class Post( require(isAllow(actor, UPDATE, this)) require(deleted.not()) if (this.content != content) { - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.UPDATE)) } this.content = content } @@ -115,7 +116,7 @@ class Post( require(isAllow(actor, UPDATE, this)) require(deleted.not()) if (this.overview != overview) { - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.UPDATE)) } this.overview = overview } @@ -127,7 +128,7 @@ class Post( isAllow(actor, UPDATE, this) require(deleted.not()) if (this.sensitive != sensitive) { - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.UPDATE)) } this.sensitive = sensitive } @@ -160,7 +161,7 @@ class Post( fun addMediaIds(mediaIds: List, actor: Actor) { require(isAllow(actor, UPDATE, this)) require(deleted.not()) - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.update)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.UPDATE)) this.mediaIds = this.mediaIds.plus(mediaIds).distinct() } @@ -170,7 +171,7 @@ class Post( fun delete(actor: Actor) { isAllow(actor, DELETE, this) if (deleted.not()) { - addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.delete)) + addDomainEvent(PostDomainEventFactory(this, actor).createEvent(PostEvent.DELETE)) content = PostContent.empty overview = null mediaIds = emptyList() @@ -179,7 +180,7 @@ class Post( } fun checkUpdate() { - addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.checkUpdate)) + addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.CHECK_UPDATE)) } fun restore(content: PostContent, overview: PostOverview?, mediaIds: List) { @@ -219,9 +220,7 @@ class Post( return id == other.id } - override fun hashCode(): Int { - return id.hashCode() - } + override fun hashCode(): Int = id.hashCode() fun reconstructWith(mediaIds: List, emojis: List, visibleActors: Set): Post { return Post( @@ -245,6 +244,7 @@ class Post( } companion object { + @Suppress("LongParameterList") fun create( id: PostId, actorId: ActorId, @@ -274,24 +274,24 @@ class Post( } val post = Post( - id, - actorId, - overview, - content, - createdAt, - visibility1, - url, - repostId, - replyId, - sensitive, - apId, - deleted, - mediaIds, - visibleActors, - hide, - moveTo + id = id, + actorId = actorId, + overview = overview, + content = content, + createdAt = createdAt, + visibility = visibility1, + url = url, + repostId = repostId, + replyId = replyId, + sensitive = sensitive, + apId = apId, + deleted = deleted, + mediaIds = mediaIds, + visibleActors = visibleActors, + hide = hide, + moveTo = moveTo ) - post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.create)) + post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.CREATE)) return post } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt index d8e0a18d..2d787e75 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostContent.kt @@ -22,7 +22,7 @@ data class PostContent(val text: String, val content: String, val emojiIds: List companion object { val empty = PostContent("", "", emptyList()) - val contentLength = 5000 - val textLength = 3000 + const val CONTENT_LENGTH = 5000 + const val TEXT_LENGTH = 3000 } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt index 1c2419bb..0793febe 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/post/PostOverview.kt @@ -19,6 +19,6 @@ package dev.usbharu.hideout.core.domain.model.post @JvmInline value class PostOverview(val overview: String) { companion object { - val length = 100 + const val LENGTH = 100 } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt index e4893712..068de901 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/Relationship.kt @@ -46,33 +46,33 @@ class Relationship( fun follow() { require(blocking.not()) following = true - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.follow)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.FOLLOW)) } fun unfollow() { following = false - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unfollow)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.UNFOLLOW)) } fun block() { require(following.not()) blocking = true - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.block)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.BLOCK)) } fun unblock() { blocking = false - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unblock)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.UNBLOCK)) } fun mute() { muting = true - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.mute)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.MUTE)) } fun unmute() { muting = false - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unmute)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.UNMUTE)) } fun muteFollowRequest() { @@ -86,12 +86,12 @@ class Relationship( fun followRequest() { require(blocking.not()) followRequesting = true - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.followRequest)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.FOLLOW_REQUEST)) } fun unfollowRequest() { followRequesting = false - addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.unfollowRequest)) + addDomainEvent(RelationshipEventFactory(this).createEvent(RelationshipEvent.UNFOLLOW_REQUEST)) } fun acceptFollowRequest() { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt index 963c2574..cf6f9f1b 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/shared/Domain.kt @@ -19,10 +19,10 @@ package dev.usbharu.hideout.core.domain.model.shared @JvmInline value class Domain(val domain: String) { init { - require(domain.length <= length) + require(domain.length <= LENGTH) } companion object { - val length = 1000 + const val LENGTH = 1000 } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt index 52016f71..e41a86b8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/userdetails/UserDetail.kt @@ -27,6 +27,17 @@ class UserDetail private constructor( var lastMigration: Instant? = null, ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UserDetail + + return id == other.id + } + + override fun hashCode(): Int = id.hashCode() + companion object { fun create( id: UserDetailId, @@ -44,17 +55,4 @@ class UserDetail private constructor( ) } } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as UserDetail - - return id == other.id - } - - override fun hashCode(): Int { - return id.hashCode() - } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt index a30cb94c..d4379335 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEvent.kt @@ -36,9 +36,8 @@ data class DomainEvent( val collectable: Boolean = false ) { companion object { - fun create(name: String, body: DomainEventBody, collectable: Boolean = false): DomainEvent { - return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body, collectable) - } + fun create(name: String, body: DomainEventBody, collectable: Boolean = false): DomainEvent = + DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body, collectable) fun reconstruct( id: String, @@ -46,8 +45,6 @@ data class DomainEvent( occurredOn: Instant, body: DomainEventBody, collectable: Boolean - ): DomainEvent { - return DomainEvent(id, name, occurredOn, body, collectable) - } + ): DomainEvent = DomainEvent(id, name, occurredOn, body, collectable) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt index 808c6bdf..f4cd139d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventBody.kt @@ -16,8 +16,7 @@ package dev.usbharu.hideout.core.domain.shared.domainevent +@Suppress("UnnecessaryAbstractClass") abstract class DomainEventBody(val map: Map) { - fun toMap(): Map { - return map - } + fun toMap(): Map = map } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt index 3fedb624..6e6b83b7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/shared/domainevent/DomainEventStorable.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.domain.shared.domainevent +@Suppress("UnnecessaryAbstractClass") abstract class DomainEventStorable { private val domainEvents: MutableList = mutableListOf() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt index 96607974..65cbc14d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt @@ -11,12 +11,10 @@ class DelegateMediaProcessor( private val fileTypeDeterminer: FileTypeDeterminer, private val mediaProcessors: List ) : MediaProcessor { - override fun isSupported(mimeType: MimeType): Boolean { - return true - } + override fun isSupported(mimeType: MimeType): Boolean = true override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia { val fileType = fileTypeDeterminer.fileType(path, filename) return mediaProcessors.first { it.isSupported(fileType) }.process(path, filename, fileType) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminer.kt similarity index 99% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminer.kt index 199ac249..83270bb6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminer.kt @@ -5,4 +5,4 @@ import java.nio.file.Path interface FileTypeDeterminer { fun fileType(path: Path, filename: String): MimeType -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt index 35e0f060..687aa718 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/MediaProcessor.kt @@ -22,4 +22,4 @@ import java.nio.file.Path interface MediaProcessor { fun isSupported(mimeType: MimeType): Boolean suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt index 23ec5321..393798b7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt @@ -48,4 +48,4 @@ class TikaFileTypeDeterminer : FileTypeDeterminer { companion object { private val logger = LoggerFactory.getLogger(TikaFileTypeDeterminer::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt index dc34540e..d2ee30d1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/mediastore/MediaStore.kt @@ -5,4 +5,4 @@ import java.nio.file.Path interface MediaStore { suspend fun upload(path: Path, id: String): URI -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt index 0e87f031..47e30858 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/awss3/AWSS3MediaStore.kt @@ -32,8 +32,7 @@ class AWSS3MediaStore( withContext(Dispatchers.IO) { s3Client.putObject(fileUploadRequest, RequestBody.fromFile(path)) } - val successSavedMedia = URI.create("${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${id}") - + val successSavedMedia = URI.create("${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$id") logger.info("SUCCESS Media upload. {}", id) logger.debug( @@ -48,4 +47,4 @@ class AWSS3MediaStore( companion object { private val logger = LoggerFactory.getLogger(AWSS3MediaStore::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt index 9fe7081d..5b6039c4 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterQueryMapper.kt @@ -34,17 +34,20 @@ class FilterQueryMapper(private val filterResultRowMapper: ResultRowMapper - FilterKeyword( - FilterKeywordId(resultRow.getOrNull(FilterKeywords.id) ?: return@mapNotNull null), - FilterKeywordKeyword( - resultRow.getOrNull(FilterKeywords.keyword) ?: return@mapNotNull null - ), - FilterMode.valueOf(resultRow.getOrNull(FilterKeywords.mode) ?: return@mapNotNull null) - ) - - }.toSet()) + reconstructWith( + it.mapNotNull { resultRow: ResultRow -> + FilterKeyword( + FilterKeywordId(resultRow.getOrNull(FilterKeywords.id) ?: return@mapNotNull null), + FilterKeywordKeyword( + resultRow.getOrNull(FilterKeywords.keyword) ?: return@mapNotNull null + ), + FilterMode.valueOf( + resultRow.getOrNull(FilterKeywords.mode) ?: return@mapNotNull null + ) + ) + }.toSet() + ) } } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt index 13221bd7..e668f045 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/FilterResultRowMapper.kt @@ -32,4 +32,4 @@ class FilterResultRowMapper : ResultRowMapper { FilterAction.valueOf(resultRow[Filters.filterAction]), emptySet() ) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt index d32a3c59..ab7ab447 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorInstanceRelationshipRepository.kt @@ -33,6 +33,9 @@ class ExposedActorInstanceRelationshipRepository(override val domainEventPublish ActorInstanceRelationshipRepository, AbstractRepository(), DomainEventPublishableRepository { + override val logger: Logger + get() = Companion.logger + override suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship { query { ActorInstanceRelationships.upsert { @@ -50,7 +53,8 @@ class ExposedActorInstanceRelationshipRepository(override val domainEventPublish override suspend fun delete(actorInstanceRelationship: ActorInstanceRelationship) { query { ActorInstanceRelationships.deleteWhere { - actorId eq actorInstanceRelationship.actorId.id and (instanceId eq actorInstanceRelationship.instanceId.instanceId) + actorId eq actorInstanceRelationship.actorId.id and + (instanceId eq actorInstanceRelationship.instanceId.instanceId) } } update(actorInstanceRelationship) @@ -63,15 +67,13 @@ class ExposedActorInstanceRelationshipRepository(override val domainEventPublish ActorInstanceRelationships .selectAll() .where { - ActorInstanceRelationships.actorId eq actorId.id and (ActorInstanceRelationships.instanceId eq instanceId.instanceId) + ActorInstanceRelationships.actorId eq actorId.id and + (ActorInstanceRelationships.instanceId eq instanceId.instanceId) } .singleOrNull() ?.toActorInstanceRelationship() } - override val logger: Logger - get() = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedActorInstanceRelationshipRepository::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt index fbdc1d62..8e184021 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedActorRepository.kt @@ -22,10 +22,6 @@ class ExposedActorRepository( override val logger: Logger get() = Companion.logger - companion object { - private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java) - } - override suspend fun save(actor: Actor): Actor { query { Actors.upsert { @@ -55,7 +51,6 @@ class ExposedActorRepository( it[emojis] = actor.emojis.joinToString(",") it[icon] = actor.icon?.id it[banner] = actor.banner?.id - } ActorsAlsoKnownAs.deleteWhere { actorId eq actor.id.id @@ -102,12 +97,16 @@ class ExposedActorRepository( .firstOrNull() } } + + companion object { + private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java) + } } object Actors : Table("actors") { val id = long("id") val name = varchar("name", ActorName.length) - val domain = varchar("domain", Domain.length) + val domain = varchar("domain", Domain.LENGTH) val screenName = varchar("screen_name", ActorScreenName.length) val description = varchar("description", ActorDescription.length) val inbox = varchar("inbox", 1000).uniqueIndex() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt index f2d2552e..42d5c460 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedApplicationRepository.kt @@ -28,6 +28,9 @@ import org.springframework.stereotype.Repository @Repository class ExposedApplicationRepository : ApplicationRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger + override suspend fun save(application: Application) = query { Applications.upsert { it[id] = application.applicationId.id @@ -40,9 +43,6 @@ class ExposedApplicationRepository : ApplicationRepository, AbstractRepository() Applications.deleteWhere { id eq application.applicationId.id } } - override val logger: Logger - get() = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedApplicationRepository::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt index 0bcbfc8e..9db15afb 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedFilterRepository.kt @@ -30,6 +30,9 @@ import org.springframework.stereotype.Repository @Repository class ExposedFilterRepository(private val filterQueryMapper: QueryMapper) : FilterRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger + override suspend fun save(filter: Filter): Filter = query { Filters.upsert { it[id] = filter.id.id @@ -69,9 +72,6 @@ class ExposedFilterRepository(private val filterQueryMapper: QueryMapper return filterQueryMapper.map(where).firstOrNull() } - override val logger: Logger - get() = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedFilterRepository::class.java) } @@ -95,4 +95,4 @@ object FilterKeywords : Table("filter_keywords") { val mode = varchar("mode", 100) override val primaryKey: PrimaryKey = PrimaryKey(id) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt index b02351d7..43d63e27 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedPostRepository.kt @@ -52,6 +52,8 @@ class ExposedPostRepository( PostRepository, AbstractRepository(), DomainEventPublishableRepository { + override val logger: Logger = Companion.logger + override suspend fun save(post: Post): Post { query { Posts.upsert { @@ -176,8 +178,6 @@ class ExposedPostRepository( update(post) } - override val logger: Logger = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java) } @@ -186,9 +186,9 @@ class ExposedPostRepository( object Posts : Table("posts") { val id = long("id") val actorId = long("actor_id").references(Actors.id) - val overview = varchar("overview", PostOverview.length).nullable() - val content = varchar("content", PostContent.contentLength) - val text = varchar("text", PostContent.textLength) + val overview = varchar("overview", PostOverview.LENGTH).nullable() + val content = varchar("content", PostContent.CONTENT_LENGTH) + val text = varchar("text", PostContent.TEXT_LENGTH) val createdAt = timestamp("created_at") val visibility = varchar("visibility", 100) val url = varchar("url", 1000) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt index e44b286f..c658754c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedRelationshipRepository.kt @@ -32,6 +32,9 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve RelationshipRepository, AbstractRepository(), DomainEventPublishableRepository { + override val logger: Logger + get() = Companion.logger + override suspend fun save(relationship: Relationship): Relationship { query { Relationships.upsert { @@ -63,9 +66,6 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve }.singleOrNull()?.toRelationships() } - override val logger: Logger - get() = Companion.logger - companion object { private val logger = LoggerFactory.getLogger(ExposedRelationshipRepository::class.java) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt index 92306fbf..b9ab43de 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/MediaRepositoryImpl.kt @@ -27,6 +27,9 @@ import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia @Repository class MediaRepositoryImpl : MediaRepository, AbstractRepository() { + override val logger: Logger + get() = Companion.logger + override suspend fun findById(id: MediaId): dev.usbharu.hideout.core.domain.model.media.Media? { return query { return@query Media @@ -42,9 +45,6 @@ class MediaRepositoryImpl : MediaRepository, AbstractRepository() { } } - override val logger: Logger - get() = Companion.logger - override suspend fun save(media: EntityMedia): EntityMedia = query { if (Media.selectAll().where { Media.id eq media.id.id }.forUpdate().singleOrNull() != null ) { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt index 2c794504..607ab397 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/localfilesystem/LocalFileSystemMediaStore.kt @@ -26,7 +26,8 @@ class LocalFileSystemMediaStore( val fileSavePathString = fileSavePath.toAbsolutePath().toString() logger.info("MEDIA save. path: {}", fileSavePathString) - @Suppress("TooGenericExceptionCaught") try { + @Suppress("TooGenericExceptionCaught") + try { path.copyTo(fileSavePath) } catch (e: Exception) { logger.warn("FAILED to Save the media.", e) @@ -39,9 +40,7 @@ class LocalFileSystemMediaStore( private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name) - companion object { private val logger = LoggerFactory.getLogger(LocalFileSystemMediaStore::class.java) } - -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt index 5febf86b..6ee2893d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhash.kt @@ -4,4 +4,4 @@ import java.awt.image.BufferedImage interface GenerateBlurhash { fun generateBlurhash(bufferedImage: BufferedImage): String -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt index 332fa4e9..cb269364 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/common/GenerateBlurhashImpl.kt @@ -6,7 +6,5 @@ import java.awt.image.BufferedImage @Component class GenerateBlurhashImpl : GenerateBlurhash { - override fun generateBlurhash(bufferedImage: BufferedImage): String { - return BlurHash.encode(bufferedImage) - } -} \ No newline at end of file + override fun generateBlurhash(bufferedImage: BufferedImage): String = BlurHash.encode(bufferedImage) +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt index 91c337af..b297ff51 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/image/ImageIOImageProcessor.kt @@ -25,9 +25,8 @@ class ImageIOImageProcessor( private val imageIOImageConfig: ImageIOImageConfig, private val blurhash: GenerateBlurhash ) : MediaProcessor { - override fun isSupported(mimeType: MimeType): Boolean { - return mimeType.fileType == FileType.Image || mimeType.type == "image" - } + override fun isSupported(mimeType: MimeType): Boolean = + mimeType.fileType == FileType.Image || mimeType.type == "image" override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia { val read = ImageIO.read(path.inputStream()) @@ -76,4 +75,4 @@ class ImageIOImageProcessor( companion object { private val logger = LoggerFactory.getLogger(ImageIOImageProcessor::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt index ae1959dd..22417894 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/media/video/FFmpegVideoProcessor.kt @@ -25,9 +25,8 @@ class FFmpegVideoProcessor( private val fFmpegVideoConfig: FFmpegVideoConfig, private val generateBlurhash: GenerateBlurhash ) : MediaProcessor { - override fun isSupported(mimeType: MimeType): Boolean { - return mimeType.fileType == FileType.Video || mimeType.type == "video" - } + override fun isSupported(mimeType: MimeType): Boolean = + mimeType.fileType == FileType.Video || mimeType.type == "video" override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia { val tempFile = Files.createTempFile("hideout-movie-processor-", ".tmp") @@ -74,7 +73,6 @@ class FFmpegVideoProcessor( it.videoBitrate = videoBitRate it.start() - val frameConverter = Java2DFrameConverter() while (true) { @@ -114,4 +112,4 @@ class FFmpegVideoProcessor( companion object { private val logger = LoggerFactory.getLogger(FFmpegVideoProcessor::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt index 2a04b380..e7b28ed5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt @@ -23,7 +23,5 @@ open class HttpCommandExecutor( val ip: String, val userAgent: String, ) : CommandExecutor { - override fun toString(): String { - return "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')" - } + override fun toString(): String = "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')" } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt index 51ab4a81..799a8d44 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringSecurityPasswordEncoder.kt @@ -24,7 +24,5 @@ class SpringSecurityPasswordEncoder( private val passwordEncoder: org.springframework.security.crypto.password.PasswordEncoder, ) : PasswordEncoder { - override suspend fun encode(input: String): String { - return passwordEncoder.encode(input) - } + override suspend fun encode(input: String): String = passwordEncoder.encode(input) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt index 0b2f61d4..616868fe 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/HideoutUserDetails.kt @@ -40,17 +40,11 @@ class HideoutUserDetails( val userDetailsId: Long, ) : UserDetails { private val authorities: MutableSet = Collections.unmodifiableSet(authorities) - override fun getAuthorities(): MutableSet { - return authorities - } + override fun getAuthorities(): MutableSet = authorities - override fun getPassword(): String { - return password - } + override fun getPassword(): String = password - override fun getUsername(): String { - return username - } + override fun getUsername(): String = username override fun equals(other: Any?): Boolean { if (this === other) return true @@ -75,7 +69,12 @@ class HideoutUserDetails( } override fun toString(): String { - return "HideoutUserDetails(authorities=$authorities, password='$password', username='$username', userDetailsId=$userDetailsId)" + return "HideoutUserDetails(" + + "password='$password', " + + "username='$username', " + + "userDetailsId=$userDetailsId, " + + "authorities=$authorities" + + ")" } companion object { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 39e8d6c8..98252d8e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -32,6 +32,7 @@ class AuthController( private val springMvcCommandExecutorFactory: SpringMvcCommandExecutorFactory, ) { @GetMapping("/auth/sign_up") + @Suppress("FunctionOnlyReturningConstant") fun signUp(): String = "sign_up" @PostMapping("/auth/sign_up") diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt index a0127a35..a8a67f4d 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/actor/ActorsTest.kt @@ -16,7 +16,7 @@ class ActorsTest { actor.suspend = true - assertContainsEvent(actor, ActorEvent.actorSuspend.eventName) + assertContainsEvent(actor, ActorEvent.ACTOR_SUSPEND.eventName) } @Test @@ -25,7 +25,7 @@ class ActorsTest { actor.suspend = false - assertContainsEvent(actor, ActorEvent.actorUnsuspend.eventName) + assertContainsEvent(actor, ActorEvent.ACTOR_UNSUSPEND.eventName) } @Test @@ -45,7 +45,7 @@ class ActorsTest { actor.moveTo = ActorId(100) - assertContainsEvent(actor, ActorEvent.move.eventName) + assertContainsEvent(actor, ActorEvent.MOVE.eventName) } @Test @@ -72,7 +72,7 @@ class ActorsTest { actor.description = ActorDescription("hoge fuga") - assertContainsEvent(actor, ActorEvent.update.eventName) + assertContainsEvent(actor, ActorEvent.UPDATE.eventName) } @Test @@ -81,7 +81,7 @@ class ActorsTest { actor.screenName = ActorScreenName("fuga hoge") - assertContainsEvent(actor, ActorEvent.update.eventName) + assertContainsEvent(actor, ActorEvent.UPDATE.eventName) } @Test @@ -106,7 +106,7 @@ class ActorsTest { assertEquals(ActorPostsCount.ZERO, actor.postsCount) assertNull(actor.followersCount) assertNull(actor.followingCount) - assertContainsEvent(actor, ActorEvent.delete.eventName) + assertContainsEvent(actor, ActorEvent.DELETE.eventName) } @Test @@ -116,7 +116,7 @@ class ActorsTest { actor.restore() assertFalse(actor.deleted) - assertContainsEvent(actor, ActorEvent.checkUpdate.eventName) + assertContainsEvent(actor, ActorEvent.CHECK_UPDATE.eventName) } @Test @@ -125,6 +125,6 @@ class ActorsTest { actor.checkUpdate() - assertContainsEvent(actor, ActorEvent.checkUpdate.eventName) + assertContainsEvent(actor, ActorEvent.CHECK_UPDATE.eventName) } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt index 65ab6cbb..c881d6e4 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/post/PostTest.kt @@ -137,7 +137,7 @@ class PostTest { val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) post.setVisibility(Visibility.PUBLIC, actor) - assertContainsEvent(post, PostEvent.update.eventName) + assertContainsEvent(post, PostEvent.UPDATE.eventName) } @Test @@ -189,7 +189,7 @@ class PostTest { val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) post.setVisibleActors(setOf(ActorId(100)), actor) - assertContainsEvent(post, PostEvent.update.eventName) + assertContainsEvent(post, PostEvent.UPDATE.eventName) } @Test @@ -213,7 +213,7 @@ class PostTest { val post = TestPostFactory.create() val actor = TestActorFactory.create(id = post.actorId.id, publicKey = ActorPublicKey("")) post.setContent(PostContent("test", "test", emptyList()), actor) - assertContainsEvent(post, PostEvent.update.eventName) + assertContainsEvent(post, PostEvent.UPDATE.eventName) } @Test @@ -249,7 +249,7 @@ class PostTest { } assertEquals(overview, post.overview) - assertContainsEvent(post, PostEvent.update.eventName) + assertContainsEvent(post, PostEvent.UPDATE.eventName) } @Test diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt index 22411fb9..0480e70e 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/filter/GetFilterV1ApplicationService.kt @@ -42,11 +42,11 @@ class GetFilterV1ApplicationService(private val filterRepository: FilterReposito phrase = filterKeyword?.keyword?.keyword, context = filter.filterContext.map { when (it) { - home -> V1Filter.Context.home - notifications -> V1Filter.Context.notifications - public -> V1Filter.Context.public - thread -> V1Filter.Context.thread - account -> V1Filter.Context.account + HOME -> V1Filter.Context.home + NOTIFICATION -> V1Filter.Context.notifications + PUBLIC -> V1Filter.Context.public + THREAD -> V1Filter.Context.thread + ACCOUNT -> V1Filter.Context.account } }, expiresAt = null, diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt index 4bfdbea5..8d1c6f41 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt @@ -82,16 +82,16 @@ class SpringFilterApi( } val filterContext = v1FilterPostRequest.context.map { when (it) { - home -> FilterContext.home - notifications -> FilterContext.notifications - public -> FilterContext.public - thread -> FilterContext.thread - account -> FilterContext.account + home -> FilterContext.HOME + notifications -> FilterContext.NOTIFICATION + public -> FilterContext.PUBLIC + thread -> FilterContext.THREAD + account -> FilterContext.ACCOUNT } }.toSet() val filter = userRegisterFilterApplicationService.execute( RegisterFilter( - v1FilterPostRequest.phrase, filterContext, FilterAction.warn, + v1FilterPostRequest.phrase, filterContext, FilterAction.WARN, setOf(RegisterFilterKeyword(v1FilterPostRequest.phrase, filterMode)) ), executor ) @@ -140,17 +140,17 @@ class SpringFilterApi( title = filter.name, context = filter.filterContext.map { when (it) { - FilterContext.home -> Filter.Context.home - FilterContext.notifications -> Filter.Context.notifications - FilterContext.public -> Filter.Context.public - FilterContext.thread -> Filter.Context.thread - FilterContext.account -> Filter.Context.account + FilterContext.HOME -> Filter.Context.home + FilterContext.NOTIFICATION -> Filter.Context.notifications + FilterContext.PUBLIC -> Filter.Context.public + FilterContext.THREAD -> Filter.Context.thread + FilterContext.ACCOUNT -> Filter.Context.account } }, expiresAt = null, filterAction = when (filter.filterAction) { - FilterAction.warn -> Filter.FilterAction.warn - FilterAction.hide -> Filter.FilterAction.hide + FilterAction.WARN -> Filter.FilterAction.warn + FilterAction.HIDE -> Filter.FilterAction.hide }, keywords = filter.filterKeywords.map { @@ -197,17 +197,17 @@ class SpringFilterApi( filterName = filterPostRequest.title, filterContext = filterPostRequest.context.map { when (it) { - Context.home -> FilterContext.home - Context.notifications -> FilterContext.notifications - Context.public -> FilterContext.public - Context.thread -> FilterContext.thread - Context.account -> FilterContext.account + Context.home -> FilterContext.HOME + Context.notifications -> FilterContext.NOTIFICATION + Context.public -> FilterContext.PUBLIC + Context.thread -> FilterContext.THREAD + Context.account -> FilterContext.ACCOUNT } }.toSet(), filterAction = when (filterPostRequest.filterAction) { - FilterPostRequest.FilterAction.warn -> FilterAction.warn - FilterPostRequest.FilterAction.hide -> FilterAction.hide - null -> FilterAction.warn + FilterPostRequest.FilterAction.warn -> FilterAction.WARN + FilterPostRequest.FilterAction.hide -> FilterAction.HIDE + null -> FilterAction.WARN }, filterKeywords = filterPostRequest.keywordsAttributes.orEmpty().map { RegisterFilterKeyword(