Compare commits

...

7 Commits

Author SHA1 Message Date
usbharu e8050699ee
Apply suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-10-10 18:57:11 +09:00
usbharu bd18985894
Update src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-10-10 18:52:45 +09:00
usbharu 6c3088bc28
Update src/main/kotlin/dev/usbharu/hideout/config/JsonOrFormModelMethodProcessor.kt
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-10-10 18:52:20 +09:00
usbharu a83eb02258
fix: 自動生成が消えていた部分を修正 2023-10-10 18:48:00 +09:00
usbharu 1435dbadc7
chore: 不要なデバッグ情報を削除 2023-10-10 18:11:57 +09:00
usbharu 18410cfdee
chore: テスト同時実行数の設定を修正 2023-10-10 18:03:25 +09:00
usbharu a373c0d880
test: fix test 2023-10-10 18:01:57 +09:00
12 changed files with 158 additions and 142 deletions

View File

@ -1,6 +1,6 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
import kotlin.math.min
import kotlin.math.max
val ktor_version: String by project
val kotlin_version: String by project
@ -29,7 +29,7 @@ version = "0.0.1"
tasks.withType<Test> {
useJUnitPlatform()
val cpus = Runtime.getRuntime().availableProcessors()
maxParallelForks = min(1, cpus - 1)
maxParallelForks = max(1, cpus - 1)
setForkEvery(4)
}
@ -68,9 +68,6 @@ tasks.create<GenerateTask>("openApiGenerateMastodonCompatibleApi", GenerateTask:
"dev.usbharu.hideout.domain.model.mastodon.StatusesRequest"
)
templateDir.set("$rootDir/templates")
globalProperties.put("debugModels", "true")
globalProperties.put("debugOpenAPI", "true")
globalProperties.put("debugOperations", "true")
}
repositories {

View File

@ -25,7 +25,6 @@ class JsonOrFormModelMethodProcessor(
webRequest: NativeWebRequest,
binderFactory: WebDataBinderFactory?
): Any? {
val contentType = webRequest.getHeader("Content-Type").orEmpty()
logger.trace("ContentType is {}", contentType)
if (contentType.contains(isJsonRegex)) {

View File

@ -15,7 +15,6 @@ class MvcConfigurer(private val jsonOrFormModelMethodProcessor: JsonOrFormModelM
}
}
@Configuration
class JsonOrFormModelMethodProcessorConfig {
@Bean

View File

@ -7,7 +7,6 @@ import org.springframework.context.annotation.Configuration
import org.springframework.web.filter.CommonsRequestLoggingFilter
import java.net.URL
@Configuration
class SpringConfig {

View File

@ -21,7 +21,6 @@ class Document : Object {
this.url = url
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Document) return false
@ -43,6 +42,4 @@ class Document : Object {
override fun toString(): String {
return "Document(mediaType=$mediaType, url=$url) ${super.toString()}"
}
}

View File

@ -40,7 +40,6 @@ open class Note : Object {
this.attachment = attachment
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Note) return false
@ -74,6 +73,4 @@ open class Note : Object {
override fun toString(): String {
return "Note(attributedTo=$attributedTo, attachment=$attachment, content=$content, published=$published, to=$to, cc=$cc, sensitive=$sensitive, inReplyTo=$inReplyTo) ${super.toString()}"
}
}

View File

@ -1,31 +1,38 @@
package dev.usbharu.hideout.domain.model.mastodon
import com.fasterxml.jackson.annotation.JsonProperty
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequest
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequestPoll
class StatusesRequest {
@JsonProperty("status")
var status: String? = null
@JsonProperty("media_ids")
var media_ids: List<String> = 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: StatusesRequest.Visibility? = null
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 dev.usbharu.hideout.domain.model.mastodon.StatusesRequest) return false
if (other !is StatusesRequest) return false
if (status != other.status) return false
if (media_ids != other.media_ids) return false
@ -40,7 +47,6 @@ class StatusesRequest {
return true
}
override fun hashCode(): Int {
var result = status?.hashCode() ?: 0
result = 31 * result + media_ids.hashCode()
@ -58,5 +64,10 @@ class StatusesRequest {
return "StatusesRequest(status=$status, mediaIds=$media_ids, poll=$poll, inReplyToId=$in_reply_to_id, sensitive=$sensitive, spoilerText=$spoiler_text, visibility=$visibility, language=$language, scheduledAt=$scheduled_at)"
}
enum class Visibility {
`public`,
unlisted,
private,
direct;
}
}

View File

@ -27,7 +27,6 @@ class StatusQueryServiceImpl : StatusQueryService {
return resolveReplyAndRepost(pairs)
}
private fun resolveReplyAndRepost(pairs: List<Pair<Status, Long?>>): List<Status> {
val statuses = pairs.map { it.first }
return pairs
@ -56,25 +55,27 @@ class StatusQueryServiceImpl : StatusQueryService {
.groupBy { it[Posts.id] }
.map { it.value }
.map {
toStatus(it.first()).copy(mediaAttachments = it.map {
it.toMedia().let {
MediaAttachment(
it.id.toString(),
when (it.type) {
FileType.Image -> MediaAttachment.Type.image
FileType.Video -> MediaAttachment.Type.video
FileType.Audio -> MediaAttachment.Type.audio
FileType.Unknown -> MediaAttachment.Type.unknown
},
it.url,
it.thumbnailUrl,
it.remoteUrl,
"",
it.blurHash,
it.url
)
toStatus(it.first()).copy(
mediaAttachments = it.map {
it.toMedia().let {
MediaAttachment(
it.id.toString(),
when (it.type) {
FileType.Image -> MediaAttachment.Type.image
FileType.Video -> MediaAttachment.Type.video
FileType.Audio -> MediaAttachment.Type.audio
FileType.Unknown -> MediaAttachment.Type.unknown
},
it.url,
it.thumbnailUrl,
it.remoteUrl,
"",
it.blurHash,
it.url
)
}
}
}) to it.first()[Posts.repostId]
) to it.first()[Posts.repostId]
}
return resolveReplyAndRepost(pairs)
}

View File

@ -90,7 +90,6 @@ object PostsMedia : Table() {
override val primaryKey = PrimaryKey(postId, mediaId)
}
fun ResultRow.toPost(): Post {
return Post.of(
id = this[Posts.id],

View File

@ -80,7 +80,9 @@ class APNoteServiceImpl(
val actor = props[DeliverPostJob.actor]
val postEntity = objectMapper.readValue<Post>(props[DeliverPostJob.post])
val mediaList =
objectMapper.readValue<List<dev.usbharu.hideout.domain.model.hideout.entity.Media>>(props[DeliverPostJob.media])
objectMapper.readValue<List<dev.usbharu.hideout.domain.model.hideout.entity.Media>>(
props[DeliverPostJob.media]
)
val note = Note(
name = "Note",
id = postEntity.url,

View File

@ -2,10 +2,10 @@ package dev.usbharu.hideout.service.api.mastodon
import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequest
import dev.usbharu.hideout.domain.model.hideout.dto.FileType
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.domain.model.mastodon.StatusesRequest
import dev.usbharu.hideout.exception.FailedToGetResourcesException
import dev.usbharu.hideout.query.PostQueryService
import dev.usbharu.hideout.query.UserQueryService

View File

@ -10,6 +10,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.MediaQueryService
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.job.JobQueueParentService
import io.ktor.client.*
@ -19,6 +20,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Test
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.eq
import org.mockito.kotlin.*
import utils.JsonObjectMapper
@ -29,118 +31,131 @@ import kotlin.test.assertEquals
class APNoteServiceImplTest {
@Test
fun `createPost 新しい投稿`() = runTest {
val followers = listOf(
User.of(
2L,
"follower",
"follower.example.com",
"followerUser",
"test follower user",
"https://follower.example.com/inbox",
"https://follower.example.com/outbox",
"https://follower.example.com",
"https://follower.example.com",
publicKey = "",
createdAt = Instant.now()
),
User.of(
3L,
"follower2",
"follower2.example.com",
"follower2User",
"test follower2 user",
"https://follower2.example.com/inbox",
"https://follower2.example.com/outbox",
"https://follower2.example.com",
"https://follower2.example.com",
publicKey = "",
createdAt = Instant.now()
fun `createPost 新しい投稿`() {
val mediaQueryService = mock<MediaQueryService> {
onBlocking { findByPostId(anyLong()) } doReturn emptyList()
}
runTest {
val followers = listOf(
User.of(
2L,
"follower",
"follower.example.com",
"followerUser",
"test follower user",
"https://follower.example.com/inbox",
"https://follower.example.com/outbox",
"https://follower.example.com",
"https://follower.example.com",
publicKey = "",
createdAt = Instant.now()
),
User.of(
3L,
"follower2",
"follower2.example.com",
"follower2User",
"test follower2 user",
"https://follower2.example.com/inbox",
"https://follower2.example.com/outbox",
"https://follower2.example.com",
"https://follower2.example.com",
publicKey = "",
createdAt = Instant.now()
)
)
)
val userQueryService = mock<UserQueryService> {
onBlocking { findById(eq(1L)) } doReturn User.of(
val userQueryService = mock<UserQueryService> {
onBlocking { findById(eq(1L)) } doReturn User.of(
1L,
"test",
"example.com",
"testUser",
"test user",
"a",
"https://example.com/inbox",
"https://example.com/outbox",
"https://example.com",
publicKey = "",
privateKey = "a",
createdAt = Instant.now()
)
}
val followerQueryService = mock<FollowerQueryService> {
onBlocking { findFollowersById(eq(1L)) } doReturn followers
}
val jobQueueParentService = mock<JobQueueParentService>()
val activityPubNoteService =
APNoteServiceImpl(
httpClient = mock(),
jobQueueParentService = jobQueueParentService,
postRepository = mock(),
apUserService = mock(),
userQueryService = userQueryService,
followerQueryService = followerQueryService,
postQueryService = mock(),
objectMapper = objectMapper,
applicationConfig = testApplicationConfig,
postService = mock(),
mediaQueryService = mediaQueryService
)
val postEntity = Post.of(
1L,
"test",
"example.com",
"testUser",
"test user",
"a",
"https://example.com/inbox",
"https://example.com/outbox",
"https://example.com",
publicKey = "",
privateKey = "a",
createdAt = Instant.now()
1L,
null,
"test text",
1L,
Visibility.PUBLIC,
"https://example.com"
)
activityPubNoteService.createNote(postEntity)
verify(jobQueueParentService, times(2)).schedule(eq(DeliverPostJob), any())
}
val followerQueryService = mock<FollowerQueryService> {
onBlocking { findFollowersById(eq(1L)) } doReturn followers
}
val jobQueueParentService = mock<JobQueueParentService>()
val activityPubNoteService =
APNoteServiceImpl(
httpClient = mock(),
jobQueueParentService = jobQueueParentService,
}
@Test
fun `createPostJob 新しい投稿のJob`() {
runTest {
val mediaQueryService = mock<MediaQueryService> {
onBlocking { findByPostId(anyLong()) } doReturn emptyList()
}
Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper)
val httpClient = HttpClient(
MockEngine { httpRequestData ->
assertEquals("https://follower.example.com/inbox", httpRequestData.url.toString())
respondOk()
}
)
val activityPubNoteService = APNoteServiceImpl(
httpClient = httpClient,
jobQueueParentService = mock(),
postRepository = mock(),
apUserService = mock(),
userQueryService = userQueryService,
followerQueryService = followerQueryService,
userQueryService = mock(),
followerQueryService = mock(),
postQueryService = mock(),
objectMapper = objectMapper,
applicationConfig = testApplicationConfig,
postService = mock(),
mediaQueryService = mediaQueryService
)
val postEntity = Post.of(
1L,
1L,
null,
"test text",
1L,
Visibility.PUBLIC,
"https://example.com"
)
activityPubNoteService.createNote(postEntity)
verify(jobQueueParentService, times(2)).schedule(eq(DeliverPostJob), any())
}
@Test
fun `createPostJob 新しい投稿のJob`() = runTest {
Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper)
val httpClient = HttpClient(
MockEngine { httpRequestData ->
assertEquals("https://follower.example.com/inbox", httpRequestData.url.toString())
respondOk()
}
)
val activityPubNoteService = APNoteServiceImpl(
httpClient = httpClient,
jobQueueParentService = mock(),
postRepository = mock(),
apUserService = mock(),
userQueryService = mock(),
followerQueryService = mock(),
postQueryService = mock(),
objectMapper = objectMapper,
applicationConfig = testApplicationConfig,
postService = mock(),
)
activityPubNoteService.createNoteJob(
JobProps(
data = mapOf<String, Any>(
DeliverPostJob.actor.name to "https://follower.example.com",
DeliverPostJob.post.name to """{
"id": 1,
"userId": 1,
"text": "test text",
"createdAt": 132525324,
"visibility": 0,
"url": "https://example.com"
}""",
DeliverPostJob.inbox.name to "https://follower.example.com/inbox"
),
json = Json
activityPubNoteService.createNoteJob(
JobProps(
data = mapOf<String, Any>(
DeliverPostJob.actor.name to "https://follower.example.com",
DeliverPostJob.post.name to """{
"id": 1,
"userId": 1,
"text": "test text",
"createdAt": 132525324,
"visibility": 0,
"url": "https://example.com"
}""",
DeliverPostJob.inbox.name to "https://follower.example.com/inbox",
DeliverPostJob.media.name to "[]"
),
json = Json
)
)
)
}
}
}