Merge pull request #700 from usbharu/feature/post-auto-truncate

Feature/post auto truncate
This commit is contained in:
usbharu 2025-05-12 00:24:21 +09:00 committed by GitHub
commit 578ed2d8fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 192 additions and 531 deletions

View File

@ -46,6 +46,7 @@ class Post(
visibleActors: Set<ActorId>,
hide: Boolean,
moveTo: PostId?,
val wasTruncated: Boolean,
) : DomainEventStorable() {
val actorId = actorId
@ -235,7 +236,8 @@ class Post(
mediaIds = mediaIds,
visibleActors = visibleActors,
hide = hide,
moveTo = moveTo
moveTo = moveTo,
wasTruncated = wasTruncated
)
}
@ -310,7 +312,8 @@ class Post(
mediaIds = mediaIds,
visibleActors = visibleActors,
hide = hide,
moveTo = moveTo
moveTo = moveTo,
wasTruncated = content.wasTruncated
)
post.addDomainEvent(PostDomainEventFactory(post).createEvent(PostEvent.CREATE))
return post

View File

@ -18,11 +18,54 @@ package dev.usbharu.hideout.core.domain.model.post
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
data class PostContent(val text: String, val content: String, val emojiIds: List<CustomEmojiId>) {
class PostContent {
val text: String
val content: String
val emojiIds: List<CustomEmojiId>
val wasTruncated: Boolean
constructor(text: String, content: String, emojiIds: List<CustomEmojiId>) {
wasTruncated = text.length > TEXT_LENGTH || content.length > CONTENT_LENGTH
this.text = text.take(TEXT_LENGTH)
this.content = content.take(CONTENT_LENGTH)
this.emojiIds = emojiIds.distinct()
}
companion object {
val empty = PostContent("", "", emptyList())
const val CONTENT_LENGTH = 5000
const val TEXT_LENGTH = 3000
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as PostContent
if (wasTruncated != other.wasTruncated) return false
if (text != other.text) return false
if (content != other.content) return false
if (emojiIds != other.emojiIds) return false
return true
}
override fun hashCode(): Int {
var result = wasTruncated.hashCode()
result = 31 * result + text.hashCode()
result = 31 * result + content.hashCode()
result = 31 * result + emojiIds.hashCode()
return result
}
override fun toString(): String {
return "PostContent(" +
"text='$text', " +
"content='$content', " +
"emojiIds=$emojiIds, " +
"wasTruncated=$wasTruncated" +
")"
}
}

View File

@ -44,7 +44,8 @@ class PostResultRowMapper : ResultRowMapper<Post> {
mediaIds = emptyList(),
visibleActors = emptySet(),
hide = resultRow[Posts.hide],
moveTo = resultRow[Posts.moveTo]?.let { PostId(it) }
moveTo = resultRow[Posts.moveTo]?.let { PostId(it) },
wasTruncated = resultRow[Posts.wasTruncated]
)
}
}

View File

@ -39,6 +39,7 @@ 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 dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.wasTruncated
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
@ -74,6 +75,7 @@ class ExposedPostRepository(
it[deleted] = post.deleted
it[hide] = post.hide
it[moveTo] = post.moveTo?.id
it[wasTruncated] = post.wasTruncated
}
PostsMedia.deleteWhere {
postId eq post.id.id
@ -123,6 +125,7 @@ class ExposedPostRepository(
this[deleted] = it.deleted
this[hide] = it.hide
this[moveTo] = it.moveTo?.id
this[wasTruncated] = it.wasTruncated
}
val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id.id to it.id } }
val postsIds = posts.map { it.id.id }
@ -296,10 +299,11 @@ object Posts : Table("posts") {
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 apId = varchar("ap_id", 1000).uniqueIndex()
val deleted = bool("deleted")
val hide = bool("hide")
val moveTo = long("move_to").references(id).nullable()
val wasTruncated = bool("was_truncated").default(false).clientDefault { false }
override val primaryKey: PrimaryKey = PrimaryKey(id)
}

View File

@ -164,7 +164,8 @@ create table if not exists posts
ap_id varchar(100) not null unique,
deleted boolean default false not null,
hide boolean default false not null,
move_to bigint default null null
move_to bigint default null null,
was_truncated boolean default false not null
);
alter table posts
add constraint fk_posts_instance_id__id foreign key (instance_id) references instance (id) on delete cascade on update cascade;

View File

@ -0,0 +1,48 @@
package dev.usbharu.hideout.core.domain.model.post
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
class PostContentTest {
@Test
fun textがtext_lengthを超える場合は切り取られてwasTruncatedがtrue() {
val postContent = PostContent(
"a".repeat(PostContent.TEXT_LENGTH + 1),
"b".repeat(PostContent.CONTENT_LENGTH),
emptyList()
)
assertTrue(postContent.wasTruncated)
assertEquals(postContent.text, "a".repeat(PostContent.TEXT_LENGTH))
assertEquals(postContent.content, "b".repeat(PostContent.CONTENT_LENGTH))
}
@Test
fun textがtext_lengthを超えない場合は変わらずwasTruncatedがfalse() {
val postContent = PostContent(
"a".repeat(PostContent.TEXT_LENGTH),
"b".repeat(PostContent.CONTENT_LENGTH),
emptyList()
)
assertFalse(postContent.wasTruncated)
assertEquals(postContent.text, "a".repeat(PostContent.TEXT_LENGTH))
assertEquals(postContent.content, "b".repeat(PostContent.CONTENT_LENGTH))
}
@Test
fun contentがcontent_lengthを超える場合は切り取られてwasTruncatedがtrue() {
val postContent = PostContent(
"a".repeat(PostContent.TEXT_LENGTH),
"b".repeat(PostContent.CONTENT_LENGTH + 1),
emptyList()
)
assertTrue(postContent.wasTruncated)
assertEquals(postContent.text, "a".repeat(PostContent.TEXT_LENGTH))
assertEquals(postContent.content, "b".repeat(PostContent.CONTENT_LENGTH))
}
}

View File

@ -31,6 +31,7 @@ object TestPostFactory {
hide: Boolean = false,
moveTo: Long? = null,
emojiIds: List<Long> = emptyList(),
wasTruncated: Boolean = false,
): Post {
return Post(
PostId(id),
@ -49,7 +50,8 @@ object TestPostFactory {
mediaIds = mediaIds.map { MediaId(it) },
visibleActors = visibleActors.map { ActorId(it) }.toSet(),
hide = hide,
moveTo = moveTo?.let { PostId(it) }
moveTo = moveTo?.let { PostId(it) },
wasTruncated = wasTruncated
)
}

View File

@ -270,7 +270,8 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
null,
false
)
}
}.launch()
@ -329,7 +330,8 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
null,
false
)
}
insertInto(PostsMedia.tableName) {
@ -364,7 +366,9 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
deleted = false,
mediaIds = listOf(MediaId(2)),
visibleActors = setOf(ActorId(4)),
hide = false, moveTo = null
hide = false,
moveTo = null,
wasTruncated = false
)
assertNotNull(actual)
@ -398,60 +402,9 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1832779978794602496,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
1832779978794602496,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1832779978794602496,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(2)
postsValues(3)
}
}.launch()
@ -467,60 +420,9 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
2,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(2, 2)
postsValues(3)
}
}.launch()
@ -536,65 +438,16 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-02T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(2)
postsValues(3)
}
}.launch()
val findAllById = repository.findByActorId(ActorId(1), Page.of(maxId = 3))
findAllById.forEach(::println)
assertThat(findAllById)
.hasSize(2)
@ -608,60 +461,9 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-02T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(2)
postsValues(3)
}
}.launch()
@ -680,60 +482,9 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-02T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(2)
postsValues(3)
}
}.launch()
@ -752,60 +503,9 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-02T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(2)
postsValues(3)
}
}.launch()
@ -823,24 +523,7 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1832779978794602496,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
postsValues(1)
}
insertInto(PostsMedia.tableName) {
columns(PostsMedia.columns)
@ -884,78 +567,10 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
2,
2,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-02T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/18327739994749734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/18327793994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"UNLISTED",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
values(
4,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-04T00:00:00Z")),
"FOLLOWERS",
"http://localhost:8081/users/a/posts/1832773999474947343912",
null,
null,
false,
"http://localhost:8081/users/a/posts/1832779399474937349312",
false,
false,
2
)
postsValues(1)
postsValues(2, 2)
postsValues(3, 2)
postsValues(4)
}
}.launch()
@ -975,42 +590,8 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"UNLISTED",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(3)
}
insertInto(PostsMedia.tableName) {
columns(PostsMedia.columns)
@ -1087,42 +668,8 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
values(
3,
1,
1832779642545639424,
"",
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-03T00:00:00Z")),
"UNLISTED",
"http://localhost:8081/users/a/posts/183277399947494734912",
null,
null,
false,
"http://localhost:8081/users/a/posts/183277939947493734912",
false,
false,
2
)
postsValues(1)
postsValues(3)
}
insertInto(PostsMedia.tableName) {
columns(PostsMedia.columns)
@ -1168,24 +715,7 @@ class ExposedPostRepositoryTest : AbstractRepositoryTest(Posts) {
execute(disableReferenceIntegrityConstraints)
insertInto("public.posts") {
columns(Posts.columns)
values(
1,
1,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z")),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912",
false,
false,
null
)
postsValues(1)
}
}.launch()

View File

@ -56,7 +56,8 @@ class ExposedReactionRepositoryTest : AbstractRepositoryTest(Reactions) {
"https://example.com",
false,
false,
null
null,
false
)
}
insertInto("public.actors") {
@ -141,7 +142,8 @@ class ExposedReactionRepositoryTest : AbstractRepositoryTest(Reactions) {
"https://example.com",
false,
false,
null
null,
false
)
}
insertInto("public.actors") {

View File

@ -0,0 +1,27 @@
package utils
import com.ninja_squad.dbsetup.operation.Insert
import java.sql.Timestamp
import java.time.Instant
fun Insert.Builder.postsValues(index: Long = 1, actor: Long = 1) {
values(
index,
actor,
1832779642545639424,
null,
"<p>test</p>",
"test",
Timestamp.from(Instant.parse("2020-01-01T00:00:00Z").plusSeconds(index.toLong())),
"PUBLIC",
"http://localhost:8081/users/a/posts/1832779994749734912",
2,
2,
false,
"http://localhost:8081/users/a/posts/1832779994749734912$index",
false,
false,
null,
false
)
}