Paging, Sorting for GET

Validation


@Configuration
class SpringDataRestConfiguration : RepositoryRestConfigurer {
    override fun configureRepositoryRestConfiguration(
        config: RepositoryRestConfiguration, cors: CorsRegistry
    ) {
        config.exposeIdsFor(Template::class.java)
    }

    override fun configureValidatingRepositoryEventListener(
                    validatingListener: ValidatingRepositoryEventListener?
    ) {
        validatingListener?
            .addValidator("beforeCreate", TemplateValidator())
    }

    class TemplateValidator : Validator {
        override fun supports(clazz: Class<*>): Boolean =
            Template::class.java.equals(clazz)

        override fun validate(target: Any, errors: Errors) {
            val template = target as Template

            if (template.name != null && template.name!!.length < 3)
                errors.rejectValue("name", "name.too.short")

            DocumentBuilderFactory.newInstance().newDocumentBuilder()
                .runCatching { parse(template.template.byteInputStream()) }
                .onFailure {
                    errors.rejectValue("name", "incorrect.template")
                }
        }
    }
}
                    

API and model tuning


@Entity
@Table(name = "templates")
class Template {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    var id: Long? = null

    @Column(name = "name")
    @Size(min = 3) // doesn't work
    var name: String? = null

    @Column(name = "type")
    var type: TemplateType = TemplateType.SVG

    @Column(name = "template")
    var template: String = ""

    @Column(name = "created")
    @JsonProperty(value = "created_at", 
        access = JsonProperty.Access.READ_ONLY)
    var created: Date? = null

    @Column(name = "updated")
    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    var updated: Date? = null

    @PrePersist
    fun onCreate() {
        updated = Date()
        created = Date()
    }
}

@Configuration
class SpringDataRestConfiguration : RepositoryRestConfigurer {
    override fun configureRepositoryRestConfiguration(
        config: RepositoryRestConfiguration, cors: CorsRegistry
    ) {
        config.exposeIdsFor(Template::class.java)
    }
}
                    

Querying

Alternative

CRUD but nicely


@RestController
@RequestMapping(path = ["/api/new/templates"])
class TemplateController(val templateRepository: SpringTemplateRepository) {

    @PostMapping
    @Transactional
    fun create(
        @Valid @RequestBody templateCreateDTO: TemplateCreateDTO
    ) {
        templateRepository.save(
            Template.create(
                templateCreateDTO.name,
                templateCreateDTO.type,
                templateCreateDTO.contents
            )
        )
    }

    @GetMapping
    @Transactional
    fun retrieve(
        @PageableDefault(size = 50) paging: Pageable, 
        @PathVariable name: String
    ): List =
        templateRepository
            .findAllByNameContaining(name, paging).toList()

    @PutMapping(path = ["/{id}"])
    @Transactional
    fun update(
        @PathVariable id: Long, 
        @Valid @RequestBody templateCreateDTO: TemplateCreateDTO
    ) {
        templateRepository.findById(id)
            .orElseThrow { ResourceNotFoundException() }
            .apply {
                name = templateCreateDTO.name
                type = templateCreateDTO.type
                template = templateCreateDTO.contents
            }.also {
                templateRepository.save(it)
            }
    }

    @DeleteMapping(path = ["/{id}"])
    @Transactional
    fun delete(@PathVariable id: Long) =
        templateRepository.findById(id)
            .orElseThrow { ResourceNotFoundException() }
            .also { templateRepository.delete(it) }


}

data class TemplateCreateDTO(
    val name: String,

    val type: Template.TemplateType,

    @field:ValidXml(message = "SVG file is invalid bruh")
    val contents: String
);

                    

But is it a correct CRUD?