Merge pull request #286 from usbharu/feature/bean-validation

Bean Validationの導入
This commit is contained in:
usbharu 2024-02-21 15:58:36 +09:00 committed by GitHub
commit fd269a52e2
5 changed files with 72 additions and 14 deletions

View File

@ -197,9 +197,10 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-log4j2")
compileOnly("jakarta.validation:jakarta.validation-api")
compileOnly("jakarta.annotation:jakarta.annotation-api:2.1.0")
compileOnly("io.swagger.core.v3:swagger-annotations:2.2.6")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("jakarta.validation:jakarta.validation-api")
implementation("jakarta.annotation:jakarta.annotation-api:2.1.0")
implementation("io.swagger.core.v3:swagger-annotations:2.2.6")
implementation("io.swagger.core.v3:swagger-models:2.2.6")
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
testImplementation("org.springframework.boot:spring-boot-starter-test")

View File

@ -24,6 +24,7 @@ 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
@ -31,7 +32,6 @@ 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
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
@ -160,6 +160,7 @@ class AccountApiTest {
}
@Test
@Disabled("JSONでも作れるようにするため")
@WithAnonymousUser
fun apiV1AccountsPostでJSONで作ろうとしても400() {
mockMvc

View File

@ -19,6 +19,7 @@ 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
@ -56,12 +57,17 @@ class JsonOrFormModelMethodProcessor(
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.")
}
}
}

View File

@ -59,19 +59,19 @@ class MastodonAccountApiController(
HttpStatus.OK
)
override suspend fun apiV1AccountsPost(
username: String,
password: String,
email: String?,
agreement: Boolean?,
locale: Boolean?,
reason: String?
): ResponseEntity<Unit> {
override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity<Unit> {
transaction.transaction {
accountApiService.registerAccount(UserCreateDto(username, username, "", password))
accountApiService.registerAccount(
UserCreateDto(
accountsCreateRequest.username,
accountsCreateRequest.username,
"",
accountsCreateRequest.password
)
)
}
val httpHeaders = HttpHeaders()
httpHeaders.location = URI("/users/$username")
httpHeaders.location = URI("/users/${accountsCreateRequest.username}")
return ResponseEntity(Unit, httpHeaders, HttpStatus.FOUND)
}

View File

@ -276,6 +276,9 @@ paths:
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AccountsCreateRequest"
application/x-www-form-urlencoded:
schema:
$ref: "#/components/schemas/AccountsCreateRequest"
@ -1355,6 +1358,7 @@ components:
format: binary
description:
type: string
maxLength: 4000
focus:
type: string
required:
@ -1365,12 +1369,18 @@ components:
properties:
username:
type: string
minLength: 1
maxLength: 300
pattern: '^[a-zA-Z0-9_-]{1,300}$'
email:
type: string
format: email
password:
type: string
format: password
agreement:
type: boolean
default: false
locale:
type: boolean
reason:
@ -1399,6 +1409,8 @@ components:
type: string
username:
type: string
minLength: 1
pattern: '^[a-zA-Z0-9_-]{1,300}$'
acct:
type: string
url:
@ -1968,8 +1980,10 @@ components:
properties:
phrase:
type: string
maxLength: 1000
context:
type: array
maxItems: 10
items:
type: string
enum:
@ -1980,8 +1994,10 @@ components:
- account
irreversible:
type: boolean
default: false
whole_word:
type: boolean
default: false
expires_in:
type: integer
required:
@ -1993,8 +2009,10 @@ components:
properties:
phrase:
type: string
maxLength: 1000
context:
type: array
maxItems: 10
items:
type: string
enum:
@ -2015,8 +2033,10 @@ components:
properties:
title:
type: string
maxLength: 255
context:
type: array
maxItems: 10
items:
type: string
enum:
@ -2033,6 +2053,7 @@ components:
expires_in:
type: integer
keywords_attributes:
maxItems: 1000
type: array
items:
$ref: "#/components/schemas/FilterPostRequestKeyword"
@ -2045,6 +2066,7 @@ components:
properties:
keyword:
type: string
maxLength: 1000
whole_word:
type: boolean
default: false
@ -2059,6 +2081,7 @@ components:
properties:
keyword:
type: string
maxLength: 1000
whole_word:
type: boolean
default: false
@ -2073,6 +2096,7 @@ components:
properties:
keyword:
type: string
maxLength: 1000
whole_word:
type: boolean
regex:
@ -2083,8 +2107,10 @@ components:
properties:
title:
type: string
maxLength: 255
context:
type: array
maxItems: 10
items:
type: string
enum:
@ -2101,6 +2127,7 @@ components:
expires_in:
type: integer
keywords_attributes:
maxItems: 1000
type: array
items:
$ref: "#/components/schemas/FilterPubRequestKeyword"
@ -2110,6 +2137,7 @@ components:
properties:
keyword:
type: string
maxLength: 1000
whole_word:
type: boolean
regex:
@ -2118,6 +2146,9 @@ components:
type: string
_destroy:
type: boolean
default: false
required:
- id
FilterStatusRequest:
type: object
@ -2473,18 +2504,22 @@ components:
status:
type: string
nullable: true
maxLength: 3000
media_ids:
type: array
items:
type: string
maxItems: 4
poll:
$ref: "#/components/schemas/StatusesRequestPoll"
in_reply_to_id:
type: string
sensitive:
type: boolean
default: false
spoiler_text:
type: string
maxLength: 100
visibility:
type: string
enum:
@ -2494,22 +2529,29 @@ components:
- direct
language:
type: string
maxLength: 100
scheduled_at:
type: string
format: date-time
example: "2019-12-05T12:33:01.000Z"
StatusesRequestPoll:
type: object
properties:
options:
type: array
maxItems: 10
items:
type: string
maxLength: 100
expires_in:
type: integer
multiple:
type: boolean
default: false
hide_totals:
type: boolean
default: false
Application:
type: object
@ -2536,12 +2578,16 @@ components:
properties:
client_name:
type: string
maxLength: 200
redirect_uris:
type: string
maxLength: 1000
scopes:
type: string
maxLength: 1000
website:
type: string
maxLength: 1000
required:
- client_name
- redirect_uris
@ -2602,16 +2648,20 @@ components:
default: false
languages:
type: array
maxItems: 10
items:
type: string
maxLength: 10
UpdateCredentials:
type: object
properties:
display_name:
type: string
maxLength: 300
note:
type: string
maxLength: 2000
avatar:
type: string
format: binary