feat(swagger): update to OpenAPI 3

migrate from Springfox to Springdoc
This commit is contained in:
Gauthier Roebroeck 2020-04-13 20:08:12 +08:00
parent b900227c83
commit c9de7c8074
6 changed files with 65 additions and 38 deletions

View file

@ -51,9 +51,11 @@ dependencies {
implementation("io.micrometer:micrometer-registry-influx")
run {
val springfoxVersion = "2.9.2"
implementation("io.springfox:springfox-swagger2:$springfoxVersion")
implementation("io.springfox:springfox-swagger-ui:$springfoxVersion")
val springdocVersion = "1.3.1"
implementation("org.springdoc:springdoc-openapi-ui:$springdocVersion")
implementation("org.springdoc:springdoc-openapi-data-rest:$springdocVersion")
implementation("org.springdoc:springdoc-openapi-security:$springdocVersion")
implementation("org.springdoc:springdoc-openapi-kotlin:$springdocVersion")
}
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

View file

@ -1,36 +1,34 @@
package org.gotson.komga.infrastructure.swagger
import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.ExternalDocumentation
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Info
import io.swagger.v3.oas.models.info.License
import io.swagger.v3.oas.models.security.SecurityScheme
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.data.domain.Pageable
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.context.request.WebRequest
import springfox.documentation.schema.AlternateTypeRules
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
@Configuration
@EnableSwagger2
class SwaggerConfiguration {
@Bean
fun getDocket(): Docket =
Docket(DocumentationType.SWAGGER_2)
.ignoredParameterTypes(
AuthenticationPrincipal::class.java,
WebRequest::class.java
)
.alternateTypeRules(AlternateTypeRules.newRule(
Pageable::class.java,
PageableMixin::class.java,
Ordered.HIGHEST_PRECEDENCE
))
fun openApi(): OpenAPI =
OpenAPI()
.info(Info()
.title("Komga API")
.version("v1.0")
.description("""
Komga offers 2 APIs: REST and OPDS.
private class PageableMixin {
val page = 0
val size = 20
val sort = ""
}
Both APIs are secured using HTTP Basic Authentication.
""".trimIndent())
.license(License().name("MIT").url("https://github.com/gotson/komga/blob/master/LICENSE")))
.externalDocs(ExternalDocumentation()
.description("Komga documentation")
.url("https://komga.org"))
.components(Components()
.addSecuritySchemes(
"basicAuth",
SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic")))
}

View file

@ -3,6 +3,8 @@ package org.gotson.komga.interfaces.rest
import com.github.klinq.jpaspec.`in`
import com.github.klinq.jpaspec.likeLower
import com.github.klinq.jpaspec.toJoin
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import mu.KotlinLogging
import org.gotson.komga.application.service.AsyncOrchestrator
import org.gotson.komga.application.service.BookLifecycle
@ -21,6 +23,7 @@ import org.gotson.komga.interfaces.rest.dto.BookDto
import org.gotson.komga.interfaces.rest.dto.BookMetadataUpdateDto
import org.gotson.komga.interfaces.rest.dto.PageDto
import org.gotson.komga.interfaces.rest.dto.toDto
import org.springdoc.api.annotations.ParameterObject
import org.springframework.core.io.FileSystemResource
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
@ -71,7 +74,7 @@ class BookController(
@RequestParam(name = "search", required = false) searchTerm: String?,
@RequestParam(name = "library_id", required = false) libraryIds: List<Long>?,
@RequestParam(name = "media_status", required = false) mediaStatus: List<Media.Status>?,
page: Pageable
@ParameterObject page: Pageable
): Page<BookDto> {
val pageRequest = PageRequest.of(
page.pageNumber,
@ -115,10 +118,12 @@ class BookController(
}
@Operation(description = "Return newly added or updated books.")
@GetMapping("api/v1/books/latest")
@Parameter(name = "sort", hidden = true)
fun getLatestSeries(
@AuthenticationPrincipal principal: KomgaPrincipal,
page: Pageable
@ParameterObject page: Pageable
): Page<BookDto> {
val pageRequest = PageRequest.of(
page.pageNumber,
@ -193,6 +198,7 @@ class BookController(
} else throw ResponseStatusException(HttpStatus.NOT_FOUND)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
@Operation(description = "Download the book file.")
@GetMapping(value = [
"api/v1/books/{bookId}/file",
"api/v1/books/{bookId}/file/*",
@ -348,6 +354,7 @@ class BookController(
@PreAuthorize("hasRole('ADMIN')")
fun updateMetadata(
@PathVariable bookId: Long,
@Parameter(description = "Metadata fields to update. Set a field to null to unset the metadata. You can omit fields you don't want to update.")
@Valid @RequestBody newMetadata: BookMetadataUpdateDto
): BookDto =
bookRepository.findByIdOrNull(bookId)?.let { book ->

View file

@ -3,6 +3,8 @@ package org.gotson.komga.interfaces.rest
import com.github.klinq.jpaspec.`in`
import com.github.klinq.jpaspec.likeLower
import com.github.klinq.jpaspec.toJoin
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import mu.KotlinLogging
import org.gotson.komga.application.service.AsyncOrchestrator
import org.gotson.komga.domain.model.Library
@ -16,6 +18,7 @@ import org.gotson.komga.interfaces.rest.dto.BookDto
import org.gotson.komga.interfaces.rest.dto.SeriesDto
import org.gotson.komga.interfaces.rest.dto.SeriesMetadataUpdateDto
import org.gotson.komga.interfaces.rest.dto.toDto
import org.springdoc.api.annotations.ParameterObject
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
@ -57,7 +60,7 @@ class SeriesController(
@RequestParam(name = "search", required = false) searchTerm: String?,
@RequestParam(name = "library_id", required = false) libraryIds: List<Long>?,
@RequestParam(name = "status", required = false) metadataStatus: List<SeriesMetadata.Status>?,
page: Pageable
@ParameterObject page: Pageable
): Page<SeriesDto> {
val pageRequest = PageRequest.of(
page.pageNumber,
@ -100,11 +103,12 @@ class SeriesController(
}.map { it.toDto(includeUrl = principal.user.isAdmin()) }
}
// all updated series, whether newly added or updated
@Operation(description = "Return recently added or updated series.")
@GetMapping("/latest")
@Parameter(name = "sort", hidden = true)
fun getLatestSeries(
@AuthenticationPrincipal principal: KomgaPrincipal,
page: Pageable
@ParameterObject page: Pageable
): Page<SeriesDto> {
val pageRequest = PageRequest.of(
page.pageNumber,
@ -119,11 +123,12 @@ class SeriesController(
}.map { it.toDto(includeUrl = principal.user.isAdmin()) }
}
// new series only, doesn't contain existing updated series
@Operation(description = "Return newly added series.")
@GetMapping("/new")
@Parameter(name = "sort", hidden = true)
fun getNewSeries(
@AuthenticationPrincipal principal: KomgaPrincipal,
page: Pageable
@ParameterObject page: Pageable
): Page<SeriesDto> {
val pageRequest = PageRequest.of(
page.pageNumber,
@ -138,11 +143,12 @@ class SeriesController(
}.map { it.toDto(includeUrl = principal.user.isAdmin()) }
}
// updated series only, doesn't contain new series
@Operation(description = "Return recently updated series, but not newly added ones.")
@GetMapping("/updated")
@Parameter(name = "sort", hidden = true)
fun getUpdatedSeries(
@AuthenticationPrincipal principal: KomgaPrincipal,
page: Pageable
@ParameterObject page: Pageable
): Page<SeriesDto> {
val pageRequest = PageRequest.of(
page.pageNumber,
@ -185,7 +191,7 @@ class SeriesController(
@AuthenticationPrincipal principal: KomgaPrincipal,
@PathVariable(name = "seriesId") id: Long,
@RequestParam(name = "media_status", required = false) mediaStatus: List<Media.Status>?,
page: Pageable
@ParameterObject page: Pageable
): Page<BookDto> {
seriesRepository.findByIdOrNull(id)?.let {
if (!principal.user.canAccessSeries(it)) throw ResponseStatusException(HttpStatus.UNAUTHORIZED)
@ -234,6 +240,7 @@ class SeriesController(
@PreAuthorize("hasRole('ADMIN')")
fun updateMetadata(
@PathVariable seriesId: Long,
@Parameter(description = "Metadata fields to update. Set a field to null to unset the metadata. You can omit fields you don't want to update.")
@Valid @RequestBody newMetadata: SeriesMetadataUpdateDto
): SeriesDto =
seriesRepository.findByIdOrNull(seriesId)?.let { series ->

View file

@ -34,6 +34,10 @@ management.metrics.export.influx:
# enabled: true
uri: http://localhost:8086
springdoc:
cache:
disabled: true
#server:
# servlet:
# context-path: /komga

View file

@ -53,3 +53,12 @@ management:
export:
influx:
enabled: false
springdoc:
group-configs:
- group: REST API
paths-to-match: /api/**
- group: OPDS
paths-to-match: /opds/**
swagger-ui:
groups-order: desc
operations-sorter: alpha