feature/#25-dtos #28
|
@ -2,13 +2,14 @@ package dev.fyloz.colorrecipesexplorer
|
|||
|
||||
object Constants {
|
||||
object ControllerPaths {
|
||||
const val file = "/api/file"
|
||||
const val material = "/api/material"
|
||||
const val FILE = "/api/file"
|
||||
const val MATERIAL = "/api/material"
|
||||
const val MATERIAL_TYPE = "/api/materialtype"
|
||||
}
|
||||
|
||||
object FilePaths {
|
||||
const val pdfs = "pdf"
|
||||
const val PDF = "pdf"
|
||||
|
||||
const val simdut = "$pdfs/simdut"
|
||||
const val SIMDUT = "$PDF/simdut"
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ package dev.fyloz.colorrecipesexplorer.config.initializers
|
|||
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.logic.MaterialTypeLogic
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
|
@ -23,17 +23,16 @@ class MaterialTypeInitializer(
|
|||
|
||||
private fun ensureSystemMaterialTypesExists() {
|
||||
val systemTypes = materialTypeProperties.systemTypes.map { it.toMaterialType() }
|
||||
val oldSystemTypes = materialTypeLogic.getAllSystemTypes().toMutableSet()
|
||||
val oldSystemTypes = materialTypeLogic.getAll(true).toMutableSet()
|
||||
|
||||
fun saveOrUpdateSystemType(type: MaterialType) {
|
||||
if (materialTypeLogic.existsByName(type.name)) {
|
||||
with(materialTypeLogic.getByName(type.name)) {
|
||||
if (!this.systemType) {
|
||||
logger.info("Material type '${type.name}' already exists and will be flagged as a system type")
|
||||
materialTypeLogic.update(this.copy(systemType = true))
|
||||
} else {
|
||||
logger.debug("System material type '${type.name}' already exists")
|
||||
}
|
||||
fun saveOrUpdateSystemType(type: MaterialTypeDto) {
|
||||
val storedMaterialType = materialTypeLogic.getByName(type.name)
|
||||
if (storedMaterialType != null) {
|
||||
if (!storedMaterialType.systemType) {
|
||||
logger.info("Material type '${type.name}' already exists and will be flagged as a system type")
|
||||
materialTypeLogic.update(storedMaterialType.copy(systemType = true))
|
||||
} else {
|
||||
logger.debug("System material type '${type.name}' already exists")
|
||||
}
|
||||
} else {
|
||||
logger.info("System material type '${type.name}' will be created")
|
||||
|
@ -50,7 +49,7 @@ class MaterialTypeInitializer(
|
|||
// Remove old system types
|
||||
oldSystemTypes.forEach {
|
||||
logger.info("Material type '${it.name}' is not a system type anymore")
|
||||
materialTypeLogic.updateSystemType(it.copy(systemType = false))
|
||||
materialTypeLogic.updateNonSystemType(it.copy(systemType = false))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package dev.fyloz.colorrecipesexplorer.config.properties
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import dev.fyloz.colorrecipesexplorer.model.materialType
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
@ -16,9 +17,9 @@ class MaterialTypeProperties {
|
|||
var prefix: String = "",
|
||||
var usePercentages: Boolean = false
|
||||
) {
|
||||
fun toMaterialType(): MaterialType {
|
||||
fun toMaterialType(): MaterialTypeDto {
|
||||
Assert.hasText(name, "A system material type has an empty name")
|
||||
return materialType(name = name, prefix = prefix, usePercentages = usePercentages, systemType = true)
|
||||
return MaterialTypeDto(name = name, prefix = prefix, usePercentages = usePercentages, systemType = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package dev.fyloz.colorrecipesexplorer.dtos
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import javax.validation.constraints.Min
|
||||
import javax.validation.constraints.NotBlank
|
||||
|
@ -14,7 +13,7 @@ data class MaterialDto(
|
|||
|
||||
val isMixType: Boolean,
|
||||
|
||||
val materialType: MaterialType,
|
||||
val materialType: MaterialTypeDto,
|
||||
|
||||
val simdutUrl: String? = null
|
||||
) : EntityDto
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package dev.fyloz.colorrecipesexplorer.dtos
|
||||
|
||||
data class MaterialTypeDto(
|
||||
override val id: Long = 0L,
|
||||
|
||||
val name: String,
|
||||
|
||||
val prefix: String,
|
||||
|
||||
val usePercentages: Boolean,
|
||||
|
||||
val systemType: Boolean = false
|
||||
) : EntityDto
|
|
@ -19,13 +19,13 @@ interface Logic<D : EntityDto, S : Service<D, *, *>> {
|
|||
/** Get all DTOs. */
|
||||
fun getAll(): Collection<D>
|
||||
|
||||
/** Get the DTO for the given [id]. */
|
||||
/** Get the DTO for the given [id]. Throws if no DTO were found. */
|
||||
fun getById(id: Long): D
|
||||
|
||||
/** Saves the given [dto]. */
|
||||
fun save(dto: D): D
|
||||
|
||||
/** Updates the given [dto]. */
|
||||
/** Updates the given [dto]. Throws if no DTO with the same id exists. */
|
||||
fun update(dto: D): D
|
||||
|
||||
/** Deletes the dto with the given [id]. */
|
||||
|
|
|
@ -1,88 +1,75 @@
|
|||
package dev.fyloz.colorrecipesexplorer.logic
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.model.validation.isNotNullAndNotBlank
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository
|
||||
import org.springframework.stereotype.Service
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import dev.fyloz.colorrecipesexplorer.service.MaterialTypeService
|
||||
|
||||
interface MaterialTypeLogic :
|
||||
ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository> {
|
||||
/** Checks if a material type with the given [prefix] exists. */
|
||||
fun existsByPrefix(prefix: String): Boolean
|
||||
interface MaterialTypeLogic : Logic<MaterialTypeDto, MaterialTypeService> {
|
||||
/** Gets all material types which are or not [systemType]s. */
|
||||
fun getAll(systemType: Boolean): Collection<MaterialTypeDto>
|
||||
|
||||
/** Gets all system material types. */
|
||||
fun getAllSystemTypes(): Collection<MaterialType>
|
||||
/** Gets the material type with the given [name]. */
|
||||
fun getByName(name: String): MaterialTypeDto?
|
||||
|
||||
/** Gets all material types who are not a system type. */
|
||||
fun getAllNonSystemType(): Collection<MaterialType>
|
||||
|
||||
/** Allows to update the given system [materialType], should not be exposed to users. */
|
||||
fun updateSystemType(materialType: MaterialType): MaterialType
|
||||
/** Updates the given [dto], and throws if it is a system types. */
|
||||
fun updateNonSystemType(dto: MaterialTypeDto)
|
||||
}
|
||||
|
||||
@Service
|
||||
@RequireDatabase
|
||||
class DefaultMaterialTypeLogic(repository: MaterialTypeRepository) :
|
||||
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository>(
|
||||
repository
|
||||
), MaterialTypeLogic {
|
||||
override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = materialIdAlreadyExistsException(id)
|
||||
override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name)
|
||||
override fun nameAlreadyExistsException(name: String) = materialTypeNameAlreadyExistsException(name)
|
||||
@LogicComponent
|
||||
class DefaultMaterialTypeLogic(service: MaterialTypeService) :
|
||||
BaseLogic<MaterialTypeDto, MaterialTypeService>(service, MaterialType::class.simpleName!!), MaterialTypeLogic {
|
||||
override fun getAll(systemType: Boolean) = service.getAll(systemType)
|
||||
override fun getByName(name: String) = service.getByName(name)
|
||||
|
||||
override fun MaterialType.toOutput() = this
|
||||
|
||||
override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix)
|
||||
|
||||
override fun getAllSystemTypes(): Collection<MaterialType> = repository.findAllBySystemTypeIs(true)
|
||||
override fun getAllNonSystemType(): Collection<MaterialType> = repository.findAllBySystemTypeIs(false)
|
||||
|
||||
override fun save(entity: MaterialType): MaterialType {
|
||||
if (existsByPrefix(entity.prefix))
|
||||
throw materialTypePrefixAlreadyExistsException(entity.prefix)
|
||||
return super<AbstractExternalNamedModelService>.save(entity)
|
||||
}
|
||||
|
||||
override fun update(entity: MaterialTypeUpdateDto): MaterialType {
|
||||
val persistedMaterialType by lazy { getById(entity.id) }
|
||||
|
||||
return update(with(entity) {
|
||||
MaterialType(
|
||||
id = id,
|
||||
name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name,
|
||||
prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix,
|
||||
systemType = false
|
||||
override fun updateNonSystemType(dto: MaterialTypeDto) {
|
||||
if (service.existsById(dto.id, true)) {
|
||||
throw CannotUpdateException(
|
||||
typeNameLowerCase,
|
||||
"Cannot update $typeNameLowerCase",
|
||||
"Cannot update material type '${dto.name}' because it is a system material type"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun updateSystemType(materialType: MaterialType) =
|
||||
update(materialType, true)
|
||||
|
||||
override fun update(entity: MaterialType) =
|
||||
update(entity, false)
|
||||
|
||||
private fun update(entity: MaterialType, allowSystemTypes: Boolean): MaterialType {
|
||||
if (!allowSystemTypes && repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) {
|
||||
throw cannotUpdateSystemMaterialTypeException(entity)
|
||||
}
|
||||
|
||||
with(repository.findByPrefix(entity.prefix)) {
|
||||
if (this != null && id != entity.id)
|
||||
throw materialTypePrefixAlreadyExistsException(entity.prefix)
|
||||
}
|
||||
|
||||
return super.update(entity)
|
||||
update(dto)
|
||||
}
|
||||
|
||||
override fun delete(entity: MaterialType) {
|
||||
if (repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) {
|
||||
throw cannotDeleteSystemMaterialTypeException(entity)
|
||||
override fun save(dto: MaterialTypeDto): MaterialTypeDto {
|
||||
throwIfNameAlreadyExists(dto.name)
|
||||
throwIfPrefixAlreadyExists(dto.prefix)
|
||||
|
||||
return super.save(dto)
|
||||
}
|
||||
|
||||
override fun update(dto: MaterialTypeDto): MaterialTypeDto {
|
||||
throwIfNameAlreadyExists(dto.name, dto.id)
|
||||
throwIfPrefixAlreadyExists(dto.prefix, dto.id)
|
||||
|
||||
return super.update(dto)
|
||||
}
|
||||
|
||||
override fun deleteById(id: Long) {
|
||||
if (service.isUsedByMaterial(id)) {
|
||||
throw cannotDeleteException("Cannot delete material type with the id '$id' because one or more materials depends on it")
|
||||
}
|
||||
|
||||
if (!repository.canBeDeleted(entity.id)) throw cannotDeleteMaterialTypeException(entity)
|
||||
super.delete(entity)
|
||||
super.deleteById(id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun throwIfNameAlreadyExists(name: String, id: Long? = null) {
|
||||
if (service.existsByName(name, id)) {
|
||||
throw alreadyExistsException(value = name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun throwIfPrefixAlreadyExists(prefix: String, id: Long? = null) {
|
||||
if (service.existsByPrefix(prefix, id)) {
|
||||
throw alreadyExistsException(PREFIX_IDENTIFIER_NAME, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREFIX_IDENTIFIER_NAME = "prefix"
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ class DefaultMixLogic(
|
|||
override fun save(entity: MixSaveDto): Mix {
|
||||
val recipe = recipeLogic.getById(entity.recipeId)
|
||||
val materialType = materialTypeLogic.getById(entity.materialTypeId)
|
||||
val mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(entity.name, materialType)
|
||||
val mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(entity.name, materialType(materialType))
|
||||
|
||||
val mixMaterials = if (entity.mixMaterials != null) mixMaterialLogic.create(entity.mixMaterials) else setOf()
|
||||
mixMaterialLogic.validateMixMaterials(mixMaterials)
|
||||
|
@ -72,7 +72,7 @@ class DefaultMixLogic(
|
|||
if (entity.name != null || entity.materialTypeId != null) {
|
||||
val name = entity.name ?: mix.mixType.name
|
||||
val materialType = if (entity.materialTypeId != null)
|
||||
materialTypeLogic.getById(entity.materialTypeId)
|
||||
materialType(materialTypeLogic.getById(entity.materialTypeId))
|
||||
else
|
||||
mix.mixType.material.materialType!!
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ data class Material(
|
|||
) : ModelEntity {
|
||||
companion object {
|
||||
fun getSimdutFilePath(name: String) =
|
||||
"${Constants.FilePaths.simdut}/$name.pdf"
|
||||
"${Constants.FilePaths.SIMDUT}/$name.pdf"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,12 +66,12 @@ fun material(
|
|||
@Deprecated("Temporary DSL for transition")
|
||||
fun material(
|
||||
dto: MaterialDto
|
||||
) = Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, dto.materialType)
|
||||
) = Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, materialType(dto.materialType))
|
||||
|
||||
@Deprecated("Temporary DSL for transition")
|
||||
fun materialDto(
|
||||
entity: Material
|
||||
) = MaterialDto(entity.id!!, entity.name, entity.inventoryQuantity, entity.isMixType, entity.materialType!!)
|
||||
) = MaterialDto(entity.id!!, entity.name, entity.inventoryQuantity, entity.isMixType, materialTypeDto(entity.materialType!!))
|
||||
|
||||
fun materialQuantityDto(
|
||||
materialId: Long,
|
||||
|
|
|
@ -1,171 +1,62 @@
|
|||
package dev.fyloz.colorrecipesexplorer.model
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import org.hibernate.annotations.ColumnDefault
|
||||
import javax.persistence.*
|
||||
import javax.validation.constraints.NotBlank
|
||||
import javax.validation.constraints.Size
|
||||
|
||||
private const val VALIDATION_PREFIX_SIZE = "Must contains exactly 3 characters"
|
||||
|
||||
@Entity
|
||||
@Table(name = "material_type")
|
||||
data class MaterialType(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long? = null,
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long? = null,
|
||||
|
||||
@Column(unique = true)
|
||||
override val name: String = "",
|
||||
@Column(unique = true)
|
||||
val name: String = "",
|
||||
|
||||
@Column(unique = true)
|
||||
val prefix: String = "",
|
||||
@Column(unique = true)
|
||||
val prefix: String = "",
|
||||
|
||||
@Column(name = "use_percentages")
|
||||
@ColumnDefault("false")
|
||||
val usePercentages: Boolean = false,
|
||||
@Column(name = "use_percentages")
|
||||
@ColumnDefault("false")
|
||||
val usePercentages: Boolean = false,
|
||||
|
||||
@Column(name = "system_type")
|
||||
@ColumnDefault("false")
|
||||
val systemType: Boolean = false
|
||||
) : NamedModelEntity
|
||||
|
||||
open class MaterialTypeSaveDto(
|
||||
@field:NotBlank
|
||||
val name: String,
|
||||
|
||||
@field:NotBlank
|
||||
@field:Size(min = 3, max = 3, message = VALIDATION_PREFIX_SIZE)
|
||||
val prefix: String,
|
||||
|
||||
val usePercentages: Boolean = false
|
||||
) : EntityDto<MaterialType> {
|
||||
override fun toEntity(): MaterialType =
|
||||
MaterialType(null, name, prefix, usePercentages)
|
||||
}
|
||||
|
||||
open class MaterialTypeUpdateDto(
|
||||
val id: Long,
|
||||
|
||||
@field:NotBlank
|
||||
val name: String?,
|
||||
|
||||
@field:Size(min = 3, max = 3, message = VALIDATION_PREFIX_SIZE)
|
||||
val prefix: String?
|
||||
) : EntityDto<MaterialType> {
|
||||
override fun toEntity(): MaterialType =
|
||||
MaterialType(id, name ?: "", prefix ?: "")
|
||||
}
|
||||
@Column(name = "system_type")
|
||||
@ColumnDefault("false")
|
||||
val systemType: Boolean = false
|
||||
) : ModelEntity
|
||||
|
||||
// ==== DSL ====
|
||||
fun materialType(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
prefix: String = "PRE",
|
||||
usePercentages: Boolean = false,
|
||||
systemType: Boolean = false,
|
||||
op: MaterialType.() -> Unit = {}
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
prefix: String = "PRE",
|
||||
usePercentages: Boolean = false,
|
||||
systemType: Boolean = false,
|
||||
op: MaterialType.() -> Unit = {}
|
||||
) = MaterialType(id, name, prefix, usePercentages, systemType).apply(op)
|
||||
|
||||
fun materialType(
|
||||
materialType: MaterialType,
|
||||
newId: Long? = null,
|
||||
newName: String? = null,
|
||||
newSystemType: Boolean? = null
|
||||
materialType: MaterialType,
|
||||
newId: Long? = null,
|
||||
newName: String? = null,
|
||||
newSystemType: Boolean? = null
|
||||
) = with(materialType) {
|
||||
MaterialType(
|
||||
newId ?: id,
|
||||
newName ?: name,
|
||||
prefix,
|
||||
usePercentages,
|
||||
newSystemType ?: systemType
|
||||
newId ?: id,
|
||||
newName ?: name,
|
||||
prefix,
|
||||
usePercentages,
|
||||
newSystemType ?: systemType
|
||||
)
|
||||
}
|
||||
|
||||
fun materialTypeSaveDto(
|
||||
name: String = "name",
|
||||
prefix: String = "PRE",
|
||||
usePercentages: Boolean = false,
|
||||
op: MaterialTypeSaveDto.() -> Unit = {}
|
||||
) = MaterialTypeSaveDto(name, prefix, usePercentages).apply(op)
|
||||
@Deprecated("Temporary DSL for transition")
|
||||
fun materialType(
|
||||
dto: MaterialTypeDto
|
||||
) = MaterialType(dto.id, dto.name, dto.prefix, dto.usePercentages, dto.systemType)
|
||||
|
||||
fun materialTypeUpdateDto(
|
||||
id: Long = 0L,
|
||||
name: String? = null,
|
||||
prefix: String? = null,
|
||||
op: MaterialTypeUpdateDto.() -> Unit = {}
|
||||
) = MaterialTypeUpdateDto(id, name, prefix).apply(op)
|
||||
|
||||
// ==== Exceptions ====
|
||||
private const val MATERIAL_TYPE_NOT_FOUND_EXCEPTION_TITLE = "Material type not found"
|
||||
private const val MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE = "Material type already exists"
|
||||
private const val MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete material type"
|
||||
private const val MATERIAL_TYPE_CANNOT_UPDATE_EXCEPTION_TITLE = "Cannot update material type"
|
||||
private const val MATERIAL_TYPE_EXCEPTION_ERROR_CODE = "materialtype"
|
||||
|
||||
fun materialTypeIdNotFoundException(id: Long) =
|
||||
NotFoundException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A material type with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
|
||||
fun materialTypeNameNotFoundException(name: String) =
|
||||
NotFoundException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A material type with the name $name could not be found",
|
||||
name,
|
||||
"name"
|
||||
)
|
||||
|
||||
fun materialTypeIdAlreadyExistsException(id: Long) =
|
||||
AlreadyExistsException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A material type with the id $id already exists",
|
||||
id
|
||||
)
|
||||
|
||||
fun materialTypeNameAlreadyExistsException(name: String) =
|
||||
AlreadyExistsException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A material type with the name $name already exists",
|
||||
name,
|
||||
"name"
|
||||
)
|
||||
|
||||
fun materialTypePrefixAlreadyExistsException(prefix: String) =
|
||||
AlreadyExistsException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A material type with the prefix $prefix already exists",
|
||||
prefix,
|
||||
"prefix"
|
||||
)
|
||||
|
||||
fun cannotUpdateSystemMaterialTypeException(materialType: MaterialType) =
|
||||
CannotUpdateException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_CANNOT_UPDATE_EXCEPTION_TITLE,
|
||||
"Cannot update material type ${materialType.name} because it is a system material type"
|
||||
)
|
||||
|
||||
fun cannotDeleteMaterialTypeException(materialType: MaterialType) =
|
||||
CannotDeleteException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE,
|
||||
"Cannot delete material type ${materialType.name} because one or more materials depends on it"
|
||||
)
|
||||
|
||||
fun cannotDeleteSystemMaterialTypeException(materialType: MaterialType) =
|
||||
CannotDeleteException(
|
||||
MATERIAL_TYPE_EXCEPTION_ERROR_CODE,
|
||||
MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE,
|
||||
"Cannot delete material type ${materialType.name} because it is a system material type"
|
||||
)
|
||||
@Deprecated("Temporary DSL for transition")
|
||||
fun materialTypeDto(
|
||||
entity: MaterialType
|
||||
) = MaterialTypeDto(entity.id!!, entity.name, entity.prefix, entity.usePercentages, entity.systemType)
|
|
@ -70,7 +70,7 @@ data class Recipe(
|
|||
groupsInformation.firstOrNull { it.group.id == groupId }
|
||||
|
||||
fun imageUrl(deploymentUrl: String, name: String) =
|
||||
"$deploymentUrl${Constants.ControllerPaths.file}?path=${
|
||||
"$deploymentUrl${Constants.ControllerPaths.FILE}?path=${
|
||||
URLEncoder.encode(
|
||||
"${this.imagesDirectoryPath}/$name",
|
||||
StandardCharsets.UTF_8
|
||||
|
|
|
@ -12,7 +12,7 @@ interface MaterialRepository : JpaRepository<Material, Long> {
|
|||
fun existsByNameAndIdNot(name: String, id: Long): Boolean
|
||||
|
||||
/** Gets all non mix type materials. */
|
||||
fun getAllByIsMixTypeIsFalse(): Collection<Material>
|
||||
fun findAllByIsMixTypeIsFalse(): Collection<Material>
|
||||
|
||||
/** Updates the [inventoryQuantity] of the [Material] with the given [id]. */
|
||||
@Modifying
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
package dev.fyloz.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface MaterialTypeRepository : NamedJpaRepository<MaterialType> {
|
||||
/** Checks if a material type exists with the given [prefix]. */
|
||||
fun existsByPrefix(prefix: String): Boolean
|
||||
|
||||
interface MaterialTypeRepository : JpaRepository<MaterialType, Long> {
|
||||
/** Checks if a system material type with the given [id] exists. */
|
||||
fun existsByIdAndSystemTypeIsTrue(id: Long): Boolean
|
||||
fun existsByIdAndSystemTypeIs(id: Long, systemType: Boolean): Boolean
|
||||
|
||||
/** Gets all material types which are not system types. */
|
||||
fun findAllBySystemTypeIs(value: Boolean): Collection<MaterialType>
|
||||
/** Checks if a material type with the given [name] and a different [id] exists. */
|
||||
fun existsByNameAndIdNot(name: String, id: Long): Boolean
|
||||
|
||||
/** Gets the material type with the given [prefix]. */
|
||||
fun findByPrefix(prefix: String): MaterialType?
|
||||
/** Checks if a material type with the given [prefix] and a different [id] exists. */
|
||||
fun existsByPrefixAndIdNot(prefix: String, id: Long): Boolean
|
||||
|
||||
/** Find all material types which are or not [systemType]s. */
|
||||
fun findAllBySystemTypeIs(systemType: Boolean): Collection<MaterialType>
|
||||
|
||||
/** Find the material type with the given [name]. */
|
||||
fun findByName(name: String): MaterialType?
|
||||
|
||||
/** Checks if a material depends on the material type with the given [id]. */
|
||||
@Query(
|
||||
"""
|
||||
select case when(count(m.id) > 0) then false else true end
|
||||
from MaterialType t
|
||||
left join Material m on t.id = m.materialType.id
|
||||
where t.id = :id
|
||||
"""
|
||||
"""
|
||||
select case when(count(m) > 0) then true else false end
|
||||
from Material m where m.materialType.id = :id
|
||||
"""
|
||||
)
|
||||
fun canBeDeleted(id: Long): Boolean
|
||||
fun isUsedByMaterial(id: Long): Boolean
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.springframework.web.multipart.MultipartFile
|
|||
import java.net.URI
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.ControllerPaths.file)
|
||||
@RequestMapping(Constants.ControllerPaths.FILE)
|
||||
class FileController(
|
||||
private val fileLogic: WriteableFileLogic,
|
||||
private val configurationLogic: ConfigurationLogic
|
||||
|
@ -43,6 +43,6 @@ class FileController(
|
|||
|
||||
private fun created(path: String): ResponseEntity<Void> =
|
||||
ResponseEntity
|
||||
.created(URI.create("${configurationLogic.get(ConfigurationType.INSTANCE_URL)}${Constants.ControllerPaths.file}?path=$path"))
|
||||
.created(URI.create("${configurationLogic.get(ConfigurationType.INSTANCE_URL)}${Constants.ControllerPaths.FILE}?path=$path"))
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.springframework.web.multipart.MultipartFile
|
|||
import javax.validation.Valid
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.ControllerPaths.material)
|
||||
@RequestMapping(Constants.ControllerPaths.MATERIAL)
|
||||
@Profile("!emergency")
|
||||
@PreAuthorizeViewCatalog
|
||||
class MaterialController(
|
||||
|
@ -34,7 +34,7 @@ class MaterialController(
|
|||
@PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
@PreAuthorize("hasAuthority('EDIT_MATERIALS')")
|
||||
fun save(@Valid material: MaterialSaveDto, simdutFile: MultipartFile?) =
|
||||
created<MaterialDto>(Constants.ControllerPaths.material) {
|
||||
created<MaterialDto>(Constants.ControllerPaths.MATERIAL) {
|
||||
materialLogic.save(material.copy(simdutFile = simdutFile))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,49 +1,46 @@
|
|||
package dev.fyloz.colorrecipesexplorer.rest
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.Constants
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewCatalog
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.logic.MaterialTypeLogic
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialTypeSaveDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialTypeUpdateDto
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import javax.validation.Valid
|
||||
|
||||
private const val MATERIAL_TYPE_CONTROLLER_PATH = "api/materialtype"
|
||||
|
||||
@RestController
|
||||
@RequestMapping(MATERIAL_TYPE_CONTROLLER_PATH)
|
||||
@RequestMapping(Constants.ControllerPaths.MATERIAL_TYPE)
|
||||
@Profile("!emergency")
|
||||
@PreAuthorizeViewCatalog
|
||||
class MaterialTypeController(private val materialTypeLogic: MaterialTypeLogic) {
|
||||
@GetMapping
|
||||
fun getAll() =
|
||||
ok(materialTypeLogic.getAllForOutput())
|
||||
ok(materialTypeLogic.getAll())
|
||||
|
||||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(materialTypeLogic.getByIdForOutput(id))
|
||||
ok(materialTypeLogic.getById(id))
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')")
|
||||
fun save(@Valid @RequestBody materialType: MaterialTypeSaveDto) =
|
||||
created<MaterialType>(MATERIAL_TYPE_CONTROLLER_PATH) {
|
||||
materialTypeLogic.save(materialType)
|
||||
}
|
||||
fun save(@Valid @RequestBody materialType: MaterialTypeDto) =
|
||||
created<MaterialTypeDto>(Constants.ControllerPaths.MATERIAL_TYPE) {
|
||||
materialTypeLogic.save(materialType)
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')")
|
||||
fun update(@Valid @RequestBody materialType: MaterialTypeUpdateDto) =
|
||||
noContent {
|
||||
materialTypeLogic.update(materialType)
|
||||
}
|
||||
fun update(@Valid @RequestBody materialType: MaterialTypeDto) =
|
||||
noContent {
|
||||
materialTypeLogic.updateNonSystemType(materialType)
|
||||
}
|
||||
|
||||
@DeleteMapping("{id}")
|
||||
@PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')")
|
||||
fun deleteById(@PathVariable id: Long) =
|
||||
noContent {
|
||||
materialTypeLogic.deleteById(id)
|
||||
}
|
||||
noContent {
|
||||
materialTypeLogic.deleteById(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,17 @@ interface MaterialService : Service<MaterialDto, Material, MaterialRepository> {
|
|||
}
|
||||
|
||||
@ServiceComponent
|
||||
class DefaultMaterialService(repository: MaterialRepository, @Qualifier("defaultFileLogic") val fileLogic: FileLogic) :
|
||||
class DefaultMaterialService(
|
||||
repository: MaterialRepository,
|
||||
private val materialTypeService: MaterialTypeService,
|
||||
@Qualifier("defaultFileLogic") val fileLogic: FileLogic
|
||||
) :
|
||||
BaseService<MaterialDto, Material, MaterialRepository>(repository), MaterialService {
|
||||
override fun existsByName(name: String, id: Long?) = repository.existsByNameAndIdNot(name, id ?: 0)
|
||||
override fun getAllNotMixType() = repository.getAllByIsMixTypeIsFalse().map(this::toDto)
|
||||
override fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) = repository.updateInventoryQuantityById(id, inventoryQuantity)
|
||||
override fun getAllNotMixType() = repository.findAllByIsMixTypeIsFalse().map(::toDto)
|
||||
override fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) =
|
||||
repository.updateInventoryQuantityById(id, inventoryQuantity)
|
||||
|
||||
override fun isUsedByMixMaterialOrMixType(id: Long) = repository.isUsedByMixMaterialOrMixType(id)
|
||||
|
||||
override fun toDto(entity: Material) =
|
||||
|
@ -38,21 +44,21 @@ class DefaultMaterialService(repository: MaterialRepository, @Qualifier("default
|
|||
entity.name,
|
||||
entity.inventoryQuantity,
|
||||
entity.isMixType,
|
||||
entity.materialType!!,
|
||||
materialTypeService.toDto(entity.materialType!!),
|
||||
getSimdutUrl(entity)
|
||||
)
|
||||
|
||||
override fun toEntity(dto: MaterialDto) =
|
||||
Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, dto.materialType)
|
||||
Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, materialTypeService.toEntity(dto.materialType))
|
||||
|
||||
private fun getSimdutUrl(material: Material): String? {
|
||||
val filePath = "${Constants.FilePaths.simdut}/${material.name}.pdf"
|
||||
val filePath = "${Constants.FilePaths.SIMDUT}/${material.name}.pdf"
|
||||
|
||||
if (!fileLogic.exists(filePath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val encodedPath = URLEncoder.encode(filePath, StandardCharsets.UTF_8)
|
||||
return "${Constants.ControllerPaths.file}?path=$encodedPath"
|
||||
return "${Constants.ControllerPaths.FILE}?path=$encodedPath"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository
|
||||
|
||||
interface MaterialTypeService : Service<MaterialTypeDto, MaterialType, MaterialTypeRepository> {
|
||||
/** Checks if a system material type with the given [id] exists. */
|
||||
fun existsById(id: Long, systemType: Boolean): Boolean
|
||||
|
||||
/** Checks if a material type with the given [name] and a different [id] exists. */
|
||||
fun existsByName(name: String, id: Long?): Boolean
|
||||
|
||||
/** Checks if a material type with the given [prefix] and a different [id] exists. */
|
||||
fun existsByPrefix(prefix: String, id: Long?): Boolean
|
||||
|
||||
/** Gets all material types which are or not a [systemType]. */
|
||||
fun getAll(systemType: Boolean): Collection<MaterialTypeDto>
|
||||
|
||||
/** Gets the material type with the given [name]. */
|
||||
fun getByName(name: String): MaterialTypeDto?
|
||||
|
||||
/** Checks if a material depends on the material type with the given [id]. */
|
||||
fun isUsedByMaterial(id: Long): Boolean
|
||||
}
|
||||
|
||||
@ServiceComponent
|
||||
class DefaultMaterialTypeService(repository: MaterialTypeRepository) :
|
||||
BaseService<MaterialTypeDto, MaterialType, MaterialTypeRepository>(repository), MaterialTypeService {
|
||||
override fun existsById(id: Long, systemType: Boolean) = repository.existsByIdAndSystemTypeIs(id, systemType)
|
||||
override fun existsByName(name: String, id: Long?) = repository.existsByNameAndIdNot(name, id ?: 0)
|
||||
override fun existsByPrefix(prefix: String, id: Long?) = repository.existsByPrefixAndIdNot(prefix, id ?: 0)
|
||||
override fun getAll(systemType: Boolean) = repository.findAllBySystemTypeIs(systemType).map(::toDto)
|
||||
override fun getByName(name: String) = repository.findByName(name)?.let(::toDto)
|
||||
|
||||
override fun isUsedByMaterial(id: Long) = repository.isUsedByMaterial(id)
|
||||
|
||||
override fun toDto(entity: MaterialType) =
|
||||
MaterialTypeDto(entity.id!!, entity.name, entity.prefix, entity.usePercentages, entity.systemType)
|
||||
|
||||
override fun toEntity(dto: MaterialTypeDto) =
|
||||
MaterialType(dto.id, dto.name, dto.prefix, dto.usePercentages, dto.systemType)
|
||||
}
|
|
@ -31,6 +31,12 @@ interface Service<D : EntityDto, E : ModelEntity, R : JpaRepository<E, Long>> {
|
|||
|
||||
/** Deletes the entity with the given [id]. */
|
||||
fun deleteById(id: Long)
|
||||
|
||||
/** Converts the given [entity] to a DTO. */
|
||||
fun toDto(entity: E): D
|
||||
|
||||
/** Converts the given [dto] to an entity. */
|
||||
fun toEntity(dto: D): E
|
||||
}
|
||||
|
||||
abstract class BaseService<D : EntityDto, E : ModelEntity, R : JpaRepository<E, Long>>(protected val repository: R) :
|
||||
|
@ -58,7 +64,4 @@ abstract class BaseService<D : EntityDto, E : ModelEntity, R : JpaRepository<E,
|
|||
override fun deleteById(id: Long) {
|
||||
repository.deleteById(id)
|
||||
}
|
||||
|
||||
abstract fun toDto(entity: E): D
|
||||
abstract fun toEntity(dto: D): E
|
||||
}
|
|
@ -2,6 +2,7 @@ package dev.fyloz.colorrecipesexplorer.logic
|
|||
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialSaveDto
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
||||
|
@ -31,9 +32,7 @@ class DefaultMaterialLogicTest {
|
|||
)
|
||||
)
|
||||
|
||||
private val materialType = MaterialType(
|
||||
1L, "Unit test material type", "UNT", usePercentages = false, systemType = false
|
||||
) // TODO move to DTO
|
||||
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UNT", usePercentages = false)
|
||||
private val material = MaterialDto(1L, "Unit test material", 1000f, false, materialType)
|
||||
private val materialMixType = material.copy(id = 2L, isMixType = true)
|
||||
private val materialMixType2 = material.copy(id = 3L, isMixType = true)
|
||||
|
@ -59,8 +58,8 @@ class DefaultMaterialLogicTest {
|
|||
private val simdutFileMock = MockMultipartFile(
|
||||
"Unit test SIMDUT",
|
||||
byteArrayOf(1, 2, 3, 4)
|
||||
) // Put some content in the mock file so it is not ignored
|
||||
private val materialSaveDto = MaterialSaveDto(1L, "Unit test material", 1000f, materialType.id!!, simdutFileMock)
|
||||
) // Put some content in the mock file, so it is not ignored
|
||||
private val materialSaveDto = MaterialSaveDto(1L, "Unit test material", 1000f, materialType.id, simdutFileMock)
|
||||
|
||||
init {
|
||||
recipe.mixes.addAll(listOf(mix, mix2))
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package dev.fyloz.colorrecipesexplorer.logic
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
||||
import dev.fyloz.colorrecipesexplorer.service.MaterialTypeService
|
||||
import io.mockk.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class DefaultMaterialTypeLogicTest {
|
||||
private val materialTypeServiceMock = mockk<MaterialTypeService>()
|
||||
|
||||
private val materialTypeLogic = spyk(DefaultMaterialTypeLogic(materialTypeServiceMock))
|
||||
|
||||
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false)
|
||||
private val systemMaterialType =
|
||||
MaterialTypeDto(2L, "Unit test system material type", "UTSMT", false, systemType = true)
|
||||
|
||||
@AfterEach
|
||||
internal fun afterEach() {
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAll_normalBehavior_returnsFromService() {
|
||||
// Arrange
|
||||
val expectedMaterialTypes = listOf(materialType, systemMaterialType)
|
||||
|
||||
every { materialTypeServiceMock.getAll(any()) } returns expectedMaterialTypes
|
||||
|
||||
// Act
|
||||
val actualMaterialTypes = materialTypeLogic.getAll(true)
|
||||
|
||||
// Assert
|
||||
assertEquals(expectedMaterialTypes, actualMaterialTypes)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getByName_normalBehavior_returnsMaterialType() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.getByName(any()) } returns materialType
|
||||
|
||||
// Act
|
||||
val actualMaterialType = materialTypeLogic.getByName(materialType.name)
|
||||
|
||||
// Assert
|
||||
assertEquals(materialType, actualMaterialType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getByName_nameNotFound_returnsNull() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.getByName(any()) } returns null
|
||||
|
||||
// Act
|
||||
val actualMaterialType = materialTypeLogic.getByName(materialType.name)
|
||||
|
||||
// Assert
|
||||
assertNull(actualMaterialType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateNonSystemType_normalBehavior_callsUpdate() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.existsById(any(), any()) } returns false
|
||||
every { materialTypeLogic.update(any()) } returnsArgument 0
|
||||
|
||||
// Act
|
||||
materialTypeLogic.updateNonSystemType(materialType)
|
||||
|
||||
// Assert
|
||||
verify {
|
||||
materialTypeLogic.update(materialType)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateNonSystemType_isSystemType_throwsCannotUpdateException() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.existsById(any(), any()) } returns true
|
||||
every { materialTypeLogic.update(any()) } returnsArgument 0
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<CannotUpdateException> { materialTypeLogic.updateNonSystemType(materialType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun save_nameExists_throwsAlreadyExistsException() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.existsByName(any(), any()) } returns true
|
||||
every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns false
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<AlreadyExistsException> { materialTypeLogic.save(materialType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun save_prefixExists_throwsAlreadyExistsException() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.existsByName(any(), any()) } returns false
|
||||
every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns true
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<AlreadyExistsException> { materialTypeLogic.save(materialType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun update_nameExists_throwsAlreadyExistsException() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.existsByName(any(), any()) } returns true
|
||||
every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns false
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<AlreadyExistsException> { materialTypeLogic.update(materialType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun update_prefixExists_throwsAlreadyExistsException() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.existsByName(any(), any()) } returns false
|
||||
every { materialTypeServiceMock.existsByPrefix(any(), any()) } returns true
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<AlreadyExistsException> { materialTypeLogic.update(materialType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteById_usedByMaterial_throwsCannotDeleteException() {
|
||||
// Arrange
|
||||
every { materialTypeServiceMock.isUsedByMaterial(any()) } returns true
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
assertThrows<CannotDeleteException> { materialTypeLogic.deleteById(materialType.id) }
|
||||
}
|
||||
}
|
|
@ -1,182 +1,182 @@
|
|||
package dev.fyloz.colorrecipesexplorer.logic
|
||||
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class MaterialTypeLogicTest :
|
||||
AbstractExternalNamedModelServiceTest<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeLogic, MaterialTypeRepository>() {
|
||||
override val repository: MaterialTypeRepository = mock()
|
||||
private val materialService: MaterialLogic = mock()
|
||||
override val logic: MaterialTypeLogic = spy(DefaultMaterialTypeLogic(repository))
|
||||
override val entity: MaterialType = materialType(id = 0L, name = "material type", prefix = "MAT")
|
||||
override val anotherEntity: MaterialType = materialType(id = 1L, name = "another material type", prefix = "AMT")
|
||||
override val entityWithEntityName: MaterialType = materialType(2L, name = entity.name, prefix = "EEN")
|
||||
private val systemType = materialType(id = 3L, name = "systype", prefix = "SYS", systemType = true)
|
||||
private val anotherSystemType = materialType(id = 4L, name = "another systype", prefix = "ASY", systemType = true)
|
||||
override val entitySaveDto: MaterialTypeSaveDto = spy(materialTypeSaveDto(name = "material type", prefix = "MAT"))
|
||||
override val entityUpdateDto: MaterialTypeUpdateDto =
|
||||
spy(materialTypeUpdateDto(id = 0L, name = "material type", prefix = "MAT"))
|
||||
|
||||
@AfterEach
|
||||
override fun afterEach() {
|
||||
reset(materialService)
|
||||
super.afterEach()
|
||||
}
|
||||
|
||||
// existsByPrefix()
|
||||
|
||||
@Test
|
||||
fun `existsByPrefix() returns true when a material type with the given prefix exists`() {
|
||||
whenever(repository.existsByPrefix(entity.prefix)).doReturn(true)
|
||||
|
||||
val found = logic.existsByPrefix(entity.prefix)
|
||||
|
||||
assertTrue(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByPrefix() returns false when no material type with the given prefix exists`() {
|
||||
whenever(repository.existsByPrefix(entity.prefix)).doReturn(false)
|
||||
|
||||
val found = logic.existsByPrefix(entity.prefix)
|
||||
|
||||
assertFalse(found)
|
||||
}
|
||||
|
||||
// getAllSystemTypes()
|
||||
|
||||
@Test
|
||||
fun `getAllSystemTypes() returns all system types`() {
|
||||
whenever(repository.findAllBySystemTypeIs(true)).doReturn(listOf(systemType, anotherSystemType))
|
||||
|
||||
val found = logic.getAllSystemTypes()
|
||||
|
||||
assertTrue(found.contains(systemType))
|
||||
assertTrue(found.contains(anotherSystemType))
|
||||
}
|
||||
|
||||
// getAllNonSystemTypes()
|
||||
|
||||
@Test
|
||||
fun `getAllNonSystemTypes() returns all non system types`() {
|
||||
whenever(repository.findAllBySystemTypeIs(false)).doReturn(listOf(entity, anotherEntity))
|
||||
|
||||
val found = logic.getAllNonSystemType()
|
||||
|
||||
assertTrue(found.contains(entity))
|
||||
assertTrue(found.contains(anotherEntity))
|
||||
}
|
||||
|
||||
// save()
|
||||
|
||||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, logic)
|
||||
}
|
||||
|
||||
// saveMaterialType()
|
||||
|
||||
@Test
|
||||
fun `saveMaterialType() throws AlreadyExistsException when a material type with the given prefix already exists`() {
|
||||
doReturn(true).whenever(logic).existsByPrefix(entity.prefix)
|
||||
|
||||
assertThrows<AlreadyExistsException> { logic.save(entity) }
|
||||
.assertErrorCode("prefix")
|
||||
}
|
||||
|
||||
// update()
|
||||
|
||||
@Test
|
||||
override fun `update(dto) calls and returns update() with the created entity`() =
|
||||
withBaseUpdateDtoTest(entity, entityUpdateDto, logic, { any() })
|
||||
|
||||
override fun `update() saves in the repository and returns the updated value`() {
|
||||
whenever(repository.save(entity)).doReturn(entity)
|
||||
whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
whenever(repository.findByPrefix(entity.prefix)).doReturn(null)
|
||||
doReturn(true).whenever(logic).existsById(entity.id!!)
|
||||
doReturn(entity).whenever(logic).getById(entity.id!!)
|
||||
|
||||
val found = logic.update(entity)
|
||||
|
||||
verify(repository).save(entity)
|
||||
assertEquals(entity, found)
|
||||
}
|
||||
|
||||
override fun `update() throws NotFoundException when no entity with the given id exists in the repository`() {
|
||||
whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
whenever(repository.findByPrefix(entity.prefix)).doReturn(null)
|
||||
doReturn(false).whenever(logic).existsById(entity.id!!)
|
||||
doReturn(null).whenever(logic).getById(entity.id!!)
|
||||
|
||||
assertThrows<NotFoundException> { logic.update(entity) }
|
||||
.assertErrorCode()
|
||||
}
|
||||
|
||||
override fun `update() throws AlreadyExistsException when an entity with the updated name exists`() {
|
||||
whenever(repository.findByName(entity.name)).doReturn(entityWithEntityName)
|
||||
whenever(repository.findByPrefix(entity.prefix)).doReturn(null)
|
||||
doReturn(true).whenever(logic).existsById(entity.id!!)
|
||||
doReturn(entity).whenever(logic).getById(entity.id!!)
|
||||
|
||||
assertThrows<AlreadyExistsException> { logic.update(entity) }
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update() throws AlreadyExistsException when an entity with the updated prefix exists`() {
|
||||
val anotherMaterialType = materialType(prefix = entity.prefix)
|
||||
whenever(repository.findByPrefix(entity.prefix)).doReturn(anotherMaterialType)
|
||||
doReturn(entity).whenever(logic).getById(entity.id!!)
|
||||
|
||||
assertThrows<AlreadyExistsException> { logic.update(entity) }
|
||||
.assertErrorCode("prefix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update() throws CannotUpdateException when updating a system material type`() {
|
||||
whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true)
|
||||
|
||||
assertThrows<CannotUpdateException> { logic.update(systemType) }
|
||||
}
|
||||
|
||||
// delete()
|
||||
|
||||
@Test
|
||||
fun `delete() throws CannotDeleteException when deleting a system material type`() {
|
||||
whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true)
|
||||
|
||||
assertThrows<CannotDeleteException> { logic.delete(systemType) }
|
||||
}
|
||||
|
||||
override fun `delete() deletes in the repository`() {
|
||||
whenCanBeDeleted {
|
||||
super.`delete() deletes in the repository`()
|
||||
}
|
||||
}
|
||||
|
||||
override fun `deleteById() deletes the entity with the given id in the repository`() {
|
||||
whenCanBeDeleted {
|
||||
super.`deleteById() deletes the entity with the given id in the repository`()
|
||||
}
|
||||
}
|
||||
|
||||
private fun whenCanBeDeleted(id: Long = any(), test: () -> Unit) {
|
||||
whenever(repository.canBeDeleted(id)).doReturn(true)
|
||||
|
||||
test()
|
||||
}
|
||||
}
|
||||
//package dev.fyloz.colorrecipesexplorer.logic
|
||||
//
|
||||
//import com.nhaarman.mockitokotlin2.*
|
||||
//import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
//import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||
//import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
||||
//import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
//import dev.fyloz.colorrecipesexplorer.model.*
|
||||
//import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository
|
||||
//import org.junit.jupiter.api.AfterEach
|
||||
//import org.junit.jupiter.api.Test
|
||||
//import org.junit.jupiter.api.TestInstance
|
||||
//import org.junit.jupiter.api.assertThrows
|
||||
//import kotlin.test.assertEquals
|
||||
//import kotlin.test.assertFalse
|
||||
//import kotlin.test.assertTrue
|
||||
//
|
||||
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
//class MaterialTypeLogicTest :
|
||||
// AbstractExternalNamedModelServiceTest<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeLogic, MaterialTypeRepository>() {
|
||||
// override val repository: MaterialTypeRepository = mock()
|
||||
// private val materialService: MaterialLogic = mock()
|
||||
// override val logic: MaterialTypeLogic = spy(DefaultMaterialTypeLogic(repository))
|
||||
// override val entity: MaterialType = materialType(id = 0L, name = "material type", prefix = "MAT")
|
||||
// override val anotherEntity: MaterialType = materialType(id = 1L, name = "another material type", prefix = "AMT")
|
||||
// override val entityWithEntityName: MaterialType = materialType(2L, name = entity.name, prefix = "EEN")
|
||||
// private val systemType = materialType(id = 3L, name = "systype", prefix = "SYS", systemType = true)
|
||||
// private val anotherSystemType = materialType(id = 4L, name = "another systype", prefix = "ASY", systemType = true)
|
||||
// override val entitySaveDto: MaterialTypeSaveDto = spy(materialTypeSaveDto(name = "material type", prefix = "MAT"))
|
||||
// override val entityUpdateDto: MaterialTypeUpdateDto =
|
||||
// spy(materialTypeUpdateDto(id = 0L, name = "material type", prefix = "MAT"))
|
||||
//
|
||||
// @AfterEach
|
||||
// override fun afterEach() {
|
||||
// reset(materialService)
|
||||
// super.afterEach()
|
||||
// }
|
||||
//
|
||||
// // existsByPrefix()
|
||||
//
|
||||
// @Test
|
||||
// fun `existsByPrefix() returns true when a material type with the given prefix exists`() {
|
||||
// whenever(repository.existsByPrefix(entity.prefix)).doReturn(true)
|
||||
//
|
||||
// val found = logic.existsByPrefix(entity.prefix)
|
||||
//
|
||||
// assertTrue(found)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `existsByPrefix() returns false when no material type with the given prefix exists`() {
|
||||
// whenever(repository.existsByPrefix(entity.prefix)).doReturn(false)
|
||||
//
|
||||
// val found = logic.existsByPrefix(entity.prefix)
|
||||
//
|
||||
// assertFalse(found)
|
||||
// }
|
||||
//
|
||||
// // getAllSystemTypes()
|
||||
//
|
||||
// @Test
|
||||
// fun `getAllSystemTypes() returns all system types`() {
|
||||
// whenever(repository.findAllBySystemTypeIs(true)).doReturn(listOf(systemType, anotherSystemType))
|
||||
//
|
||||
// val found = logic.getAllSystemTypes()
|
||||
//
|
||||
// assertTrue(found.contains(systemType))
|
||||
// assertTrue(found.contains(anotherSystemType))
|
||||
// }
|
||||
//
|
||||
// // getAllNonSystemTypes()
|
||||
//
|
||||
// @Test
|
||||
// fun `getAllNonSystemTypes() returns all non system types`() {
|
||||
// whenever(repository.findAllBySystemTypeIs(false)).doReturn(listOf(entity, anotherEntity))
|
||||
//
|
||||
// val found = logic.getAllNonSystemType()
|
||||
//
|
||||
// assertTrue(found.contains(entity))
|
||||
// assertTrue(found.contains(anotherEntity))
|
||||
// }
|
||||
//
|
||||
// // save()
|
||||
//
|
||||
// @Test
|
||||
// override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
// withBaseSaveDtoTest(entity, entitySaveDto, logic)
|
||||
// }
|
||||
//
|
||||
// // saveMaterialType()
|
||||
//
|
||||
// @Test
|
||||
// fun `saveMaterialType() throws AlreadyExistsException when a material type with the given prefix already exists`() {
|
||||
// doReturn(true).whenever(logic).existsByPrefix(entity.prefix)
|
||||
//
|
||||
// assertThrows<AlreadyExistsException> { logic.save(entity) }
|
||||
// .assertErrorCode("prefix")
|
||||
// }
|
||||
//
|
||||
// // update()
|
||||
//
|
||||
// @Test
|
||||
// override fun `update(dto) calls and returns update() with the created entity`() =
|
||||
// withBaseUpdateDtoTest(entity, entityUpdateDto, logic, { any() })
|
||||
//
|
||||
// override fun `update() saves in the repository and returns the updated value`() {
|
||||
// whenever(repository.save(entity)).doReturn(entity)
|
||||
// whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
// whenever(repository.findByPrefix(entity.prefix)).doReturn(null)
|
||||
// doReturn(true).whenever(logic).existsById(entity.id!!)
|
||||
// doReturn(entity).whenever(logic).getById(entity.id!!)
|
||||
//
|
||||
// val found = logic.update(entity)
|
||||
//
|
||||
// verify(repository).save(entity)
|
||||
// assertEquals(entity, found)
|
||||
// }
|
||||
//
|
||||
// override fun `update() throws NotFoundException when no entity with the given id exists in the repository`() {
|
||||
// whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
// whenever(repository.findByPrefix(entity.prefix)).doReturn(null)
|
||||
// doReturn(false).whenever(logic).existsById(entity.id!!)
|
||||
// doReturn(null).whenever(logic).getById(entity.id!!)
|
||||
//
|
||||
// assertThrows<NotFoundException> { logic.update(entity) }
|
||||
// .assertErrorCode()
|
||||
// }
|
||||
//
|
||||
// override fun `update() throws AlreadyExistsException when an entity with the updated name exists`() {
|
||||
// whenever(repository.findByName(entity.name)).doReturn(entityWithEntityName)
|
||||
// whenever(repository.findByPrefix(entity.prefix)).doReturn(null)
|
||||
// doReturn(true).whenever(logic).existsById(entity.id!!)
|
||||
// doReturn(entity).whenever(logic).getById(entity.id!!)
|
||||
//
|
||||
// assertThrows<AlreadyExistsException> { logic.update(entity) }
|
||||
// .assertErrorCode("name")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `update() throws AlreadyExistsException when an entity with the updated prefix exists`() {
|
||||
// val anotherMaterialType = materialType(prefix = entity.prefix)
|
||||
// whenever(repository.findByPrefix(entity.prefix)).doReturn(anotherMaterialType)
|
||||
// doReturn(entity).whenever(logic).getById(entity.id!!)
|
||||
//
|
||||
// assertThrows<AlreadyExistsException> { logic.update(entity) }
|
||||
// .assertErrorCode("prefix")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `update() throws CannotUpdateException when updating a system material type`() {
|
||||
// whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true)
|
||||
//
|
||||
// assertThrows<CannotUpdateException> { logic.update(systemType) }
|
||||
// }
|
||||
//
|
||||
// // delete()
|
||||
//
|
||||
// @Test
|
||||
// fun `delete() throws CannotDeleteException when deleting a system material type`() {
|
||||
// whenever(repository.existsByIdAndSystemTypeIsTrue(systemType.id!!)).doReturn(true)
|
||||
//
|
||||
// assertThrows<CannotDeleteException> { logic.delete(systemType) }
|
||||
// }
|
||||
//
|
||||
// override fun `delete() deletes in the repository`() {
|
||||
// whenCanBeDeleted {
|
||||
// super.`delete() deletes in the repository`()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun `deleteById() deletes the entity with the given id in the repository`() {
|
||||
// whenCanBeDeleted {
|
||||
// super.`deleteById() deletes the entity with the given id in the repository`()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun whenCanBeDeleted(id: Long = any(), test: () -> Unit) {
|
||||
// whenever(repository.canBeDeleted(id)).doReturn(true)
|
||||
//
|
||||
// test()
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -62,7 +62,7 @@ class MixLogicTest : AbstractExternalModelServiceTest<Mix, MixSaveDto, MixUpdate
|
|||
val mixMaterials = setOf(mixMaterial(material = material(id = 1L), quantity = 1000f))
|
||||
|
||||
whenever(recipeService.getById(recipe.id!!)).doReturn(recipe)
|
||||
whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialType)
|
||||
whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialTypeDto(materialType))
|
||||
whenever(mixMaterialService.create(entitySaveDto.mixMaterials!!)).doReturn(mixMaterials)
|
||||
whenever(
|
||||
mixTypeService.getOrCreateForNameAndMaterialType(
|
||||
|
@ -98,7 +98,7 @@ class MixLogicTest : AbstractExternalModelServiceTest<Mix, MixSaveDto, MixUpdate
|
|||
doAnswer { it.arguments[0] }.whenever(logic).update(any<Mix>())
|
||||
|
||||
if (mixUpdateDto.materialTypeId != null) {
|
||||
whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialType)
|
||||
whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialTypeDto(materialType))
|
||||
}
|
||||
|
||||
op()
|
||||
|
|
Loading…
Reference in New Issue