feat: フォローのMastodon互換APIを作成

This commit is contained in:
usbharu 2023-11-21 12:38:06 +09:00
parent d93c34adb4
commit 44563e2251
3 changed files with 170 additions and 5 deletions

View File

@ -3,7 +3,10 @@ package dev.usbharu.hideout.mastodon.interfaces.api.account
import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.external.Transaction
import dev.usbharu.hideout.controller.mastodon.generated.AccountApi import dev.usbharu.hideout.controller.mastodon.generated.AccountApi
import dev.usbharu.hideout.core.service.user.UserCreateDto import dev.usbharu.hideout.core.service.user.UserCreateDto
import dev.usbharu.hideout.domain.mastodon.model.generated.Account
import dev.usbharu.hideout.domain.mastodon.model.generated.CredentialAccount import dev.usbharu.hideout.domain.mastodon.model.generated.CredentialAccount
import dev.usbharu.hideout.domain.mastodon.model.generated.FollowRequestBody
import dev.usbharu.hideout.domain.mastodon.model.generated.Relationship
import dev.usbharu.hideout.mastodon.service.account.AccountApiService import dev.usbharu.hideout.mastodon.service.account.AccountApiService
import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
@ -18,6 +21,19 @@ class MastodonAccountApiController(
private val accountApiService: AccountApiService, private val accountApiService: AccountApiService,
private val transaction: Transaction private val transaction: Transaction
) : AccountApi { ) : AccountApi {
override suspend fun apiV1AccountsIdFollowPost(
id: String,
followRequestBody: FollowRequestBody?
): ResponseEntity<Relationship> {
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt
return ResponseEntity.ok(accountApiService.follow(principal.getClaim<String>("uid").toLong(), id.toLong()))
}
override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity<Account> =
ResponseEntity.ok(accountApiService.account(id.toLong()))
override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity<CredentialAccount> { override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity<CredentialAccount> {
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt

View File

@ -1,25 +1,28 @@
package dev.usbharu.hideout.mastodon.service.account package dev.usbharu.hideout.mastodon.service.account
import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.external.Transaction
import dev.usbharu.hideout.core.domain.model.user.UserRepository
import dev.usbharu.hideout.core.query.FollowerQueryService
import dev.usbharu.hideout.core.service.user.UserCreateDto import dev.usbharu.hideout.core.service.user.UserCreateDto
import dev.usbharu.hideout.core.service.user.UserService 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.*
import dev.usbharu.hideout.domain.mastodon.model.generated.CredentialAccount
import dev.usbharu.hideout.domain.mastodon.model.generated.CredentialAccountSource
import dev.usbharu.hideout.domain.mastodon.model.generated.Role
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
interface AccountApiService { interface AccountApiService {
suspend fun verifyCredentials(userid: Long): CredentialAccount suspend fun verifyCredentials(userid: Long): CredentialAccount
suspend fun registerAccount(userCreateDto: UserCreateDto): Unit suspend fun registerAccount(userCreateDto: UserCreateDto): Unit
suspend fun follow(userid: Long, followeeId: Long): Relationship
suspend fun account(id: Long): Account
} }
@Service @Service
class AccountApiServiceImpl( class AccountApiServiceImpl(
private val accountService: AccountService, private val accountService: AccountService,
private val transaction: Transaction, private val transaction: Transaction,
private val userService: UserService private val userService: UserService,
private val followerQueryService: FollowerQueryService,
private val userRepository: UserRepository
) : ) :
AccountApiService { AccountApiService {
override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction { override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction {
@ -31,6 +34,42 @@ class AccountApiServiceImpl(
userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password)) userService.createLocalUser(UserCreateDto(userCreateDto.name, userCreateDto.name, "", userCreateDto.password))
} }
override suspend fun follow(userid: Long, followeeId: Long): Relationship = transaction.transaction {
val alreadyFollow = followerQueryService.alreadyFollow(followeeId, userid)
val followRequest = if (alreadyFollow) {
true
} else {
userService.followRequest(followeeId, userid)
}
val alreadyFollow1 = followerQueryService.alreadyFollow(userid, followeeId)
val followRequestsById = userRepository.findFollowRequestsById(followeeId, userid)
return@transaction Relationship(
followeeId.toString(),
followRequest,
true,
false,
alreadyFollow1,
false,
false,
false,
false,
followRequestsById,
false,
false,
""
)
}
override suspend fun account(id: Long): Account = transaction.transaction {
return@transaction accountService.findById(id)
}
private fun from(account: Account): CredentialAccount { private fun from(account: Account): CredentialAccount {
return CredentialAccount( return CredentialAccount(
id = account.id, id = account.id,

View File

@ -206,6 +206,55 @@ paths:
200: 200:
description: 成功 description: 成功
/api/v1/accounts/{id}:
get:
tags:
- account
security:
- { }
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
200:
description: 成功
content:
application/json:
schema:
$ref: "#/components/schemas/Account"
/api/v1/accounts/{id}/follow:
post:
tags:
- account
security:
- OAuth2:
- "write:follows"
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
required: false
content:
application/json:
schema:
$ref: "#/components/schemas/FollowRequestBody"
application/x-www-form-urlencoded:
schema:
$ref: "#/components/schemas/FollowRequestBody"
responses:
200:
description: 成功
content:
application/json:
schema:
$ref: "#/components/schemas/Relationship"
/api/v1/timelines/public: /api/v1/timelines/public:
get: get:
tags: tags:
@ -1314,6 +1363,8 @@ components:
type: string type: string
client_secret: client_secret:
type: string type: string
redirect_uri:
type: string
required: required:
- name - name
- vapid_key - vapid_key
@ -1333,6 +1384,65 @@ components:
- client_name - client_name
- redirect_uris - redirect_uris
Relationship:
type: object
properties:
id:
type: string
following:
type: boolean
showing_reblogs:
type: boolean
notifying:
type: boolean
followed_by:
type: boolean
blocking:
type: boolean
blocked_by:
type: boolean
muting:
type: boolean
muting_notifications:
type: boolean
requested:
type: boolean
domain_blocking:
type: boolean
endorsed:
type: boolean
note:
type: string
required:
- id
- following
- showing_reblogs
- notifying
- followed_by
- blocking
- blocked_by
- muting
- muting_notifications
- requested
- domain_blocking
- endorsed
- note
FollowRequestBody:
type: object
properties:
reblogs:
type: boolean
default: true
notify:
type: boolean
default: false
languages:
type: array
items:
type: string
securitySchemes: securitySchemes:
OAuth2: OAuth2:
type: oauth2 type: oauth2