AbstractController.kt
package com.example.templateproject.web.controller
import com.example.templateproject.api.dto.BaseDTO
import com.example.templateproject.api.dto.ErrorDTO
import com.example.templateproject.api.dto.PageDetails
import com.example.templateproject.core.service.AbstractService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import jakarta.validation.Valid
import org.slf4j.LoggerFactory
import org.springdoc.core.converters.models.PageableAsQueryParam
import org.springframework.data.domain.Pageable
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import kotlin.reflect.KClass
abstract class AbstractController<D : BaseDTO>(
protected open val service: AbstractService<*, D>,
protected open val clazz: KClass<D>,
) {
companion object {
private val LOGGER = LoggerFactory.getLogger(AbstractController::class.java)
}
@GetMapping(produces = ["application/json"])
@Operation(summary = "Gets paginated and sorted entities.")
@ApiResponses(
ApiResponse(
responseCode = "200",
description = "The entity list has been successfully returned.",
useReturnTypeSchema = true,
),
)
@PageableAsQueryParam
fun getEntities(
@Parameter(hidden = true) @PageableDefault pageable: Pageable,
): PageDetails<D> =
service
.getEntities(pageable)
.apply { LOGGER.info("Returning {} out of {} {}", content.size, totalElements, clazz.simpleName) }
@GetMapping("/{id:[0-9]*}", produces = ["application/json"])
@Operation(summary = "Gets entity by its id.")
@ApiResponses(
ApiResponse(responseCode = "200", description = "The requested entity has been successfully returned."),
ApiResponse(
responseCode = "404",
description = "The requested entity was not found.",
content = [
Content(mediaType = "application/json", schema = Schema(implementation = ErrorDTO::class)),
],
),
)
fun getEntityById(
@PathVariable id: Long,
): D {
LOGGER.info("Returning {} with id: {}", clazz.simpleName, id)
return service.getEntityById(id)
}
@PostMapping(consumes = ["application/json"], produces = ["application/json"])
@Operation(summary = "Creates a new entity.")
@ApiResponses(
ApiResponse(responseCode = "201", description = "Entity successfully created."),
ApiResponse(
responseCode = "400",
description = "The request body is invalid.",
content = [
Content(mediaType = "application/json", schema = Schema(implementation = ErrorDTO::class)),
],
),
)
fun createEntity(
@RequestBody @Valid request: D,
): ResponseEntity<D> {
LOGGER.info("Creating {}: {}", clazz.simpleName, request)
val createdEntity = service.createEntity(request)
return ResponseEntity.status(HttpStatus.CREATED).body(createdEntity)
}
@PutMapping(consumes = ["application/json"], produces = ["application/json"])
@Operation(summary = "Update an existing entity.")
@ApiResponses(
ApiResponse(responseCode = "200", description = "Entity successfully updated."),
ApiResponse(
responseCode = "404",
description = "The requested entity was not found.",
content = [
Content(mediaType = "application/json", schema = Schema(implementation = ErrorDTO::class)),
],
),
ApiResponse(
responseCode = "400",
description = "The request body is invalid.",
content = [
Content(mediaType = "application/json", schema = Schema(implementation = ErrorDTO::class)),
],
),
)
fun updateEntity(
@RequestBody @Valid request: D,
): D {
LOGGER.info("Updating {}: {}", clazz.simpleName, request)
return service.updateEntity(request)
}
@DeleteMapping("/{id:[0-9]*}")
@Operation(summary = "Deletes an entity by its id.")
@ApiResponses(ApiResponse(responseCode = "204"))
fun deleteEntity(
@PathVariable id: Long,
): ResponseEntity<Void> {
LOGGER.info("Deleting {} with id: {}", clazz.simpleName, id)
service.deleteEntity(id)
return ResponseEntity.noContent().build()
}
}