From 0321dd45f6a9c5238cb98f75e77b0594fe8840d7 Mon Sep 17 00:00:00 2001 From: FyloZ Date: Sat, 10 Apr 2021 21:03:41 -0400 Subject: [PATCH 1/4] =?UTF-8?q?R=C3=A9-impl=C3=A9mentation=20des=20erreurs?= =?UTF-8?q?=20pour=20suivre=20un=20standard.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/WebSecurityConfig.kt | 4 +- .../exception/RestException.kt | 130 ++++--- .../model/AccountModel.kt | 331 ------------------ .../colorrecipesexplorer/model/Company.kt | 42 +++ .../colorrecipesexplorer/model/Employee.kt | 191 ++++++++++ .../model/EmployeeGroup.kt | 129 +++++++ .../model/EmployeePermission.kt | 93 +++++ .../colorrecipesexplorer/model/Material.kt | 42 +++ .../model/MaterialType.kt | 51 +++ .../fyloz/colorrecipesexplorer/model/Mix.kt | 22 ++ .../colorrecipesexplorer/model/MixMaterial.kt | 13 + .../colorrecipesexplorer/model/MixType.kt | 58 ++- .../colorrecipesexplorer/model/Recipe.kt | 27 ++ .../colorrecipesexplorer/model/RecipeStep.kt | 13 + .../service/AccountService.kt | 38 +- .../service/CompanyService.kt | 16 +- .../service/InventoryService.kt | 43 ++- .../service/MaterialService.kt | 11 +- .../service/MaterialTypeService.kt | 32 +- .../service/MixMaterialService.kt | 7 +- .../service/MixService.kt | 9 +- .../service/MixTypeService.kt | 19 +- .../service/RecipeService.kt | 5 +- .../service/RecipeStepService.kt | 5 +- .../colorrecipesexplorer/service/Service.kt | 21 +- .../service/files/SimdutService.kt | 13 +- .../service/AbstractServiceTest.kt | 18 +- .../service/AccountsServiceTest.kt | 16 +- .../service/InventoryServiceTest.kt | 3 +- .../service/MaterialServiceTest.kt | 4 +- .../service/MaterialTypeServiceTest.kt | 10 +- .../service/MixTypeServiceTest.kt | 6 +- .../service/RecipeServiceTest.kt | 4 +- 33 files changed, 898 insertions(+), 528 deletions(-) delete mode 100644 src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/AccountModel.kt create mode 100644 src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt create mode 100644 src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt create mode 100644 src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt index 389bd5a..a780be3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt @@ -1,7 +1,7 @@ package dev.fyloz.colorrecipesexplorer.config import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.Employee import dev.fyloz.colorrecipesexplorer.model.EmployeeLoginRequest import dev.fyloz.colorrecipesexplorer.model.EmployeePermission @@ -272,7 +272,7 @@ class JwtAuthorizationFilter( private fun getAuthenticationToken(employeeId: String): UsernamePasswordAuthenticationToken? = try { val employeeDetails = userDetailsService.loadUserByEmployeeId(employeeId.toLong(), false) UsernamePasswordAuthenticationToken(employeeDetails.username, null, employeeDetails.authorities) - } catch (_: EntityNotFoundException) { + } catch (_: NotFoundException) { null } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt index d64c824..03067f2 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt @@ -1,9 +1,7 @@ package dev.fyloz.colorrecipesexplorer.exception -import com.fasterxml.jackson.annotation.JsonProperty import dev.fyloz.colorrecipesexplorer.model.Material import dev.fyloz.colorrecipesexplorer.model.MaterialQuantityDto -import org.springframework.context.annotation.Profile import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -11,84 +9,84 @@ import org.springframework.validation.FieldError import org.springframework.web.bind.MethodArgumentNotValidException import org.springframework.web.bind.annotation.ControllerAdvice import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.context.request.ServletWebRequest import org.springframework.web.context.request.WebRequest import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler -abstract class RestException(val exceptionMessage: String, val httpStatus: HttpStatus) : - RuntimeException(exceptionMessage) { - abstract fun buildBody(): RestExceptionBody +abstract class RestException( + val errorCode: String, + val title: String, + val status: HttpStatus, + val details: String, + val extensions: Map = mapOf() +) : RuntimeException(details) { + fun buildExceptionBody() = mapOf( + "type" to errorCode, + "title" to title, + "status" to status.value(), + "detail" to details, - @Suppress("unused") - open inner class RestExceptionBody( - val status: Int = httpStatus.value(), - @JsonProperty("message") val message: String = exceptionMessage + *extensions.map { it.key to it.value }.toTypedArray() ) } -class EntityAlreadyExistsException(val value: Any) : - RestException("An entity with the given identifier already exists", HttpStatus.CONFLICT) { - @Suppress("unused") - override fun buildBody(): RestExceptionBody = object : RestExceptionBody() { - val id = value - } -} +class NotFoundException( + errorCode: String, + title: String, + details: String, + identifierValue: Any, + identifierName: String = "id" +) : RestException( + errorCode = "notfound-$errorCode-$identifierName", + title = title, + status = HttpStatus.NOT_FOUND, + details = details, + extensions = mapOf( + identifierName to identifierValue + ) +) -class EntityNotFoundException(val value: Any) : - RestException("An entity could not be found with the given identifier", HttpStatus.NOT_FOUND) { - @Suppress("unused") - override fun buildBody(): RestExceptionBody = object : RestExceptionBody() { - val id = value - } -} +class AlreadyExistsException( + errorCode: String, + title: String, + details: String, + identifierValue: Any, + identifierName: String = "id" +) : RestException( + errorCode = "exists-$errorCode-$identifierName", + title = title, + status = HttpStatus.CONFLICT, + details = details, + extensions = mapOf( + identifierName to identifierValue + ) +) -class CannotDeleteEntityException(val value: Long) : - RestException( - "The entity with the given identifier could not be deleted because it is required by other entities", - HttpStatus.CONFLICT - ) { - @Suppress("unused") - override fun buildBody(): RestExceptionBody = object : RestExceptionBody() { - val id = value - } -} - -class SimdutWriteException(val material: Material) : - RestException( - "Could not write the SIMDUT file to disk", - HttpStatus.INTERNAL_SERVER_ERROR - ) { - @Suppress("unused") - override fun buildBody(): RestExceptionBody = RestExceptionBody() -} - -class LowQuantityException(val materialQuantity: MaterialQuantityDto) : - RestException( - "There is not enough of the given material in the inventory", - HttpStatus.CONFLICT - ) { - @Suppress("unused") - override fun buildBody(): RestExceptionBody = object : RestExceptionBody() { - val material = materialQuantity.material - val quantity = materialQuantity.quantity - } -} - -class LowQuantitiesException(val materialQuantities: Collection) : - RestException( - "There is not enough of one or more given materials in the inventory", - HttpStatus.CONFLICT - ) { - @Suppress - override fun buildBody(): RestExceptionBody = object : RestExceptionBody() { - val lowQuantities = materialQuantities - } -} +class CannotDeleteException( + errorCode: String, + title: String, + details: String +) : RestException( + errorCode = "cannotdelete-$errorCode", + title = title, + status = HttpStatus.CONFLICT, + details = details +) @ControllerAdvice class RestResponseEntityExceptionHandler : ResponseEntityExceptionHandler() { @ExceptionHandler(RestException::class) fun handleRestExceptions(exception: RestException, request: WebRequest): ResponseEntity { - return handleExceptionInternal(exception, exception.buildBody(), HttpHeaders(), exception.httpStatus, request) + val finalBody = exception.buildExceptionBody().toMutableMap() + finalBody["instance"] = (request as ServletWebRequest).request.requestURI + + return handleExceptionInternal( + exception, + finalBody, + HttpHeaders(), + exception.status, + request + ) } override fun handleMethodArgumentNotValid( diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/AccountModel.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/AccountModel.kt deleted file mode 100644 index 1f680f1..0000000 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/AccountModel.kt +++ /dev/null @@ -1,331 +0,0 @@ -package dev.fyloz.colorrecipesexplorer.model - -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.annotation.JsonProperty -import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank -import org.hibernate.annotations.Fetch -import org.hibernate.annotations.FetchMode -import org.springframework.security.core.GrantedAuthority -import org.springframework.security.core.authority.SimpleGrantedAuthority -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder -import org.springframework.security.crypto.password.PasswordEncoder -import java.time.LocalDateTime -import javax.persistence.* -import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull -import javax.validation.constraints.Size - - -private const val EMPLOYEE_ID_NULL_MESSAGE = "Un numéro d'employé est requis" -private const val EMPLOYEE_LAST_NAME_EMPTY_MESSAGE = "Un nom est requis" -private const val EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE = "Un prénom est requis" -private const val EMPLOYEE_PASSWORD_EMPTY_MESSAGE = "Un mot de passe est requis" -private const val EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE = "Le mot de passe doit contenir au moins 8 caractères" - -@Entity -@Table(name = "employee") -data class Employee( - @Id - override val id: Long, - - @Column(name = "first_name") - val firstName: String = "", - - @Column(name = "last_name") - val lastName: String = "", - - @JsonIgnore - val password: String = "", - - @JsonIgnore - @Column(name = "default_group_user") - val isDefaultGroupUser: Boolean = false, - - @JsonIgnore - @Column(name = "system_user") - val isSystemUser: Boolean = false, - - @ManyToOne - @JoinColumn(name = "group_id") - @Fetch(FetchMode.SELECT) - var group: EmployeeGroup? = null, - - @Enumerated(EnumType.STRING) - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "employee_permission", joinColumns = [JoinColumn(name = "employee_id")]) - @Column(name = "permission") - @Fetch(FetchMode.SUBSELECT) - @get:JsonProperty("explicitPermissions") - val permissions: MutableSet = mutableSetOf(), - - @Column(name = "last_login_time") - var lastLoginTime: LocalDateTime? = null -) : Model { - @get:JsonProperty("permissions") - val flatPermissions: Set - get() = permissions - .flatMap { it.flat() } - .filter { !it.deprecated } - .toMutableSet() - .apply { - if (group != null) this.addAll(group!!.flatPermissions) - } - - @get:JsonIgnore - val authorities: Set - get() = flatPermissions.map { it.toAuthority() }.toMutableSet() -} - -/** DTO for creating employees. Allows a [password] a [groupId]. */ -open class EmployeeSaveDto( - @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) - val id: Long, - - @field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) - val firstName: String, - - @field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) - val lastName: String, - - @field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE) - @field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE) - val password: String, - - val groupId: Long?, - - @Enumerated(EnumType.STRING) - val permissions: MutableSet = mutableSetOf() -) : EntityDto - -open class EmployeeUpdateDto( - @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) - val id: Long, - - @field:NullOrNotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) - val firstName: String?, - - @field:NullOrNotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) - val lastName: String?, - - val groupId: Long?, - - @Enumerated(EnumType.STRING) - val permissions: Set? -) : EntityDto - - -private const val GROUP_ID_NULL_MESSAGE = "Un identifiant est requis" -private const val GROUP_NAME_NULL_MESSAGE = "Un nom est requis" -private const val GROUP_PERMISSIONS_EMPTY_MESSAGE = "Au moins une permission est requise" - -@Entity -@Table(name = "employee_group") -data class EmployeeGroup( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override var id: Long? = null, - - @Column(unique = true) - override val name: String = "", - - @Enumerated(EnumType.STRING) - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "group_permission", joinColumns = [JoinColumn(name = "group_id")]) - @Column(name = "permission") - @Fetch(FetchMode.SUBSELECT) - @get:JsonProperty("explicitPermissions") - val permissions: MutableSet = mutableSetOf(), -) : NamedModel { - @get:JsonProperty("permissions") - val flatPermissions: Set - get() = this.permissions - .flatMap { it.flat() } - .filter { !it.deprecated } - .toSet() -} - -open class EmployeeGroupSaveDto( - @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) - @field:Size(min = 3) - val name: String, - - @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) - val permissions: MutableSet -) : EntityDto { - override fun toEntity(): EmployeeGroup = - EmployeeGroup(null, name, permissions) -} - -open class EmployeeGroupUpdateDto( - @field:NotNull(message = GROUP_ID_NULL_MESSAGE) - val id: Long, - - @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) - @field:Size(min = 3) - val name: String, - - @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) - val permissions: MutableSet -) : EntityDto { - override fun toEntity(): EmployeeGroup = - EmployeeGroup(id, name, permissions) -} - - -data class EmployeeLoginRequest(val id: Long, val password: String) - - -enum class EmployeePermission( - val impliedPermissions: List = listOf(), - val deprecated: Boolean = false -) { - VIEW_RECIPES, - VIEW_CATALOG, - VIEW_USERS, - - PRINT_MIXES(listOf(VIEW_RECIPES)), - - EDIT_RECIPES_PUBLIC_DATA(listOf(VIEW_RECIPES)), - EDIT_RECIPES(listOf(EDIT_RECIPES_PUBLIC_DATA)), - EDIT_MATERIALS(listOf(VIEW_CATALOG)), - EDIT_MATERIAL_TYPES(listOf(VIEW_CATALOG)), - EDIT_COMPANIES(listOf(VIEW_CATALOG)), - EDIT_USERS(listOf(VIEW_USERS)), - EDIT_CATALOG(listOf(EDIT_MATERIALS, EDIT_MATERIAL_TYPES, EDIT_COMPANIES)), - - ADD_TO_INVENTORY(listOf(VIEW_CATALOG)), - DEDUCT_FROM_INVENTORY(listOf(VIEW_RECIPES)), - - REMOVE_RECIPES(listOf(EDIT_RECIPES)), - REMOVE_MATERIALS(listOf(EDIT_MATERIALS)), - REMOVE_MATERIAL_TYPES(listOf(EDIT_MATERIAL_TYPES)), - REMOVE_COMPANIES(listOf(EDIT_COMPANIES)), - REMOVE_USERS(listOf(EDIT_USERS)), - REMOVE_CATALOG(listOf(REMOVE_MATERIALS, REMOVE_MATERIAL_TYPES, REMOVE_COMPANIES)), - - ADMIN( - listOf( - EDIT_CATALOG, - - REMOVE_RECIPES, - REMOVE_USERS, - REMOVE_CATALOG, - - PRINT_MIXES, - ADD_TO_INVENTORY, - DEDUCT_FROM_INVENTORY - ) - ), - - // deprecated permissions - VIEW_RECIPE(listOf(VIEW_RECIPES), true), - VIEW_MATERIAL(listOf(VIEW_CATALOG), true), - VIEW_MATERIAL_TYPE(listOf(VIEW_CATALOG), true), - VIEW_COMPANY(listOf(VIEW_CATALOG), true), - VIEW(listOf(VIEW_RECIPES, VIEW_CATALOG), true), - VIEW_EMPLOYEE(listOf(VIEW_USERS), true), - VIEW_EMPLOYEE_GROUP(listOf(VIEW_USERS), true), - - EDIT_RECIPE(listOf(EDIT_RECIPES), true), - EDIT_MATERIAL(listOf(EDIT_MATERIALS), true), - EDIT_MATERIAL_TYPE(listOf(EDIT_MATERIAL_TYPES), true), - EDIT_COMPANY(listOf(EDIT_COMPANIES), true), - EDIT(listOf(EDIT_RECIPES, EDIT_CATALOG), true), - EDIT_EMPLOYEE(listOf(EDIT_USERS), true), - EDIT_EMPLOYEE_PASSWORD(listOf(EDIT_USERS), true), - EDIT_EMPLOYEE_GROUP(listOf(EDIT_USERS), true), - - REMOVE_RECIPE(listOf(REMOVE_RECIPES), true), - REMOVE_MATERIAL(listOf(REMOVE_MATERIALS), true), - REMOVE_MATERIAL_TYPE(listOf(REMOVE_MATERIAL_TYPES), true), - REMOVE_COMPANY(listOf(REMOVE_COMPANIES), true), - REMOVE(listOf(REMOVE_RECIPES, REMOVE_CATALOG), true), - REMOVE_EMPLOYEE(listOf(REMOVE_USERS), true), - REMOVE_EMPLOYEE_GROUP(listOf(REMOVE_USERS), true), - - SET_BROWSER_DEFAULT_GROUP(listOf(VIEW_USERS), true), - ; - - operator fun contains(permission: EmployeePermission): Boolean { - return permission == this || impliedPermissions.any { permission in it } - } -} - -fun EmployeePermission.flat(): Iterable { - return mutableSetOf(this).apply { - impliedPermissions.forEach { - addAll(it.flat()) - } - } -} - -/** Converts the given [EmployeePermission] to a [GrantedAuthority]. */ -private fun EmployeePermission.toAuthority(): GrantedAuthority { - return SimpleGrantedAuthority(name) -} - - -// ==== DSL ==== -fun employee( - passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), - id: Long = 0L, - firstName: String = "firstName", - lastName: String = "lastName", - password: String = passwordEncoder.encode("password"), - isDefaultGroupUser: Boolean = false, - isSystemUser: Boolean = false, - group: EmployeeGroup? = null, - permissions: MutableSet = mutableSetOf(), - lastLoginTime: LocalDateTime? = null, - op: Employee.() -> Unit = {} -) = Employee( - id, - firstName, - lastName, - password, - isDefaultGroupUser, - isSystemUser, - group, - permissions, - lastLoginTime -).apply(op) - -fun employeeSaveDto( - passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), - id: Long = 0L, - firstName: String = "firstName", - lastName: String = "lastName", - password: String = passwordEncoder.encode("password"), - groupId: Long? = null, - permissions: MutableSet = mutableSetOf(), - op: EmployeeSaveDto.() -> Unit = {} -) = EmployeeSaveDto(id, firstName, lastName, password, groupId, permissions).apply(op) - -fun employeeUpdateDto( - id: Long = 0L, - firstName: String = "firstName", - lastName: String = "lastName", - groupId: Long? = null, - permissions: MutableSet = mutableSetOf(), - op: EmployeeUpdateDto.() -> Unit = {} -) = EmployeeUpdateDto(id, firstName, lastName, groupId, permissions).apply(op) - -fun employeeGroup( - id: Long? = null, - name: String = "name", - permissions: MutableSet = mutableSetOf(), - op: EmployeeGroup.() -> Unit = {} -) = EmployeeGroup(id, name, permissions).apply(op) - -fun employeeGroupSaveDto( - name: String = "name", - permissions: MutableSet = mutableSetOf(), - op: EmployeeGroupSaveDto.() -> Unit = {} -) = EmployeeGroupSaveDto(name, permissions).apply(op) - -fun employeeGroupUpdateDto( - id: Long = 0L, - name: String = "name", - permissions: MutableSet = mutableSetOf(), - op: EmployeeGroupUpdateDto.() -> Unit = {} -) = EmployeeGroupUpdateDto(id, name, permissions).apply(op) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt index 9362b8a..1d75903 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt @@ -1,5 +1,8 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank import javax.persistence.* import javax.validation.constraints.NotBlank @@ -55,3 +58,42 @@ fun companyUpdateDto( name: String? = "name", op: CompanyUpdateDto.() -> Unit = {} ) = CompanyUpdateDto(id, name).apply(op) + +// ==== Exceptions ==== +private const val COMPANY_NOT_FOUND_EXCEPTION_TITLE = "Company not found" +private const val COMPANY_ALREADY_EXISTS_EXCEPTION_TITLE = "Company already exists" +private const val COMPANY_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete company" +private const val COMPANY_EXCEPTION_ERROR_CODE = "company" + +fun companyIdNotFoundException(id: Long) = + NotFoundException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_NOT_FOUND_EXCEPTION_TITLE, + "A company with the id $id could not be found", + id + ) + +fun companyNameNotFoundException(name: String) = + NotFoundException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_NOT_FOUND_EXCEPTION_TITLE, + "A company with the name $name could not be found", + name, + "name" + ) + +fun companyNameAlreadyExistsException(name: String) = + AlreadyExistsException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_ALREADY_EXISTS_EXCEPTION_TITLE, + "A company with the name $name already exists", + name, + "name" + ) + +fun cannotDeleteCompany(company: Company) = + CannotDeleteException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the company ${company.name} because one or more recipes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt new file mode 100644 index 0000000..b65e73b --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt @@ -0,0 +1,191 @@ +package dev.fyloz.colorrecipesexplorer.model + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonProperty +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank +import org.hibernate.annotations.Fetch +import org.hibernate.annotations.FetchMode +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import java.time.LocalDateTime +import javax.persistence.* +import javax.validation.constraints.NotBlank +import javax.validation.constraints.NotNull +import javax.validation.constraints.Size + +private const val EMPLOYEE_ID_NULL_MESSAGE = "Un numéro d'employé est requis" +private const val EMPLOYEE_LAST_NAME_EMPTY_MESSAGE = "Un nom est requis" +private const val EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE = "Un prénom est requis" +private const val EMPLOYEE_PASSWORD_EMPTY_MESSAGE = "Un mot de passe est requis" +private const val EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE = "Le mot de passe doit contenir au moins 8 caractères" + +@Entity +@Table(name = "employee") +data class Employee( + @Id + override val id: Long, + + @Column(name = "first_name") + val firstName: String = "", + + @Column(name = "last_name") + val lastName: String = "", + + @JsonIgnore + val password: String = "", + + @JsonIgnore + @Column(name = "default_group_user") + val isDefaultGroupUser: Boolean = false, + + @JsonIgnore + @Column(name = "system_user") + val isSystemUser: Boolean = false, + + @ManyToOne + @JoinColumn(name = "group_id") + @Fetch(FetchMode.SELECT) + var group: EmployeeGroup? = null, + + @Enumerated(EnumType.STRING) + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "employee_permission", joinColumns = [JoinColumn(name = "employee_id")]) + @Column(name = "permission") + @Fetch(FetchMode.SUBSELECT) + @get:JsonProperty("explicitPermissions") + val permissions: MutableSet = mutableSetOf(), + + @Column(name = "last_login_time") + var lastLoginTime: LocalDateTime? = null +) : Model { + @get:JsonProperty("permissions") + val flatPermissions: Set + get() = permissions + .flatMap { it.flat() } + .filter { !it.deprecated } + .toMutableSet() + .apply { + if (group != null) this.addAll(group!!.flatPermissions) + } + + @get:JsonIgnore + val authorities: Set + get() = flatPermissions.map { it.toAuthority() }.toMutableSet() +} + +/** DTO for creating employees. Allows a [password] a [groupId]. */ +open class EmployeeSaveDto( + @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) + val id: Long, + + @field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) + val firstName: String, + + @field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) + val lastName: String, + + @field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE) + @field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE) + val password: String, + + val groupId: Long?, + + @Enumerated(EnumType.STRING) + val permissions: MutableSet = mutableSetOf() +) : EntityDto + +open class EmployeeUpdateDto( + @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) + val id: Long, + + @field:NullOrNotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) + val firstName: String?, + + @field:NullOrNotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) + val lastName: String?, + + val groupId: Long?, + + @Enumerated(EnumType.STRING) + val permissions: Set? +) : EntityDto + +data class EmployeeLoginRequest(val id: Long, val password: String) + +// ==== DSL ==== +fun employee( + passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), + id: Long = 0L, + firstName: String = "firstName", + lastName: String = "lastName", + password: String = passwordEncoder.encode("password"), + isDefaultGroupUser: Boolean = false, + isSystemUser: Boolean = false, + group: EmployeeGroup? = null, + permissions: MutableSet = mutableSetOf(), + lastLoginTime: LocalDateTime? = null, + op: Employee.() -> Unit = {} +) = Employee( + id, + firstName, + lastName, + password, + isDefaultGroupUser, + isSystemUser, + group, + permissions, + lastLoginTime +).apply(op) + +fun employeeSaveDto( + passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), + id: Long = 0L, + firstName: String = "firstName", + lastName: String = "lastName", + password: String = passwordEncoder.encode("password"), + groupId: Long? = null, + permissions: MutableSet = mutableSetOf(), + op: EmployeeSaveDto.() -> Unit = {} +) = EmployeeSaveDto(id, firstName, lastName, password, groupId, permissions).apply(op) + +fun employeeUpdateDto( + id: Long = 0L, + firstName: String = "firstName", + lastName: String = "lastName", + groupId: Long? = null, + permissions: MutableSet = mutableSetOf(), + op: EmployeeUpdateDto.() -> Unit = {} +) = EmployeeUpdateDto(id, firstName, lastName, groupId, permissions).apply(op) + +// ==== Exceptions ==== +private const val EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE = "Employee not found" +private const val EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE = "Employee already exists" +private const val EMPLOYEE_EXCEPTION_ERROR_CODE = "employee" + +fun employeeIdNotFoundException(id: Long) = + NotFoundException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, + "An employee with the id $id could not be found", + id + ) + +fun employeeIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee with the id $id already exists", + id + ) + +fun employeeFullNameAlreadyExistsException(firstName: String, lastName: String) = + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee with the name '$firstName $lastName' already exists", + "$firstName $lastName", + "fullName" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt new file mode 100644 index 0000000..32e91bf --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt @@ -0,0 +1,129 @@ +package dev.fyloz.colorrecipesexplorer.model + +import com.fasterxml.jackson.annotation.JsonProperty +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +import dev.fyloz.colorrecipesexplorer.exception.RestException +import org.hibernate.annotations.Fetch +import org.hibernate.annotations.FetchMode +import org.springframework.http.HttpStatus +import javax.persistence.* +import javax.validation.constraints.NotBlank +import javax.validation.constraints.NotNull +import javax.validation.constraints.Size + +private const val GROUP_ID_NULL_MESSAGE = "Un identifiant est requis" +private const val GROUP_NAME_NULL_MESSAGE = "Un nom est requis" +private const val GROUP_PERMISSIONS_EMPTY_MESSAGE = "Au moins une permission est requise" + +@Entity +@Table(name = "employee_group") +data class EmployeeGroup( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override var id: Long? = null, + + @Column(unique = true) + override val name: String = "", + + @Enumerated(EnumType.STRING) + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "group_permission", joinColumns = [JoinColumn(name = "group_id")]) + @Column(name = "permission") + @Fetch(FetchMode.SUBSELECT) + @get:JsonProperty("explicitPermissions") + val permissions: MutableSet = mutableSetOf(), +) : NamedModel { + @get:JsonProperty("permissions") + val flatPermissions: Set + get() = this.permissions + .flatMap { it.flat() } + .filter { !it.deprecated } + .toSet() +} + +open class EmployeeGroupSaveDto( + @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) + @field:Size(min = 3) + val name: String, + + @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) + val permissions: MutableSet +) : EntityDto { + override fun toEntity(): EmployeeGroup = + EmployeeGroup(null, name, permissions) +} + +open class EmployeeGroupUpdateDto( + @field:NotNull(message = GROUP_ID_NULL_MESSAGE) + val id: Long, + + @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) + @field:Size(min = 3) + val name: String, + + @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) + val permissions: MutableSet +) : EntityDto { + override fun toEntity(): EmployeeGroup = + EmployeeGroup(id, name, permissions) +} + +fun employeeGroup( + id: Long? = null, + name: String = "name", + permissions: MutableSet = mutableSetOf(), + op: EmployeeGroup.() -> Unit = {} +) = EmployeeGroup(id, name, permissions).apply(op) + +fun employeeGroupSaveDto( + name: String = "name", + permissions: MutableSet = mutableSetOf(), + op: EmployeeGroupSaveDto.() -> Unit = {} +) = EmployeeGroupSaveDto(name, permissions).apply(op) + +fun employeeGroupUpdateDto( + id: Long = 0L, + name: String = "name", + permissions: MutableSet = mutableSetOf(), + op: EmployeeGroupUpdateDto.() -> Unit = {} +) = EmployeeGroupUpdateDto(id, name, permissions).apply(op) + +// ==== Exceptions ==== +private const val EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE = "Employee group not found" +private const val EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE = "Employee group already exists" +private const val EMPLOYEE_EXCEPTION_ERROR_CODE = "employeegroup" + +class NoDefaultGroupException : RestException( + "nodefaultgroup", + "No default group", + HttpStatus.NOT_FOUND, + "No default group cookie is defined in the current request" +) + +fun employeeGroupIdNotFoundException(id: Long) = + NotFoundException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, + "An employee group with the id $id could not be found", + id + ) + +fun employeeGroupNameNotFoundException(name: String) = + NotFoundException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, + "An employee group with the name $name could not be found", + name, + "name" + ) + +fun employeeGroupNameAlreadyExistsException(name: String) = + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee group with the name $name already exists", + name, + "name" + ) + diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt new file mode 100644 index 0000000..3bcfd42 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt @@ -0,0 +1,93 @@ +package dev.fyloz.colorrecipesexplorer.model + +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority + +enum class EmployeePermission( + val impliedPermissions: List = listOf(), + val deprecated: Boolean = false +) { + VIEW_RECIPES, + VIEW_CATALOG, + VIEW_USERS, + + PRINT_MIXES(listOf(VIEW_RECIPES)), + + EDIT_RECIPES_PUBLIC_DATA(listOf(VIEW_RECIPES)), + EDIT_RECIPES(listOf(EDIT_RECIPES_PUBLIC_DATA)), + EDIT_MATERIALS(listOf(VIEW_CATALOG)), + EDIT_MATERIAL_TYPES(listOf(VIEW_CATALOG)), + EDIT_COMPANIES(listOf(VIEW_CATALOG)), + EDIT_USERS(listOf(VIEW_USERS)), + EDIT_CATALOG(listOf(EDIT_MATERIALS, EDIT_MATERIAL_TYPES, EDIT_COMPANIES)), + + ADD_TO_INVENTORY(listOf(VIEW_CATALOG)), + DEDUCT_FROM_INVENTORY(listOf(VIEW_RECIPES)), + + REMOVE_RECIPES(listOf(EDIT_RECIPES)), + REMOVE_MATERIALS(listOf(EDIT_MATERIALS)), + REMOVE_MATERIAL_TYPES(listOf(EDIT_MATERIAL_TYPES)), + REMOVE_COMPANIES(listOf(EDIT_COMPANIES)), + REMOVE_USERS(listOf(EDIT_USERS)), + REMOVE_CATALOG(listOf(REMOVE_MATERIALS, REMOVE_MATERIAL_TYPES, REMOVE_COMPANIES)), + + ADMIN( + listOf( + EDIT_CATALOG, + + REMOVE_RECIPES, + REMOVE_USERS, + REMOVE_CATALOG, + + PRINT_MIXES, + ADD_TO_INVENTORY, + DEDUCT_FROM_INVENTORY + ) + ), + + // deprecated permissions + VIEW_RECIPE(listOf(VIEW_RECIPES), true), + VIEW_MATERIAL(listOf(VIEW_CATALOG), true), + VIEW_MATERIAL_TYPE(listOf(VIEW_CATALOG), true), + VIEW_COMPANY(listOf(VIEW_CATALOG), true), + VIEW(listOf(VIEW_RECIPES, VIEW_CATALOG), true), + VIEW_EMPLOYEE(listOf(VIEW_USERS), true), + VIEW_EMPLOYEE_GROUP(listOf(VIEW_USERS), true), + + EDIT_RECIPE(listOf(EDIT_RECIPES), true), + EDIT_MATERIAL(listOf(EDIT_MATERIALS), true), + EDIT_MATERIAL_TYPE(listOf(EDIT_MATERIAL_TYPES), true), + EDIT_COMPANY(listOf(EDIT_COMPANIES), true), + EDIT(listOf(EDIT_RECIPES, EDIT_CATALOG), true), + EDIT_EMPLOYEE(listOf(EDIT_USERS), true), + EDIT_EMPLOYEE_PASSWORD(listOf(EDIT_USERS), true), + EDIT_EMPLOYEE_GROUP(listOf(EDIT_USERS), true), + + REMOVE_RECIPE(listOf(REMOVE_RECIPES), true), + REMOVE_MATERIAL(listOf(REMOVE_MATERIALS), true), + REMOVE_MATERIAL_TYPE(listOf(REMOVE_MATERIAL_TYPES), true), + REMOVE_COMPANY(listOf(REMOVE_COMPANIES), true), + REMOVE(listOf(REMOVE_RECIPES, REMOVE_CATALOG), true), + REMOVE_EMPLOYEE(listOf(REMOVE_USERS), true), + REMOVE_EMPLOYEE_GROUP(listOf(REMOVE_USERS), true), + + SET_BROWSER_DEFAULT_GROUP(listOf(VIEW_USERS), true), + ; + + operator fun contains(permission: EmployeePermission): Boolean { + return permission == this || impliedPermissions.any { permission in it } + } +} + +fun EmployeePermission.flat(): Iterable { + return mutableSetOf(this).apply { + impliedPermissions.forEach { + addAll(it.flat()) + } + } +} + +/** Converts the given [EmployeePermission] to a [GrantedAuthority]. */ +fun EmployeePermission.toAuthority(): GrantedAuthority { + return SimpleGrantedAuthority(name) +} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt index 7ebe745..c28e1cd 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt @@ -1,5 +1,8 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize import org.springframework.web.multipart.MultipartFile @@ -119,3 +122,42 @@ fun materialQuantityDto( quantity: Float, op: MaterialQuantityDto.() -> Unit = {} ) = MaterialQuantityDto(materialId, quantity).apply(op) + +// ==== Exceptions ==== +private const val MATERIAL_NOT_FOUND_EXCEPTION_TITLE = "Material not found" +private const val MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE = "Material already exists" +private const val MATERIAL_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete material" +private const val MATERIAL_EXCEPTION_ERROR_CODE = "material" + +fun materialIdNotFoundException(id: Long) = + NotFoundException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_NOT_FOUND_EXCEPTION_TITLE, + "A material with the id $id could not be found", + id + ) + +fun materialNameNotFoundException(name: String) = + NotFoundException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_NOT_FOUND_EXCEPTION_TITLE, + "A material with the name $name could not be found", + name, + "name" + ) + +fun materialNameAlreadyExistsException(name: String) = + AlreadyExistsException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE, + "A material with the name $name already exists", + name, + "name" + ) + +fun cannotDeleteMaterialException(material: Material) = + CannotDeleteException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the material ${material.name} because one or more recipes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt index f9aa6d3..30beff6 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt @@ -1,5 +1,8 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize import org.hibernate.annotations.ColumnDefault @@ -101,3 +104,51 @@ fun materialTypeUpdateDto( 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_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 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 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" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt index f66daa1..6178a54 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt @@ -1,6 +1,8 @@ package dev.fyloz.colorrecipesexplorer.model import com.fasterxml.jackson.annotation.JsonIgnore +import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank import javax.persistence.* import javax.validation.constraints.Min @@ -121,3 +123,23 @@ fun mixLocationDto( location: String? = "location", op: MixLocationDto.() -> Unit = {} ) = MixLocationDto(mixId, location).apply(op) + +// ==== Exceptions ==== +private const val MIX_NOT_FOUND_EXCEPTION_TITLE = "Mix not found" +private const val MIX_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete mix" +private const val MIX_EXCEPTION_ERROR_CODE = "mix" + +fun mixIdNotFoundException(id: Long) = + NotFoundException( + MIX_EXCEPTION_ERROR_CODE, + MIX_NOT_FOUND_EXCEPTION_TITLE, + "A mix with the id $id could not be found", + id + ) + +fun cannotDeleteMixException(mix: Mix) = + CannotDeleteException( + MIX_EXCEPTION_ERROR_CODE, + MIX_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the mix ${mix.mixType.name} because one or more mixes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt index a0eb56f..90d9614 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import javax.persistence.* import javax.validation.constraints.Min import javax.validation.constraints.NotNull @@ -50,3 +51,15 @@ fun mixMaterialDto( position: Int = 0, op: MixMaterialDto.() -> Unit = {} ) = MixMaterialDto(materialId, quantity, position).apply(op) + +// ==== Exceptions ==== +private const val MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE = "Mix material not found" +private const val MIX_MATERIAL_EXCEPTION_ERROR_CODE = "mixmaterial" + +fun mixMaterialIdNotFoundException(id: Long) = + NotFoundException( + MIX_MATERIAL_EXCEPTION_ERROR_CODE, + MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE, + "A mix material with the id $id could not be found", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt index 13a677e..8fa0bfc 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt @@ -1,9 +1,12 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +import dev.fyloz.colorrecipesexplorer.exception.RestException +import org.springframework.http.HttpStatus import javax.persistence.* -const val IDENTIFIER_MATERIAL_NAME = "material" - @Entity @Table(name = "mix_type") data class MixType( @@ -36,3 +39,54 @@ fun mixType( name, material = material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType) ).apply(op) + +// ==== Exceptions ==== +private const val MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE = "Mix type not found" +private const val MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE = "Mix type already exists" +private const val MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete mix type" +private const val MIX_TYPE_EXCEPTION_ERROR_CODE = "mixtype" + +class MixTypeNameAndMaterialTypeNotFoundException(name: String, materialType: MaterialType) : + RestException( + "notfound-mixtype-namematerialtype", + MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, + HttpStatus.NOT_FOUND, + "A mix type with the name $name and material type ${materialType.name} could not be found", + mapOf( + "name" to name, + "materialType" to materialType.name + ) + ) + +fun mixTypeIdNotFoundException(id: Long) = + NotFoundException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, + "A mix type with the id $id could not be found", + id + ) + +fun mixTypeNameNotFoundException(name: String) = + NotFoundException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, + "A mix type with the name $name could not be found", + name, + "name" + ) + +fun mixTypeNameAlreadyExistsException(name: String) = + AlreadyExistsException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, + "A mix type with the name $name already exists", + name, + "name" + ) + +fun cannotDeleteMixTypeException(mixType: MixType) = + CannotDeleteException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the mix type ${mixType.name} because one or more mixes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt index bac32a1..b047f8f 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt @@ -1,8 +1,11 @@ package dev.fyloz.colorrecipesexplorer.model import com.fasterxml.jackson.annotation.JsonIgnore +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +import dev.fyloz.colorrecipesexplorer.exception.RestException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize +import org.springframework.http.HttpStatus import java.time.LocalDate import javax.persistence.* import javax.validation.constraints.* @@ -246,3 +249,27 @@ fun noteDto( content: String? = "note", op: NoteDto.() -> Unit = {} ) = NoteDto(groupId, content).apply(op) + +// ==== Exceptions ==== +private const val RECIPE_NOT_FOUND_EXCEPTION_TITLE = "Recipe not found" +private const val RECIPE_EXCEPTION_ERROR_CODE = "recipe" + +class RecipeImageNotFoundException(id: Long, recipe: Recipe) : + RestException( + "notfound-recipeimage-id", + "Recipe image not found", + HttpStatus.NOT_FOUND, + "A recipe image with the id $id could no be found for the recipe ${recipe.name}", + mapOf( + "id" to id, + "recipe" to recipe.name + ) + ) + +fun recipeIdNotFoundException(id: Long) = + NotFoundException( + RECIPE_EXCEPTION_ERROR_CODE, + RECIPE_NOT_FOUND_EXCEPTION_TITLE, + "A recipe with the id $id could not be found", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt index 42f6889..830c348 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import javax.persistence.* @Entity @@ -21,3 +22,15 @@ fun recipeStep( message: String = "message", op: RecipeStep.() -> Unit = {} ) = RecipeStep(id, position, message).apply(op) + +// ==== Exceptions ==== +private const val RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE = "Recipe step not found" +private const val RECIPE_STEP_EXCEPTION_ERROR_CODE = "recipestep" + +fun recipeStepIdNotFoundException(id: Long) = + NotFoundException( + RECIPE_STEP_EXCEPTION_ERROR_CODE, + RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE, + "A recipe step with the id $id could not be found", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt index 48029c4..7462285 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt @@ -2,13 +2,11 @@ package dev.fyloz.colorrecipesexplorer.service import dev.fyloz.colorrecipesexplorer.config.blacklistedJwtTokens import dev.fyloz.colorrecipesexplorer.config.defaultGroupCookieName -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.model.validation.or import dev.fyloz.colorrecipesexplorer.repository.EmployeeGroupRepository import dev.fyloz.colorrecipesexplorer.repository.EmployeeRepository -import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Lazy import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UserDetails @@ -77,11 +75,11 @@ interface EmployeeUserDetailsService : UserDetailsService { @Service class EmployeeServiceImpl( employeeRepository: EmployeeRepository, - @Lazy val passwordEncoder: PasswordEncoder + @Lazy val groupService: EmployeeGroupService, + @Lazy val passwordEncoder: PasswordEncoder, ) : AbstractExternalModelService(employeeRepository), EmployeeService { - @Autowired - lateinit var groupService: EmployeeGroupServiceImpl + override fun idNotFoundException(id: Long) = employeeIdNotFoundException(id) override fun existsByFirstNameAndLastName(firstName: String, lastName: String): Boolean = repository.existsByFirstNameAndLastName(firstName, lastName) @@ -94,9 +92,8 @@ class EmployeeServiceImpl( override fun getById(id: Long, ignoreDefaultGroupUsers: Boolean, ignoreSystemUsers: Boolean): Employee = super.getById(id).apply { - if (ignoreSystemUsers && isSystemUser || ignoreDefaultGroupUsers && isDefaultGroupUser) throw EntityNotFoundException( - id - ) + if (ignoreSystemUsers && isSystemUser || ignoreDefaultGroupUsers && isDefaultGroupUser) + throw idNotFoundException(id) } override fun getByGroup(group: EmployeeGroup): Collection = @@ -122,8 +119,10 @@ class EmployeeServiceImpl( }) override fun save(entity: Employee): Employee { + if (existsById(entity.id)) + throw employeeIdAlreadyExistsException(entity.id) if (existsByFirstNameAndLastName(entity.firstName, entity.lastName)) - throw EntityAlreadyExistsException("${entity.firstName} ${entity.lastName}") + throw employeeFullNameAlreadyExistsException(entity.firstName, entity.lastName) return super.save(entity) } @@ -173,7 +172,7 @@ class EmployeeServiceImpl( override fun update(entity: Employee, ignoreDefaultGroupUsers: Boolean, ignoreSystemUsers: Boolean): Employee { with(repository.findByFirstNameAndLastName(entity.firstName, entity.lastName)) { if (this != null && id != entity.id) - throw EntityAlreadyExistsException("${entity.firstName} ${entity.lastName}") + throw employeeFullNameAlreadyExistsException(entity.firstName, entity.lastName) } return super.update(entity) @@ -219,11 +218,14 @@ const val defaultGroupCookieMaxAge = 10 * 365 * 24 * 60 * 60 // 10 ans class EmployeeGroupServiceImpl( val employeeService: EmployeeService, employeeGroupRepository: EmployeeGroupRepository -) : - AbstractExternalNamedModelService( - employeeGroupRepository - ), +) : AbstractExternalNamedModelService( + employeeGroupRepository +), EmployeeGroupService { + override fun idNotFoundException(id: Long) = employeeGroupIdNotFoundException(id) + override fun nameNotFoundException(name: String) = employeeGroupNameNotFoundException(name) + override fun nameAlreadyExistsException(name: String) = employeeGroupNameAlreadyExistsException(name) + override fun existsByName(name: String): Boolean = repository.existsByName(name) override fun getEmployeesForGroup(id: Long): Collection = employeeService.getByGroup(getById(id)) @@ -254,7 +256,7 @@ class EmployeeGroupServiceImpl( override fun getRequestDefaultGroup(request: HttpServletRequest): EmployeeGroup { val defaultGroupCookie = WebUtils.getCookie(request, defaultGroupCookieName) - ?: throw EntityNotFoundException("defaultGroup") + ?: throw NoDefaultGroupException() val defaultGroupUser = employeeService.getById( defaultGroupCookie.value.toLong(), ignoreDefaultGroupUsers = false, @@ -281,9 +283,9 @@ class EmployeeUserDetailsServiceImpl( override fun loadUserByUsername(username: String): UserDetails { try { return loadUserByEmployeeId(username.toLong(), true) - } catch (ex: EntityNotFoundException) { + } catch (ex: NotFoundException) { throw UsernameNotFoundException(username) - } catch (ex: EntityNotFoundException) { + } catch (ex: NotFoundException) { throw UsernameNotFoundException(username) } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt index 1f48547..65f037f 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt @@ -1,10 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteEntityException -import dev.fyloz.colorrecipesexplorer.model.Company -import dev.fyloz.colorrecipesexplorer.model.CompanySaveDto -import dev.fyloz.colorrecipesexplorer.model.CompanyUpdateDto -import dev.fyloz.colorrecipesexplorer.model.company +import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.CompanyRepository import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Service @@ -21,6 +17,10 @@ class CompanyServiceImpl( ) : AbstractExternalNamedModelService(companyRepository), CompanyService { + override fun idNotFoundException(id: Long) = companyIdNotFoundException(id) + override fun nameNotFoundException(name: String) = companyNameNotFoundException(name) + override fun nameAlreadyExistsException(name: String) = companyNameAlreadyExistsException(name) + override fun isLinkedToRecipes(company: Company): Boolean = recipeService.existsByCompany(company) override fun update(entity: CompanyUpdateDto): Company { @@ -35,8 +35,8 @@ class CompanyServiceImpl( }) } - override fun deleteById(id: Long) { - if (!repository.canBeDeleted(id)) throw CannotDeleteEntityException(id) - super.deleteById(id) + override fun delete(entity: Company) { + if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteCompany(entity) + super.delete(entity) } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt index 58ea131..0dc05b3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt @@ -1,12 +1,9 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.LowQuantitiesException -import dev.fyloz.colorrecipesexplorer.exception.LowQuantityException -import dev.fyloz.colorrecipesexplorer.model.MaterialQuantityDto -import dev.fyloz.colorrecipesexplorer.model.MixDeductDto -import dev.fyloz.colorrecipesexplorer.model.MixMaterial -import dev.fyloz.colorrecipesexplorer.model.materialQuantityDto +import dev.fyloz.colorrecipesexplorer.exception.RestException +import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.service.utils.mapMayThrow +import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import javax.transaction.Transactional @@ -66,16 +63,16 @@ class InventoryServiceImpl( @Transactional override fun deduct(materialQuantities: Collection): Collection { - val thrown = mutableListOf() + val thrown = mutableListOf() val updatedQuantities = - materialQuantities.mapMayThrow( - { thrown.add(it.materialQuantity) } + materialQuantities.mapMayThrow( + { thrown.add(it) } ) { materialQuantityDto(materialId = it.material, quantity = deduct(it)) } if (thrown.isNotEmpty()) { - throw LowQuantitiesException(thrown) + throw MultiplesNotEnoughInventoryException(thrown) } return updatedQuantities } @@ -85,7 +82,31 @@ class InventoryServiceImpl( if (this.inventoryQuantity >= materialQuantity.quantity) { materialService.updateQuantity(this, -materialQuantity.quantity) } else { - throw LowQuantityException(materialQuantity) + throw NotEnoughInventoryException(materialQuantity.quantity, this) } } } + +class NotEnoughInventoryException(quantity: Float, material: Material) : + RestException( + "notenoughinventory", + "Not enough inventory", + HttpStatus.BAD_REQUEST, + "Cannot deduct ${quantity}mL of ${material.name} because there is only ${material.inventoryQuantity}mL in inventory", + mapOf( + "material" to material.name, + "requestQuantity" to quantity, + "availableQuantity" to material.inventoryQuantity + ) + ) + +class MultiplesNotEnoughInventoryException(exceptions: List) : + RestException( + "notenoughinventory-multiple", + "Not enough inventory", + HttpStatus.BAD_REQUEST, + "Cannot deduct requested quantities because there is no enough of them in inventory", + mapOf( + "lowQuantities" to exceptions.map { it.extensions } + ) + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt index c0f5b0c..384e04c 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteEntityException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MaterialRepository import dev.fyloz.colorrecipesexplorer.service.files.SimdutService @@ -47,6 +46,10 @@ class MaterialServiceImpl( materialRepository ), MaterialService { + override fun idNotFoundException(id: Long) = materialIdNotFoundException(id) + override fun nameNotFoundException(name: String) = materialNameNotFoundException(name) + override fun nameAlreadyExistsException(name: String) = materialNameAlreadyExistsException(name) + override fun existsByMaterialType(materialType: MaterialType): Boolean = repository.existsByMaterialType(materialType) @@ -113,8 +116,8 @@ class MaterialServiceImpl( Assert.notNull(material.name, "The persisted material with the id ${material.id} has a null name") } - override fun deleteById(id: Long) { - if (!repository.canBeDeleted(id)) throw CannotDeleteEntityException(id) - super.deleteById(id) + override fun delete(entity: Material) { + if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMaterialException(entity) + super.delete(entity) } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt index 6c807df..192c9e1 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt @@ -1,19 +1,10 @@ package dev.fyloz.colorrecipesexplorer.service import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteEntityException -import dev.fyloz.colorrecipesexplorer.exception.RestException -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.model.MaterialType -import dev.fyloz.colorrecipesexplorer.model.MaterialTypeSaveDto -import dev.fyloz.colorrecipesexplorer.model.MaterialTypeUpdateDto -import dev.fyloz.colorrecipesexplorer.model.materialType +import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.model.validation.isNotNullAndNotBlank import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository -import org.springframework.http.HttpStatus import org.springframework.stereotype.Service -import org.springframework.web.bind.annotation.ResponseStatus -import kotlin.contracts.ExperimentalContracts interface MaterialTypeService : ExternalNamedModelService { @@ -38,6 +29,10 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma AbstractExternalNamedModelService( repository ), MaterialTypeService { + override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id) + override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name) + override fun nameAlreadyExistsException(name: String) = materialTypeNameAlreadyExistsException(name) + override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix) override fun isUsedByMaterial(materialType: MaterialType): Boolean = materialService.existsByMaterialType(materialType) @@ -47,11 +42,10 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma override fun save(entity: MaterialType): MaterialType { if (existsByPrefix(entity.prefix)) - throw EntityAlreadyExistsException(entity.prefix) + throw materialTypePrefixAlreadyExistsException(entity.prefix) return super.save(entity) } - @ExperimentalContracts override fun update(entity: MaterialTypeUpdateDto): MaterialType { val persistedMaterialType by lazy { getById(entity.id) } @@ -68,15 +62,15 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma override fun update(entity: MaterialType): MaterialType { with(repository.findByPrefix(entity.prefix)) { if (this != null && id != entity.id) - throw EntityAlreadyExistsException(entity.prefix) + throw materialTypePrefixAlreadyExistsException(entity.prefix) } return super.update(entity) } - override fun deleteById(id: Long) { - if (!repository.canBeDeleted(id)) throw CannotDeleteEntityException(id) - super.deleteById(id) + override fun delete(entity: MaterialType) { + if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMaterialTypeException(entity) + super.delete(entity) } override fun saveSystemTypes(systemTypeProperties: Collection) { @@ -102,9 +96,3 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma oldSystemTypes.forEach { update(materialType(it, newSystemType = false)) } } } - -@ResponseStatus(HttpStatus.CONFLICT) -class CannotDeleteUsedMaterialTypeRestException : - RestException("Cannot delete a used material type", HttpStatus.CONFLICT) { - override fun buildBody(): RestExceptionBody = object : RestExceptionBody() {} -} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt index 33ad48e..2431802 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt @@ -1,9 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.model.Material -import dev.fyloz.colorrecipesexplorer.model.MixMaterial -import dev.fyloz.colorrecipesexplorer.model.MixMaterialDto -import dev.fyloz.colorrecipesexplorer.model.mixMaterial +import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MixMaterialRepository import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Service @@ -27,6 +24,8 @@ class MixMaterialServiceImpl( mixMaterialRepository: MixMaterialRepository, @Lazy val materialService: MaterialService ) : AbstractModelService(mixMaterialRepository), MixMaterialService { + override fun idNotFoundException(id: Long) = mixMaterialIdNotFoundException(id) + override fun existsByMaterial(material: Material): Boolean = repository.existsByMaterial(material) override fun create(mixMaterials: Set): Set = mixMaterials.map(::create).toSet() diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt index 011a255..b1cafd6 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteEntityException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MixRepository import org.springframework.context.annotation.Lazy @@ -30,6 +29,8 @@ class MixServiceImpl( val mixTypeService: MixTypeService ) : AbstractModelService(mixRepository), MixService { + override fun idNotFoundException(id: Long) = mixIdNotFoundException(id) + override fun getAllByMixType(mixType: MixType): Collection = repository.findAllByMixType(mixType) override fun mixTypeIsShared(mixType: MixType): Boolean = getAllByMixType(mixType).count() > 1 @@ -81,12 +82,8 @@ class MixServiceImpl( @Transactional override fun delete(entity: Mix) { + if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMixException(entity) recipeService.removeMix(entity) super.delete(entity) } - - override fun deleteById(id: Long) { - if (!repository.canBeDeleted(id)) throw CannotDeleteEntityException(id) - super.deleteById(id) - } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt index 06f858a..5b6e682 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt @@ -1,8 +1,5 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteEntityException -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MixTypeRepository import org.springframework.context.annotation.Lazy @@ -35,15 +32,19 @@ class MixTypeServiceImpl( @Lazy val mixService: MixService ) : AbstractNamedModelService(mixTypeRepository), MixTypeService { + override fun idNotFoundException(id: Long) = mixTypeIdNotFoundException(id) + override fun nameNotFoundException(name: String) = mixTypeNameNotFoundException(name) + override fun nameAlreadyExistsException(name: String) = mixTypeNameAlreadyExistsException(name) + override fun existsByNameAndMaterialType(name: String, materialType: MaterialType): Boolean = repository.existsByNameAndMaterialType(name, materialType) override fun getByMaterial(material: Material): MixType = - repository.findByMaterial(material) ?: throw EntityNotFoundException(material.name) + repository.findByMaterial(material) ?: throw nameNotFoundException(material.name) override fun getByNameAndMaterialType(name: String, materialType: MaterialType): MixType = repository.findByNameAndMaterialType(name, materialType) - ?: throw EntityNotFoundException("$name/${materialType.name}") + ?: throw MixTypeNameAndMaterialTypeNotFoundException(name, materialType) override fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialType): MixType = if (existsByNameAndMaterialType(name, materialType)) @@ -53,7 +54,7 @@ class MixTypeServiceImpl( override fun save(entity: MixType): MixType { if (materialService.existsByName(entity.name)) - throw EntityAlreadyExistsException(entity.name) + throw materialNameAlreadyExistsException(entity.name) return super.save(entity) } @@ -77,8 +78,8 @@ class MixTypeServiceImpl( material.materialType = materialType }) - override fun deleteById(id: Long) { - if (!repository.canBeDeleted(id)) throw CannotDeleteEntityException(id) - super.deleteById(id) + override fun delete(entity: MixType) { + if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMixTypeException(entity) + super.delete(entity) } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt index 1173456..b8db07c 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.model.validation.or import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository @@ -38,6 +37,8 @@ class RecipeServiceImpl( ) : AbstractExternalModelService(recipeRepository), RecipeService { + override fun idNotFoundException(id: Long) = recipeIdNotFoundException(id) + override fun existsByCompany(company: Company): Boolean = repository.existsByCompany(company) override fun getAllByCompany(company: Company): Collection = repository.findAllByCompany(company) @@ -154,7 +155,7 @@ class RecipeImageServiceImpl(val recipeService: RecipeService, val fileService: try { fileService.readAsBytes(getPath(id, recipeId)) } catch (ex: NoSuchFileException) { - throw EntityNotFoundException("$recipeId/$id") + throw RecipeImageNotFoundException(id, recipeService.getById(recipeId)) } override fun getAllIdsForRecipe(recipeId: Long): Collection { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt index 1128929..42d941a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt @@ -1,6 +1,7 @@ package dev.fyloz.colorrecipesexplorer.service import dev.fyloz.colorrecipesexplorer.model.RecipeStep +import dev.fyloz.colorrecipesexplorer.model.recipeStepIdNotFoundException import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository import org.springframework.stereotype.Service @@ -9,4 +10,6 @@ interface RecipeStepService : ModelService @Service class RecipeStepServiceImpl(recipeStepRepository: RecipeStepRepository) : AbstractModelService(recipeStepRepository), - RecipeStepService + RecipeStepService { + override fun idNotFoundException(id: Long) = recipeStepIdNotFoundException(id) +} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt index d44f2ff..0414c96 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt @@ -1,7 +1,7 @@ package dev.fyloz.colorrecipesexplorer.service -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.EntityDto import dev.fyloz.colorrecipesexplorer.model.Model import dev.fyloz.colorrecipesexplorer.model.NamedModel @@ -66,19 +66,21 @@ abstract class AbstractService>(override val reposito abstract class AbstractModelService>(repository: R) : AbstractService(repository), ModelService { + protected abstract fun idNotFoundException(id: Long): NotFoundException + override fun existsById(id: Long): Boolean = repository.existsById(id) - override fun getById(id: Long): E = repository.findByIdOrNull(id) ?: throw EntityNotFoundException(id) + override fun getById(id: Long): E = repository.findByIdOrNull(id) ?: throw idNotFoundException(id) override fun save(entity: E): E { if (entity.id != null && existsById(entity.id!!)) - throw EntityAlreadyExistsException(entity.id!!) + throw idNotFoundException(entity.id!!) return super.save(entity) } override fun update(entity: E): E { assertId(entity.id) if (!existsById(entity.id!!)) - throw EntityNotFoundException(entity.id!!) + throw idNotFoundException(entity.id!!) return super.update(entity) } @@ -92,13 +94,16 @@ abstract class AbstractModelService>(repos abstract class AbstractNamedModelService>(repository: R) : AbstractModelService(repository), NamedModelService { + protected abstract fun nameNotFoundException(name: String): NotFoundException + protected abstract fun nameAlreadyExistsException(name: String): AlreadyExistsException + override fun existsByName(name: String): Boolean = repository.existsByName(name) - override fun getByName(name: String): E = repository.findByName(name) ?: throw EntityNotFoundException(name) + override fun getByName(name: String): E = repository.findByName(name) ?: throw nameNotFoundException(name) override fun deleteByName(name: String) = repository.deleteByName(name) override fun save(entity: E): E { if (existsByName(entity.name)) - throw EntityAlreadyExistsException(entity.name) + throw nameAlreadyExistsException(entity.name) return super.save(entity) } @@ -107,7 +112,7 @@ abstract class AbstractNamedModelService, R : J open fun `getById() throws EntityNotFoundException when no entity with the given id exists in the repository`() { whenever(repository.findById(entity.id!!)).doReturn(Optional.empty()) - val exception = assertThrows { service.getById(entity.id!!) } + val exception = assertThrows { service.getById(entity.id!!) } assertTrue(exception.value is Long) assertEquals(entity.id, exception.value as Long) } @@ -139,8 +139,8 @@ abstract class AbstractModelServiceTest, R : J doReturn(true).whenever(repository).existsById(entity.id!!) val exception = assertThrows { service.save(entity) } - assertTrue(exception.value is Long) - assertEquals(entity.id, exception.value as Long) + assertTrue(exception.id is Long) + assertEquals(entity.id, exception.id as Long) } // update() @@ -161,7 +161,7 @@ abstract class AbstractModelServiceTest, R : J open fun `update() throws EntityNotFoundException when no entity with the given id exists in the repository`() { doReturn(false).whenever(service).existsById(entity.id!!) - val exception = assertThrows { service.update(entity) } + val exception = assertThrows { service.update(entity) } assertTrue(exception.value is Long) assertEquals(entity.id, exception.value as Long) } @@ -217,7 +217,7 @@ abstract class AbstractNamedModelServiceTest { service.getByName(entity.name) } + val exception = assertThrows { service.getByName(entity.name) } assertEquals(entity.name, exception.value) } @@ -228,7 +228,7 @@ abstract class AbstractNamedModelServiceTest { service.save(entity) } - assertEquals(entity.name, exception.value) + assertEquals(entity.name, exception.id) } // update() @@ -251,7 +251,7 @@ abstract class AbstractNamedModelServiceTest { service.update(entity) } + val exception = assertThrows { service.update(entity) } assertTrue(exception.value is Long) assertEquals(entity.id, exception.value as Long) @@ -263,7 +263,7 @@ abstract class AbstractNamedModelServiceTest { service.update(entity) } - assertEquals(entity.name, exception.value) + assertEquals(entity.name, exception.id) } // deleteByName() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt index 3c7e704..ac536f8 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt @@ -3,7 +3,7 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* import dev.fyloz.colorrecipesexplorer.config.defaultGroupCookieName import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.EmployeeGroupRepository import dev.fyloz.colorrecipesexplorer.repository.EmployeeRepository @@ -76,7 +76,7 @@ class EmployeeServiceTest : fun `getById() throws EntityNotFoundException when the corresponding employee is a default group user`() { whenever(repository.findById(entityDefaultGroupUser.id)).doReturn(Optional.of(entityDefaultGroupUser)) - val exception = assertThrows { + val exception = assertThrows { service.getById( entityDefaultGroupUser.id, ignoreDefaultGroupUsers = true, @@ -91,7 +91,7 @@ class EmployeeServiceTest : fun `getById() throws EntityNotFoundException when the corresponding employee is a system user`() { whenever(repository.findById(entitySystemUser.id)).doReturn(Optional.of(entitySystemUser)) - val exception = assertThrows { + val exception = assertThrows { service.getById( entitySystemUser.id, ignoreDefaultGroupUsers = false, @@ -151,7 +151,7 @@ class EmployeeServiceTest : doReturn(true).whenever(repository).existsByFirstNameAndLastName(entity.firstName, entity.lastName) val exception = assertThrows { service.save(entity) } - assertEquals("${entity.firstName} ${entity.lastName}", exception.value) + assertEquals("${entity.firstName} ${entity.lastName}", exception.id) } @Test @@ -191,8 +191,8 @@ class EmployeeServiceTest : ignoreSystemUsers = true ) } - assertTrue(exception.value is String) - assertEquals("${entity.firstName} ${entity.lastName}", exception.value as String) + assertTrue(exception.id is String) + assertEquals("${entity.firstName} ${entity.lastName}", exception.id as String) } } @@ -262,7 +262,7 @@ class EmployeeGroupServiceTest : whenever(request.cookies).doReturn(arrayOf()) - val exception = assertThrows { service.getRequestDefaultGroup(request) } + val exception = assertThrows { service.getRequestDefaultGroup(request) } assertEquals("defaultGroup", exception.value) } @@ -327,7 +327,7 @@ class EmployeeUserDetailsServiceTest { @Test fun `loadUserByUsername() throws UsernameNotFoundException when no employee with the given id exists`() { whenever(employeeService.getById(eq(employee.id), any(), any())).doThrow( - EntityNotFoundException( + NotFoundException( employee.id ) ) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt index 24558e1..694eb90 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt @@ -2,7 +2,6 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* import dev.fyloz.colorrecipesexplorer.exception.LowQuantitiesException -import dev.fyloz.colorrecipesexplorer.exception.LowQuantityException import dev.fyloz.colorrecipesexplorer.model.* import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test @@ -155,7 +154,7 @@ class InventoryServiceTest { @Test fun `deduct(materialQuantity) throws LowQuantityException when there is not enough inventory of the given material`() { withGivenQuantities(0f, 1000f) { - val exception = assertThrows { service.deduct(this) } + val exception = assertThrows { service.deduct(this) } assertEquals(this, exception.materialQuantity) } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt index 0a454b7..43a1b76 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt @@ -128,7 +128,7 @@ class MaterialServiceTest : doReturn(true).whenever(service).existsByName(entity.name) val exception = assertThrows { service.save(entity) } - assertEquals(entity.name, exception.value) + assertEquals(entity.name, exception.id) } @Test @@ -160,7 +160,7 @@ class MaterialServiceTest : doReturn(entity).whenever(service).getById(material.id!!) val exception = assertThrows { service.update(material) } - assertEquals(material.name, exception.value) + assertEquals(material.name, exception.id) } // updateQuantity() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt index e74d355..608e180 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt @@ -2,7 +2,7 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository import org.junit.jupiter.api.AfterEach @@ -110,7 +110,7 @@ class MaterialTypeServiceTest : doReturn(true).whenever(service).existsByPrefix(entity.prefix) val exception = assertThrows { service.save(entity) } - assertEquals(entity.prefix, exception.value) + assertEquals(entity.prefix, exception.id) } // update() @@ -138,7 +138,7 @@ class MaterialTypeServiceTest : doReturn(false).whenever(service).existsById(entity.id!!) doReturn(null).whenever(service).getById(entity.id!!) - val exception = assertThrows { service.update(entity) } + val exception = assertThrows { service.update(entity) } assertTrue(exception.value is Long) assertEquals(entity.id, exception.value as Long) } @@ -150,7 +150,7 @@ class MaterialTypeServiceTest : doReturn(entity).whenever(service).getById(entity.id!!) val exception = assertThrows { service.update(entity) } - assertEquals(entity.name, exception.value) + assertEquals(entity.name, exception.id) } @Test @@ -160,7 +160,7 @@ class MaterialTypeServiceTest : doReturn(entity).whenever(service).getById(entity.id!!) val exception = assertThrows { service.update(entity) } - assertEquals(entity.prefix, exception.value) + assertEquals(entity.prefix, exception.id) } // delete() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt index b4a8201..0a0ac47 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt @@ -2,7 +2,7 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MixTypeRepository import org.junit.jupiter.api.AfterEach @@ -57,7 +57,7 @@ class MixTypeServiceTest : AbstractNamedModelServiceTest { service.getByMaterial(material) } + val exception = assertThrows { service.getByMaterial(material) } assertEquals(material.name, exception.value) } @@ -104,7 +104,7 @@ class MixTypeServiceTest : AbstractNamedModelServiceTest { service.save(entity) } - assertEquals(entity.name, exception.value) + assertEquals(entity.name, exception.id) } // saveForNameAndMaterialType() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt index dc771ff..3851792 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt @@ -1,7 +1,7 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.EntityNotFoundException +import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository import dev.fyloz.colorrecipesexplorer.service.files.FileService @@ -198,7 +198,7 @@ class RecipeImageServiceTest { whenever(fileService.readAsBytes(imagePath)).doAnswer { throw NoSuchFileException(imagePath) } val exception = - assertThrows { service.getByIdForRecipe(imageId, recipeId) } + assertThrows { service.getByIdForRecipe(imageId, recipeId) } assertEquals("$recipeId/$imageId", exception.value) } From 891e32990dad880ce6d1d3242b21f1d22d0a822b Mon Sep 17 00:00:00 2001 From: FyloZ Date: Sun, 11 Apr 2021 16:26:45 -0400 Subject: [PATCH 2/4] =?UTF-8?q?Correction=20de=20la=20sauvegarde=20dans=20?= =?UTF-8?q?les=20services=20g=C3=A9n=C3=A9riques=20qui=20lan=C3=A7aient=20?= =?UTF-8?q?une=20NotFoundException=20lorsque=20l'identifiant=20sp=C3=A9cif?= =?UTF-8?q?i=C3=A9=20existe.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 1 - .../service/files/TouchUpKitService.java | 3 +- .../service/files/XlsService.java | 7 +- .../utils/PdfBuilder.java | 16 +- .../xlsx/XlsxExporter.java | 12 +- .../xlsx/builder/CellBuilder.java | 11 +- .../xlsx/builder/SheetBuilder.java | 9 +- .../xlsx/component/Catalog.java | 7 +- .../xlsx/component/Document.java | 5 +- .../xlsx/component/Sheet.java | 9 +- .../xlsx/component/Table.java | 41 +-- .../xlsx/component/cells/DescriptionCell.java | 5 +- .../xlsx/component/cells/NoteCell.java | 4 +- .../component/cells/SectionTitleCell.java | 5 +- .../xlsx/component/cells/TitleCell.java | 5 +- .../xlsx/util/CellUtils.java | 7 +- .../ColorRecipesExplorerApplication.kt | 8 +- .../DatabaseVersioning.kt | 44 +-- .../config/InitialDataLoader.kt | 6 +- .../config/WebSecurityConfig.kt | 162 ++++----- .../properties/MaterialTypeProperties.kt | 6 +- .../exception/RestException.kt | 102 +++--- .../colorrecipesexplorer/model/Company.kt | 96 +++--- .../colorrecipesexplorer/model/Employee.kt | 214 ++++++------ .../model/EmployeeGroup.kt | 137 ++++---- .../model/EmployeePermission.kt | 22 +- .../colorrecipesexplorer/model/Material.kt | 174 +++++----- .../model/MaterialType.kt | 172 +++++----- .../fyloz/colorrecipesexplorer/model/Mix.kt | 146 ++++---- .../colorrecipesexplorer/model/MixMaterial.kt | 68 ++-- .../colorrecipesexplorer/model/MixType.kt | 112 +++--- .../colorrecipesexplorer/model/Recipe.kt | 318 +++++++++--------- .../colorrecipesexplorer/model/RecipeStep.kt | 40 ++- .../model/validation/NullOrNotBlank.kt | 6 +- .../model/validation/NullOrSize.kt | 10 +- .../repository/CompanyRepository.kt | 2 +- .../repository/MaterialRepository.kt | 2 +- .../repository/MaterialTypeRepository.kt | 2 +- .../repository/MixRepository.kt | 2 +- .../repository/MixTypeRepository.kt | 2 +- .../rest/AccountControllers.kt | 88 ++--- .../rest/CompanyController.kt | 22 +- .../rest/InventoryController.kt | 6 +- .../rest/MaterialController.kt | 58 ++-- .../rest/MaterialTypeController.kt | 22 +- .../rest/RecipeController.kt | 58 ++-- .../colorrecipesexplorer/rest/RestUtils.kt | 18 +- .../service/AccountService.kt | 160 ++++----- .../service/CompanyService.kt | 13 +- .../service/InventoryService.kt | 90 ++--- .../service/MaterialService.kt | 66 ++-- .../service/MaterialTypeService.kt | 20 +- .../service/MixMaterialService.kt | 23 +- .../service/MixService.kt | 14 +- .../service/MixTypeService.kt | 55 +-- .../service/RecipeService.kt | 108 +++--- .../service/RecipeStepService.kt | 6 +- .../colorrecipesexplorer/service/Service.kt | 19 +- .../service/files/FileService.kt | 29 +- .../service/files/SimdutService.kt | 24 +- .../service/utils/Collections.kt | 4 +- src/main/resources/application.properties | 3 - src/main/resources/junit-platform.properties | 2 +- .../repository/MaterialRepositoryTest.kt | 4 +- .../repository/MixRepositoryTest.kt | 4 +- .../service/AbstractServiceTest.kt | 97 +++--- .../service/AccountsServiceTest.kt | 109 +++--- .../service/CompanyServiceTest.kt | 4 +- .../service/InventoryServiceTest.kt | 76 ++--- .../service/MaterialServiceTest.kt | 40 +-- .../service/MaterialTypeServiceTest.kt | 37 +- .../service/MixMaterialServiceTest.kt | 18 +- .../service/MixServiceTest.kt | 74 ++-- .../service/MixTypeServiceTest.kt | 14 +- .../service/RecipeServiceTest.kt | 31 +- .../service/RecipeStepServiceTest.kt | 2 +- .../service/files/SimdutServiceTest.kt | 6 +- 77 files changed, 1729 insertions(+), 1695 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d40b7a7..4544a44 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,6 @@ plugins { id("java") id("org.jetbrains.kotlin.jvm") version "1.4.30" id("org.jetbrains.dokka") version "1.4.20" - id("com.leobia.gradle.sassjavacompiler") version "0.2.1" id("org.springframework.boot") version "2.3.4.RELEASE" id("org.jetbrains.kotlin.plugin.spring") version "1.4.30" id("org.jetbrains.kotlin.plugin.jpa") version "1.4.30" diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.java b/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.java index 24f906c..93df0aa 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.java @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.service.files; -import dev.fyloz.colorrecipesexplorer.utils.PdfBuilder; import dev.fyloz.colorrecipesexplorer.utils.PdfBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ResourceLoader; @@ -15,7 +14,7 @@ public class TouchUpKitService { private static final String TOUCH_UP_EN = "TOUCH UP KIT"; public static final int FONT_SIZE = 42; - private ResourceLoader resourceLoader; + private final ResourceLoader resourceLoader; @Autowired public TouchUpKitService(ResourceLoader resourceLoader) { diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/XlsService.java b/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/XlsService.java index 0083e8b..50553b6 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/XlsService.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/service/files/XlsService.java @@ -1,8 +1,5 @@ package dev.fyloz.colorrecipesexplorer.service.files; -import dev.fyloz.colorrecipesexplorer.model.Recipe; -import dev.fyloz.colorrecipesexplorer.service.RecipeService; -import dev.fyloz.colorrecipesexplorer.xlsx.XlsxExporter; import dev.fyloz.colorrecipesexplorer.model.Recipe; import dev.fyloz.colorrecipesexplorer.service.RecipeService; import dev.fyloz.colorrecipesexplorer.xlsx.XlsxExporter; @@ -19,8 +16,8 @@ import java.util.zip.ZipOutputStream; @Service public class XlsService { - private RecipeService recipeService; - private Logger logger; + private final RecipeService recipeService; + private final Logger logger; @Autowired public XlsService(RecipeService recipeService, Logger logger) { diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/utils/PdfBuilder.java b/src/main/java/dev/fyloz/colorrecipesexplorer/utils/PdfBuilder.java index 559ab00..39ea4ec 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/utils/PdfBuilder.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/utils/PdfBuilder.java @@ -16,14 +16,14 @@ public class PdfBuilder { private static final String PATH_FONT_ARIAL_BOLD = "classpath:fonts/arialbd.ttf"; - private PDFont font; - private PDDocument document = new PDDocument(); - private PDPage page = new PDPage(); - private Collection lines = new ArrayList<>(); - private boolean duplicated; - private int fontSize; - private int fontSizeBold; - private int lineSpacing; + private final PDFont font; + private final PDDocument document = new PDDocument(); + private final PDPage page = new PDPage(); + private final Collection lines = new ArrayList<>(); + private final boolean duplicated; + private final int fontSize; + private final int fontSizeBold; + private final int lineSpacing; public PdfBuilder(ResourceLoader resourceLoader, boolean duplicated, int fontSize) throws IOException { this.duplicated = duplicated; diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/XlsxExporter.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/XlsxExporter.java index 05dbab1..79b54e4 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/XlsxExporter.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/XlsxExporter.java @@ -1,22 +1,12 @@ package dev.fyloz.colorrecipesexplorer.xlsx; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Table; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.DescriptionCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.SectionTitleCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.TitleCell; -import dev.fyloz.colorrecipesexplorer.xlsx.util.Position; import dev.fyloz.colorrecipesexplorer.model.Mix; import dev.fyloz.colorrecipesexplorer.model.MixMaterial; import dev.fyloz.colorrecipesexplorer.model.Recipe; -import dev.fyloz.colorrecipesexplorer.model.RecipeStep; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Catalog; import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet; import dev.fyloz.colorrecipesexplorer.xlsx.component.Table; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.DescriptionCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.NoteCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.SectionTitleCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.TitleCell; import dev.fyloz.colorrecipesexplorer.xlsx.util.Position; @@ -27,7 +17,7 @@ import java.io.IOException; import java.util.Collection; public class XlsxExporter { - private Logger logger; + private final Logger logger; public XlsxExporter(Logger logger) { this.logger = logger; diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/CellBuilder.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/CellBuilder.java index 658b2fa..1020043 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/CellBuilder.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/CellBuilder.java @@ -1,10 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.builder; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; -import dev.fyloz.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException; import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; @@ -18,9 +13,9 @@ import org.apache.poi.xssf.usermodel.XSSFFont; public class CellBuilder { - private Document document; - private Cell cell; - private XSSFCell xcell; + private final Document document; + private final Cell cell; + private final XSSFCell xcell; public CellBuilder(Document document, Cell cell, int x, int y) { this.document = document; diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/SheetBuilder.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/SheetBuilder.java index 020135c..1947319 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/SheetBuilder.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/builder/SheetBuilder.java @@ -1,10 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.builder; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException; import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; @@ -15,8 +10,8 @@ import org.apache.poi.ss.util.CellRangeAddress; public class SheetBuilder { - private Sheet sheet; - private Iterable cells; + private final Sheet sheet; + private final Iterable cells; public SheetBuilder(Sheet sheet) { this.sheet = sheet; diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Catalog.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Catalog.java index c8fea6f..4e8bc2b 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Catalog.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Catalog.java @@ -1,8 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; @@ -14,7 +11,7 @@ import java.util.List; public class Catalog { - private List content; + private final List content; public Catalog() { content = new ArrayList<>(); @@ -39,7 +36,7 @@ public class Catalog { private class CatalogValueCell extends Cell implements IBiggerCell, IFontCell { - private String value; + private final String value; public CatalogValueCell(String value) { super(CellType.STRING); diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Document.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Document.java index a29e9c3..ad27dcb 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Document.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Document.java @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component; -import dev.fyloz.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException; import dev.fyloz.colorrecipesexplorer.xlsx.builder.SheetBuilder; import dev.fyloz.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -12,8 +11,8 @@ public class Document extends XSSFWorkbook { public static final int WIDTH = 8; private static final XSSFWorkbookType WORKBOOK_TYPE = XSSFWorkbookType.XLSX; - private Sheet sheet; - private Logger logger; + private final Sheet sheet; + private final Logger logger; public Document(String name, Logger logger) { super(WORKBOOK_TYPE); diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Sheet.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Sheet.java index a576e66..77cba39 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Sheet.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Sheet.java @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.xssf.usermodel.XSSFCell; @@ -11,11 +10,11 @@ import java.util.*; public class Sheet { - private Document document; - private XSSFSheet xsheet; + private final Document document; + private final XSSFSheet xsheet; - private Map rows; - private List registeredCells; + private final Map rows; + private final List registeredCells; Sheet(Document document, XSSFSheet xsheet) { this.document = document; diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Table.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Table.java index 7973c59..4fc2c6f 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Table.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/Table.java @@ -1,10 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; -import dev.fyloz.colorrecipesexplorer.xlsx.exception.IndexOutOfTableBoundsException; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.Cell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; @@ -19,16 +14,16 @@ import java.util.*; public class Table { - private static DecimalFormat format = new DecimalFormat("#.###"); + private static final DecimalFormat format = new DecimalFormat("#.###"); - private int width; - private int height; - private int cellWidth; + private final int width; + private final int height; + private final int cellWidth; - private Map content; + private final Map content; - private List columns; - private List rows; + private final List columns; + private final List rows; public Table(int width, int height, String name) { this.width = width; @@ -140,9 +135,9 @@ public class Table { private final IndexedColors COLOR = IndexedColors.GREY_40_PERCENT; - private String name; - private int width; - private int height = 2; + private final String name; + private final int width; + private final int height = 2; public TableNameCell(String name, int width) { super(CellType.STRING); @@ -175,8 +170,8 @@ public class Table { private class TableSubNameCell extends Cell implements IFontCell, IBiggerCell, IColoredCell { private String name; - private int width; - private int height = 2; + private final int width; + private final int height = 2; public TableSubNameCell(String name, int width) { super(CellType.STRING); @@ -212,9 +207,9 @@ public class Table { private class TableStringValueCell extends Cell implements IFontCell, IBiggerCell { - private String value; - private int width; - private int height = 2; + private final String value; + private final int width; + private final int height = 2; public TableStringValueCell(String value, int width) { super(CellType.STRING); @@ -246,9 +241,9 @@ public class Table { private class TableNumValueCell extends Cell implements IFontCell, IBiggerCell { - private double value; - private int width; - private int height = 2; + private final double value; + private final int width; + private final int height = 2; public TableNumValueCell(double value, int width) { super(CellType.NUMERIC); diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/DescriptionCell.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/DescriptionCell.java index 87af00b..0184fc3 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/DescriptionCell.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/DescriptionCell.java @@ -1,8 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component.cells; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; @@ -17,7 +14,7 @@ public class DescriptionCell extends Cell implements IColoredCell, IBiggerCell, private static final IndexedColors BG_COLOR_NAME = IndexedColors.GREY_40_PERCENT; private static final short FONT_HEIGHT = 18; - private DescriptionCellType type; + private final DescriptionCellType type; private String valueStr; private double valueNbr; diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/NoteCell.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/NoteCell.java index 7b81584..998f129 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/NoteCell.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/NoteCell.java @@ -1,7 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component.cells; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; @@ -9,7 +7,7 @@ import org.apache.poi.ss.usermodel.CellType; public class NoteCell extends Cell implements IBiggerCell, IFontCell { - private String value; + private final String value; public NoteCell(String value) { super(CellType.STRING); diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/SectionTitleCell.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/SectionTitleCell.java index fcde067..1c4b1bd 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/SectionTitleCell.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/SectionTitleCell.java @@ -1,8 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component.cells; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; @@ -19,7 +16,7 @@ public class SectionTitleCell extends Cell implements IColoredCell, IBiggerCell, private static final IndexedColors FONT_COLOR = IndexedColors.WHITE; private static final int FONT_HEIGHT = 24; - private String title; + private final String title; public SectionTitleCell(String title) { super(CellType.STRING); diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/TitleCell.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/TitleCell.java index cf82182..bfcec7d 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/TitleCell.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/component/cells/TitleCell.java @@ -1,8 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.component.cells; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; -import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.Document; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell; import dev.fyloz.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell; @@ -18,7 +15,7 @@ public class TitleCell extends Cell implements IColoredCell, IBiggerCell, IFontC private static final IndexedColors FONT_COLOR = IndexedColors.WHITE; private static IndexedColors BACKGROUND_COLOR = IndexedColors.BLACK; - private String title; + private final String title; public TitleCell(String title) { super(TYPE); diff --git a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/util/CellUtils.java b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/util/CellUtils.java index 28dc3b1..bf1f35e 100644 --- a/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/util/CellUtils.java +++ b/src/main/java/dev/fyloz/colorrecipesexplorer/xlsx/util/CellUtils.java @@ -1,6 +1,5 @@ package dev.fyloz.colorrecipesexplorer.xlsx.util; -import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet; import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.util.CellRangeAddress; @@ -28,11 +27,7 @@ public class CellUtils { return true; } - if (cell.getCellType() == CellType.STRING && cell.getStringCellValue().trim().isEmpty()) { - return true; - } - - return false; + return cell.getCellType() == CellType.STRING && cell.getStringCellValue().trim().isEmpty(); } public static boolean isCellInMergedCell(Sheet sheet, int x, int y) { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/ColorRecipesExplorerApplication.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/ColorRecipesExplorerApplication.kt index 1a8579c..17c867a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/ColorRecipesExplorerApplication.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/ColorRecipesExplorerApplication.kt @@ -1,7 +1,7 @@ package dev.fyloz.colorrecipesexplorer -import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties +import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -9,9 +9,9 @@ import org.springframework.boot.runApplication @SpringBootApplication(exclude = [LiquibaseAutoConfiguration::class]) @EnableConfigurationProperties( - MaterialTypeProperties::class, - CreProperties::class, - DatabaseUpdaterProperties::class + MaterialTypeProperties::class, + CreProperties::class, + DatabaseUpdaterProperties::class ) class ColorRecipesExplorerApplication diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt index 4e818c6..81d6419 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/DatabaseVersioning.kt @@ -20,18 +20,18 @@ class DataSourceConfiguration { @Bean(name = ["dataSource"]) @ConfigurationProperties(prefix = "spring.datasource") fun customDataSource( - logger: Logger, - environment: Environment, - databaseUpdaterProperties: DatabaseUpdaterProperties + logger: Logger, + environment: Environment, + databaseUpdaterProperties: DatabaseUpdaterProperties ): DataSource { val databaseUrl: String = environment.getProperty("spring.datasource.url")!! runDatabaseVersionCheck(logger, databaseUrl, databaseUpdaterProperties) return DataSourceBuilder - .create() - .url(databaseUrl) // Hikari won't start without that - .build() + .create() + .url(databaseUrl) // Hikari won't start without that + .build() } } @@ -75,24 +75,24 @@ fun runDatabaseUpdate(logger: Logger, database: CreDatabase) { } fun getDatabase( - databaseUrl: String, - databaseUpdaterProperties: DatabaseUpdaterProperties, - logger: Logger + databaseUrl: String, + databaseUpdaterProperties: DatabaseUpdaterProperties, + logger: Logger ): CreDatabase { val databaseName = - (DATABASE_NAME_REGEX.find(databaseUrl) ?: throw DatabaseVersioningException.InvalidUrl(databaseUrl)).value + (DATABASE_NAME_REGEX.find(databaseUrl) ?: throw DatabaseVersioningException.InvalidUrl(databaseUrl)).value return CreDatabase( - databaseContext( - properties = databaseUpdaterProperties( - targetVersion = SUPPORTED_DATABASE_VERSION, - url = databaseUrl.removeSuffix(databaseName), - dbName = databaseName, - username = databaseUpdaterProperties.username, - password = databaseUpdaterProperties.password - ), - logger - ) + databaseContext( + properties = databaseUpdaterProperties( + targetVersion = SUPPORTED_DATABASE_VERSION, + url = databaseUrl.removeSuffix(databaseName), + dbName = databaseName, + username = databaseUpdaterProperties.username, + password = databaseUpdaterProperties.password + ), + logger + ) ) } @@ -101,7 +101,7 @@ fun throwUnsupportedDatabaseVersion(version: Int, logger: Logger) { logger.error("Version $version of the database is not supported; Only version $SUPPORTED_DATABASE_VERSION is currently supported; Update this application to use the database.") } else { logger.error( - """Version $version of the database is not supported; Only version $SUPPORTED_DATABASE_VERSION is currently supported. + """Version $version of the database is not supported; Only version $SUPPORTED_DATABASE_VERSION is currently supported. |You can update the database to the supported version by either: | - Setting the environment variable '$ENV_VAR_ENABLE_DATABASE_UPDATE_NAME' to '1' to update the database automatically | - Updating the database manually with the database manager utility (https://git.fyloz.dev/color-recipes-explorer/database-manager) @@ -122,5 +122,5 @@ class DatabaseUpdaterProperties { sealed class DatabaseVersioningException(message: String) : Exception(message) { class InvalidUrl(url: String) : DatabaseVersioningException("Invalid database url: $url") class UnsupportedDatabaseVersion(version: Int) : - DatabaseVersioningException("Unsupported database version: $version; Only version $SUPPORTED_DATABASE_VERSION is currently supported") + DatabaseVersioningException("Unsupported database version: $version; Only version $SUPPORTED_DATABASE_VERSION is currently supported") } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/InitialDataLoader.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/InitialDataLoader.kt index 6db1101..0e1ca4b 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/InitialDataLoader.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/InitialDataLoader.kt @@ -11,9 +11,9 @@ import org.springframework.core.annotation.Order @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) class InitialDataLoader( - private val materialTypeService: MaterialTypeService, - private val materialTypeProperties: MaterialTypeProperties + private val materialTypeService: MaterialTypeService, + private val materialTypeProperties: MaterialTypeProperties ) : ApplicationListener { override fun onApplicationEvent(event: ApplicationReadyEvent) = - materialTypeService.saveSystemTypes(materialTypeProperties.systemTypes) + materialTypeService.saveSystemTypes(materialTypeProperties.systemTypes) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt index a780be3..681bf0e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt @@ -51,11 +51,11 @@ import javax.servlet.http.HttpServletResponse @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableConfigurationProperties(SecurityConfigurationProperties::class) class WebSecurityConfig( - val securityConfigurationProperties: SecurityConfigurationProperties, - @Lazy val userDetailsService: EmployeeUserDetailsServiceImpl, - @Lazy val employeeService: EmployeeServiceImpl, - val environment: Environment, - val logger: Logger + val securityConfigurationProperties: SecurityConfigurationProperties, + @Lazy val userDetailsService: EmployeeUserDetailsServiceImpl, + @Lazy val employeeService: EmployeeServiceImpl, + val environment: Environment, + val logger: Logger ) : WebSecurityConfigurerAdapter() { var debugMode = false @@ -65,36 +65,36 @@ class WebSecurityConfig( @Bean fun passwordEncoder() = - BCryptPasswordEncoder() + BCryptPasswordEncoder() @Bean override fun authenticationManagerBean(): AuthenticationManager = - super.authenticationManagerBean() + super.authenticationManagerBean() @Bean fun corsConfigurationSource() = - UrlBasedCorsConfigurationSource().apply { - registerCorsConfiguration("/**", CorsConfiguration().apply { - allowedOrigins = listOf("http://localhost:4200") // Angular development server - allowedMethods = listOf( - HttpMethod.GET.name, - HttpMethod.POST.name, - HttpMethod.PUT.name, - HttpMethod.DELETE.name, - HttpMethod.OPTIONS.name, - HttpMethod.HEAD.name - ) - allowCredentials = true - }.applyPermitDefaultValues()) - } + UrlBasedCorsConfigurationSource().apply { + registerCorsConfiguration("/**", CorsConfiguration().apply { + allowedOrigins = listOf("http://localhost:4200") // Angular development server + allowedMethods = listOf( + HttpMethod.GET.name, + HttpMethod.POST.name, + HttpMethod.PUT.name, + HttpMethod.DELETE.name, + HttpMethod.OPTIONS.name, + HttpMethod.HEAD.name + ) + allowCredentials = true + }.applyPermitDefaultValues()) + } @PostConstruct fun initWebSecurity() { fun createUser( - credentials: SecurityConfigurationProperties.SystemUserCredentials?, - firstName: String, - lastName: String, - permissions: List + credentials: SecurityConfigurationProperties.SystemUserCredentials?, + firstName: String, + lastName: String, + permissions: List ) { Assert.notNull(credentials, "No root user has been defined.") credentials!! @@ -102,14 +102,14 @@ class WebSecurityConfig( Assert.notNull(credentials.password, "The root user has no password defined.") if (!employeeService.existsById(credentials.id!!)) { employeeService.save( - Employee( - id = credentials.id!!, - firstName = firstName, - lastName = lastName, - password = passwordEncoder().encode(credentials.password!!), - isSystemUser = true, - permissions = permissions.toMutableSet() - ) + Employee( + id = credentials.id!!, + firstName = firstName, + lastName = lastName, + password = passwordEncoder().encode(credentials.password!!), + isSystemUser = true, + permissions = permissions.toMutableSet() + ) ) } } @@ -121,37 +121,37 @@ class WebSecurityConfig( override fun configure(http: HttpSecurity) { http - .headers().frameOptions().disable() - .and() - .csrf().disable() - .addFilter( - JwtAuthenticationFilter( - authenticationManager(), - employeeService, - securityConfigurationProperties - ) + .headers().frameOptions().disable() + .and() + .csrf().disable() + .addFilter( + JwtAuthenticationFilter( + authenticationManager(), + employeeService, + securityConfigurationProperties ) - .addFilter( - JwtAuthorizationFilter( - userDetailsService, - securityConfigurationProperties, - authenticationManager() - ) + ) + .addFilter( + JwtAuthorizationFilter( + userDetailsService, + securityConfigurationProperties, + authenticationManager() ) - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) if (!debugMode) { http.authorizeRequests() - .antMatchers("/api/login").permitAll() - .antMatchers("/api/logout").authenticated() - .antMatchers("/api/employee/current").authenticated() - .anyRequest().authenticated() + .antMatchers("/api/login").permitAll() + .antMatchers("/api/logout").authenticated() + .antMatchers("/api/employee/current").authenticated() + .anyRequest().authenticated() } else { http - .cors() - .and() - .authorizeRequests() - .antMatchers("**").permitAll() + .cors() + .and() + .authorizeRequests() + .antMatchers("**").permitAll() } } } @@ -159,9 +159,9 @@ class WebSecurityConfig( @Component class RestAuthenticationEntryPoint : AuthenticationEntryPoint { override fun commence( - request: HttpServletRequest, - response: HttpServletResponse, - authException: AuthenticationException + request: HttpServletRequest, + response: HttpServletResponse, + authException: AuthenticationException ) = response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized") } @@ -170,9 +170,9 @@ const val defaultGroupCookieName = "Default-Group" val blacklistedJwtTokens = mutableListOf() class JwtAuthenticationFilter( - private val authManager: AuthenticationManager, - private val employeeService: EmployeeService, - private val securityConfigurationProperties: SecurityConfigurationProperties + private val authManager: AuthenticationManager, + private val employeeService: EmployeeService, + private val securityConfigurationProperties: SecurityConfigurationProperties ) : UsernamePasswordAuthenticationFilter() { private var debugMode = false @@ -187,10 +187,10 @@ class JwtAuthenticationFilter( } override fun successfulAuthentication( - request: HttpServletRequest, - response: HttpServletResponse, - chain: FilterChain, - authResult: Authentication + request: HttpServletRequest, + response: HttpServletResponse, + chain: FilterChain, + authResult: Authentication ) { val jwtSecret = securityConfigurationProperties.jwtSecret val jwtDuration = securityConfigurationProperties.jwtDuration @@ -201,17 +201,17 @@ class JwtAuthenticationFilter( val expirationMs = System.currentTimeMillis() + jwtDuration!! val expirationDate = Date(expirationMs) val token = Jwts.builder() - .setSubject(employeeId) - .setExpiration(expirationDate) - .signWith(SignatureAlgorithm.HS512, jwtSecret!!.toByteArray()) - .compact() + .setSubject(employeeId) + .setExpiration(expirationDate) + .signWith(SignatureAlgorithm.HS512, jwtSecret!!.toByteArray()) + .compact() response.addHeader("Access-Control-Expose-Headers", "X-Authentication-Expiration") var bearerCookie = - "$authorizationCookieName=Bearer$token; Max-Age=${jwtDuration / 1000}; HttpOnly; SameSite=strict" + "$authorizationCookieName=Bearer$token; Max-Age=${jwtDuration / 1000}; HttpOnly; SameSite=strict" if (!debugMode) bearerCookie += "; Secure;" response.addHeader( - "Set-Cookie", - bearerCookie + "Set-Cookie", + bearerCookie ) response.addHeader(authorizationCookieName, "Bearer $token") response.addHeader("X-Authentication-Expiration", "$expirationMs") @@ -219,9 +219,9 @@ class JwtAuthenticationFilter( } class JwtAuthorizationFilter( - private val userDetailsService: EmployeeUserDetailsServiceImpl, - private val securityConfigurationProperties: SecurityConfigurationProperties, - authenticationManager: AuthenticationManager + private val userDetailsService: EmployeeUserDetailsServiceImpl, + private val securityConfigurationProperties: SecurityConfigurationProperties, + authenticationManager: AuthenticationManager ) : BasicAuthenticationFilter(authenticationManager) { override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) { fun tryLoginFromBearer(): Boolean { @@ -259,10 +259,10 @@ class JwtAuthorizationFilter( Assert.notNull(jwtSecret, "No JWT secret has been defined.") return try { val employeeId = Jwts.parser() - .setSigningKey(jwtSecret!!.toByteArray()) - .parseClaimsJws(token.replace("Bearer", "")) - .body - .subject + .setSigningKey(jwtSecret!!.toByteArray()) + .parseClaimsJws(token.replace("Bearer", "")) + .body + .subject if (employeeId != null) getAuthenticationToken(employeeId) else null } catch (_: ExpiredJwtException) { null diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt index 870bd39..728fb75 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/properties/MaterialTypeProperties.kt @@ -13,9 +13,9 @@ class MaterialTypeProperties { var baseName: String = "" data class MaterialTypeProperty( - var name: String = "", - var prefix: String = "", - var usePercentages: Boolean = false + var name: String = "", + var prefix: String = "", + var usePercentages: Boolean = false ) { fun toMaterialType(): MaterialType { Assert.hasText(name, "A system material type has an empty name") diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt index 03067f2..2732f61 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/exception/RestException.kt @@ -1,7 +1,5 @@ package dev.fyloz.colorrecipesexplorer.exception -import dev.fyloz.colorrecipesexplorer.model.Material -import dev.fyloz.colorrecipesexplorer.model.MaterialQuantityDto import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -14,63 +12,63 @@ import org.springframework.web.context.request.WebRequest import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler abstract class RestException( - val errorCode: String, - val title: String, - val status: HttpStatus, - val details: String, - val extensions: Map = mapOf() + val errorCode: String, + val title: String, + val status: HttpStatus, + val details: String, + val extensions: Map = mapOf() ) : RuntimeException(details) { fun buildExceptionBody() = mapOf( - "type" to errorCode, - "title" to title, - "status" to status.value(), - "detail" to details, + "type" to errorCode, + "title" to title, + "status" to status.value(), + "detail" to details, - *extensions.map { it.key to it.value }.toTypedArray() + *extensions.map { it.key to it.value }.toTypedArray() ) } class NotFoundException( - errorCode: String, - title: String, - details: String, - identifierValue: Any, - identifierName: String = "id" + errorCode: String, + title: String, + details: String, + identifierValue: Any, + identifierName: String = "id" ) : RestException( - errorCode = "notfound-$errorCode-$identifierName", - title = title, - status = HttpStatus.NOT_FOUND, - details = details, - extensions = mapOf( - identifierName to identifierValue - ) + errorCode = "notfound-$errorCode-$identifierName", + title = title, + status = HttpStatus.NOT_FOUND, + details = details, + extensions = mapOf( + identifierName to identifierValue + ) ) class AlreadyExistsException( - errorCode: String, - title: String, - details: String, - identifierValue: Any, - identifierName: String = "id" + errorCode: String, + title: String, + details: String, + identifierValue: Any, + identifierName: String = "id" ) : RestException( - errorCode = "exists-$errorCode-$identifierName", - title = title, - status = HttpStatus.CONFLICT, - details = details, - extensions = mapOf( - identifierName to identifierValue - ) + errorCode = "exists-$errorCode-$identifierName", + title = title, + status = HttpStatus.CONFLICT, + details = details, + extensions = mapOf( + identifierName to identifierValue + ) ) class CannotDeleteException( - errorCode: String, - title: String, - details: String + errorCode: String, + title: String, + details: String ) : RestException( - errorCode = "cannotdelete-$errorCode", - title = title, - status = HttpStatus.CONFLICT, - details = details + errorCode = "cannotdelete-$errorCode", + title = title, + status = HttpStatus.CONFLICT, + details = details ) @ControllerAdvice @@ -81,19 +79,19 @@ class RestResponseEntityExceptionHandler : ResponseEntityExceptionHandler() { finalBody["instance"] = (request as ServletWebRequest).request.requestURI return handleExceptionInternal( - exception, - finalBody, - HttpHeaders(), - exception.status, - request + exception, + finalBody, + HttpHeaders(), + exception.status, + request ) } override fun handleMethodArgumentNotValid( - ex: MethodArgumentNotValidException, - headers: HttpHeaders, - status: HttpStatus, - request: WebRequest + ex: MethodArgumentNotValidException, + headers: HttpHeaders, + status: HttpStatus, + request: WebRequest ): ResponseEntity { val errors = hashMapOf() ex.bindingResult.allErrors.forEach { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt index 1d75903..c6b1fdf 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt @@ -14,49 +14,49 @@ private const val COMPANY_NAME_NULL_MESSAGE = "Un nom est requis" @Entity @Table(name = "company") data class Company( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - @Column(unique = true) - override val name: String + @Column(unique = true) + override val name: String ) : NamedModel open class CompanySaveDto( - @field:NotBlank(message = COMPANY_NAME_NULL_MESSAGE) - val name: String + @field:NotBlank(message = COMPANY_NAME_NULL_MESSAGE) + val name: String ) : EntityDto { override fun toEntity(): Company = Company(null, name) } open class CompanyUpdateDto( - @field:NotNull(message = COMPANY_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = COMPANY_ID_NULL_MESSAGE) + val id: Long, - @field:NullOrNotBlank(message = COMPANY_NAME_NULL_MESSAGE) - val name: String? + @field:NullOrNotBlank(message = COMPANY_NAME_NULL_MESSAGE) + val name: String? ) : EntityDto { override fun toEntity(): Company = Company(id, name ?: "") } // ==== DSL ==== fun company( - id: Long? = null, - name: String = "name", - op: Company.() -> Unit = {} + id: Long? = null, + name: String = "name", + op: Company.() -> Unit = {} ) = Company(id, name).apply(op) fun companySaveDto( - name: String = "name", - op: CompanySaveDto.() -> Unit = {} + name: String = "name", + op: CompanySaveDto.() -> Unit = {} ) = CompanySaveDto(name).apply(op) fun companyUpdateDto( - id: Long = 0L, - name: String? = "name", - op: CompanyUpdateDto.() -> Unit = {} + id: Long = 0L, + name: String? = "name", + op: CompanyUpdateDto.() -> Unit = {} ) = CompanyUpdateDto(id, name).apply(op) // ==== Exceptions ==== @@ -66,34 +66,42 @@ private const val COMPANY_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete company private const val COMPANY_EXCEPTION_ERROR_CODE = "company" fun companyIdNotFoundException(id: Long) = - NotFoundException( - COMPANY_EXCEPTION_ERROR_CODE, - COMPANY_NOT_FOUND_EXCEPTION_TITLE, - "A company with the id $id could not be found", - id - ) + NotFoundException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_NOT_FOUND_EXCEPTION_TITLE, + "A company with the id $id could not be found", + id + ) fun companyNameNotFoundException(name: String) = - NotFoundException( - COMPANY_EXCEPTION_ERROR_CODE, - COMPANY_NOT_FOUND_EXCEPTION_TITLE, - "A company with the name $name could not be found", - name, - "name" - ) + NotFoundException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_NOT_FOUND_EXCEPTION_TITLE, + "A company with the name $name could not be found", + name, + "name" + ) + +fun companyIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_ALREADY_EXISTS_EXCEPTION_TITLE, + "A company with the id $id already exists", + id + ) fun companyNameAlreadyExistsException(name: String) = - AlreadyExistsException( - COMPANY_EXCEPTION_ERROR_CODE, - COMPANY_ALREADY_EXISTS_EXCEPTION_TITLE, - "A company with the name $name already exists", - name, - "name" - ) + AlreadyExistsException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_ALREADY_EXISTS_EXCEPTION_TITLE, + "A company with the name $name already exists", + name, + "name" + ) fun cannotDeleteCompany(company: Company) = - CannotDeleteException( - COMPANY_EXCEPTION_ERROR_CODE, - COMPANY_CANNOT_DELETE_EXCEPTION_TITLE, - "Cannot delete the company ${company.name} because one or more recipes depends on it" - ) + CannotDeleteException( + COMPANY_EXCEPTION_ERROR_CODE, + COMPANY_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the company ${company.name} because one or more recipes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt index b65e73b..fa564eb 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Employee.kt @@ -25,51 +25,51 @@ private const val EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE = "Le mot de passe doit co @Entity @Table(name = "employee") data class Employee( - @Id - override val id: Long, + @Id + override val id: Long, - @Column(name = "first_name") - val firstName: String = "", + @Column(name = "first_name") + val firstName: String = "", - @Column(name = "last_name") - val lastName: String = "", + @Column(name = "last_name") + val lastName: String = "", - @JsonIgnore - val password: String = "", + @JsonIgnore + val password: String = "", - @JsonIgnore - @Column(name = "default_group_user") - val isDefaultGroupUser: Boolean = false, + @JsonIgnore + @Column(name = "default_group_user") + val isDefaultGroupUser: Boolean = false, - @JsonIgnore - @Column(name = "system_user") - val isSystemUser: Boolean = false, + @JsonIgnore + @Column(name = "system_user") + val isSystemUser: Boolean = false, - @ManyToOne - @JoinColumn(name = "group_id") - @Fetch(FetchMode.SELECT) - var group: EmployeeGroup? = null, + @ManyToOne + @JoinColumn(name = "group_id") + @Fetch(FetchMode.SELECT) + var group: EmployeeGroup? = null, - @Enumerated(EnumType.STRING) - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "employee_permission", joinColumns = [JoinColumn(name = "employee_id")]) - @Column(name = "permission") - @Fetch(FetchMode.SUBSELECT) - @get:JsonProperty("explicitPermissions") - val permissions: MutableSet = mutableSetOf(), + @Enumerated(EnumType.STRING) + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "employee_permission", joinColumns = [JoinColumn(name = "employee_id")]) + @Column(name = "permission") + @Fetch(FetchMode.SUBSELECT) + @get:JsonProperty("explicitPermissions") + val permissions: MutableSet = mutableSetOf(), - @Column(name = "last_login_time") - var lastLoginTime: LocalDateTime? = null + @Column(name = "last_login_time") + var lastLoginTime: LocalDateTime? = null ) : Model { @get:JsonProperty("permissions") val flatPermissions: Set get() = permissions - .flatMap { it.flat() } - .filter { !it.deprecated } - .toMutableSet() - .apply { - if (group != null) this.addAll(group!!.flatPermissions) - } + .flatMap { it.flat() } + .filter { !it.deprecated } + .toMutableSet() + .apply { + if (group != null) this.addAll(group!!.flatPermissions) + } @get:JsonIgnore val authorities: Set @@ -78,86 +78,86 @@ data class Employee( /** DTO for creating employees. Allows a [password] a [groupId]. */ open class EmployeeSaveDto( - @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) + val id: Long, - @field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) - val firstName: String, + @field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) + val firstName: String, - @field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) - val lastName: String, + @field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) + val lastName: String, - @field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE) - @field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE) - val password: String, + @field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE) + @field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE) + val password: String, - val groupId: Long?, + val groupId: Long?, - @Enumerated(EnumType.STRING) - val permissions: MutableSet = mutableSetOf() + @Enumerated(EnumType.STRING) + val permissions: MutableSet = mutableSetOf() ) : EntityDto open class EmployeeUpdateDto( - @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE) + val id: Long, - @field:NullOrNotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) - val firstName: String?, + @field:NullOrNotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE) + val firstName: String?, - @field:NullOrNotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) - val lastName: String?, + @field:NullOrNotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE) + val lastName: String?, - val groupId: Long?, + val groupId: Long?, - @Enumerated(EnumType.STRING) - val permissions: Set? + @Enumerated(EnumType.STRING) + val permissions: Set? ) : EntityDto data class EmployeeLoginRequest(val id: Long, val password: String) // ==== DSL ==== fun employee( - passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), - id: Long = 0L, - firstName: String = "firstName", - lastName: String = "lastName", - password: String = passwordEncoder.encode("password"), - isDefaultGroupUser: Boolean = false, - isSystemUser: Boolean = false, - group: EmployeeGroup? = null, - permissions: MutableSet = mutableSetOf(), - lastLoginTime: LocalDateTime? = null, - op: Employee.() -> Unit = {} + passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), + id: Long = 0L, + firstName: String = "firstName", + lastName: String = "lastName", + password: String = passwordEncoder.encode("password"), + isDefaultGroupUser: Boolean = false, + isSystemUser: Boolean = false, + group: EmployeeGroup? = null, + permissions: MutableSet = mutableSetOf(), + lastLoginTime: LocalDateTime? = null, + op: Employee.() -> Unit = {} ) = Employee( - id, - firstName, - lastName, - password, - isDefaultGroupUser, - isSystemUser, - group, - permissions, - lastLoginTime + id, + firstName, + lastName, + password, + isDefaultGroupUser, + isSystemUser, + group, + permissions, + lastLoginTime ).apply(op) fun employeeSaveDto( - passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), - id: Long = 0L, - firstName: String = "firstName", - lastName: String = "lastName", - password: String = passwordEncoder.encode("password"), - groupId: Long? = null, - permissions: MutableSet = mutableSetOf(), - op: EmployeeSaveDto.() -> Unit = {} + passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(), + id: Long = 0L, + firstName: String = "firstName", + lastName: String = "lastName", + password: String = passwordEncoder.encode("password"), + groupId: Long? = null, + permissions: MutableSet = mutableSetOf(), + op: EmployeeSaveDto.() -> Unit = {} ) = EmployeeSaveDto(id, firstName, lastName, password, groupId, permissions).apply(op) fun employeeUpdateDto( - id: Long = 0L, - firstName: String = "firstName", - lastName: String = "lastName", - groupId: Long? = null, - permissions: MutableSet = mutableSetOf(), - op: EmployeeUpdateDto.() -> Unit = {} + id: Long = 0L, + firstName: String = "firstName", + lastName: String = "lastName", + groupId: Long? = null, + permissions: MutableSet = mutableSetOf(), + op: EmployeeUpdateDto.() -> Unit = {} ) = EmployeeUpdateDto(id, firstName, lastName, groupId, permissions).apply(op) // ==== Exceptions ==== @@ -166,26 +166,26 @@ private const val EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE = "Employee already ex private const val EMPLOYEE_EXCEPTION_ERROR_CODE = "employee" fun employeeIdNotFoundException(id: Long) = - NotFoundException( - EMPLOYEE_EXCEPTION_ERROR_CODE, - EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, - "An employee with the id $id could not be found", - id - ) + NotFoundException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, + "An employee with the id $id could not be found", + id + ) fun employeeIdAlreadyExistsException(id: Long) = - AlreadyExistsException( - EMPLOYEE_EXCEPTION_ERROR_CODE, - EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, - "An employee with the id $id already exists", - id - ) + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee with the id $id already exists", + id + ) fun employeeFullNameAlreadyExistsException(firstName: String, lastName: String) = - AlreadyExistsException( - EMPLOYEE_EXCEPTION_ERROR_CODE, - EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, - "An employee with the name '$firstName $lastName' already exists", - "$firstName $lastName", - "fullName" - ) + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee with the name '$firstName $lastName' already exists", + "$firstName $lastName", + "fullName" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt index 32e91bf..a0d467e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeeGroup.kt @@ -19,74 +19,74 @@ private const val GROUP_PERMISSIONS_EMPTY_MESSAGE = "Au moins une permission est @Entity @Table(name = "employee_group") data class EmployeeGroup( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override var id: Long? = null, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override var id: Long? = null, - @Column(unique = true) - override val name: String = "", + @Column(unique = true) + override val name: String = "", - @Enumerated(EnumType.STRING) - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "group_permission", joinColumns = [JoinColumn(name = "group_id")]) - @Column(name = "permission") - @Fetch(FetchMode.SUBSELECT) - @get:JsonProperty("explicitPermissions") - val permissions: MutableSet = mutableSetOf(), + @Enumerated(EnumType.STRING) + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "group_permission", joinColumns = [JoinColumn(name = "group_id")]) + @Column(name = "permission") + @Fetch(FetchMode.SUBSELECT) + @get:JsonProperty("explicitPermissions") + val permissions: MutableSet = mutableSetOf(), ) : NamedModel { @get:JsonProperty("permissions") val flatPermissions: Set get() = this.permissions - .flatMap { it.flat() } - .filter { !it.deprecated } - .toSet() + .flatMap { it.flat() } + .filter { !it.deprecated } + .toSet() } open class EmployeeGroupSaveDto( - @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) - @field:Size(min = 3) - val name: String, + @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) + @field:Size(min = 3) + val name: String, - @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) - val permissions: MutableSet + @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) + val permissions: MutableSet ) : EntityDto { override fun toEntity(): EmployeeGroup = - EmployeeGroup(null, name, permissions) + EmployeeGroup(null, name, permissions) } open class EmployeeGroupUpdateDto( - @field:NotNull(message = GROUP_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = GROUP_ID_NULL_MESSAGE) + val id: Long, - @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) - @field:Size(min = 3) - val name: String, + @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) + @field:Size(min = 3) + val name: String, - @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) - val permissions: MutableSet + @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) + val permissions: MutableSet ) : EntityDto { override fun toEntity(): EmployeeGroup = - EmployeeGroup(id, name, permissions) + EmployeeGroup(id, name, permissions) } fun employeeGroup( - id: Long? = null, - name: String = "name", - permissions: MutableSet = mutableSetOf(), - op: EmployeeGroup.() -> Unit = {} + id: Long? = null, + name: String = "name", + permissions: MutableSet = mutableSetOf(), + op: EmployeeGroup.() -> Unit = {} ) = EmployeeGroup(id, name, permissions).apply(op) fun employeeGroupSaveDto( - name: String = "name", - permissions: MutableSet = mutableSetOf(), - op: EmployeeGroupSaveDto.() -> Unit = {} + name: String = "name", + permissions: MutableSet = mutableSetOf(), + op: EmployeeGroupSaveDto.() -> Unit = {} ) = EmployeeGroupSaveDto(name, permissions).apply(op) fun employeeGroupUpdateDto( - id: Long = 0L, - name: String = "name", - permissions: MutableSet = mutableSetOf(), - op: EmployeeGroupUpdateDto.() -> Unit = {} + id: Long = 0L, + name: String = "name", + permissions: MutableSet = mutableSetOf(), + op: EmployeeGroupUpdateDto.() -> Unit = {} ) = EmployeeGroupUpdateDto(id, name, permissions).apply(op) // ==== Exceptions ==== @@ -95,35 +95,42 @@ private const val EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE = "Employee group alre private const val EMPLOYEE_EXCEPTION_ERROR_CODE = "employeegroup" class NoDefaultGroupException : RestException( - "nodefaultgroup", - "No default group", - HttpStatus.NOT_FOUND, - "No default group cookie is defined in the current request" + "nodefaultgroup", + "No default group", + HttpStatus.NOT_FOUND, + "No default group cookie is defined in the current request" ) fun employeeGroupIdNotFoundException(id: Long) = - NotFoundException( - EMPLOYEE_EXCEPTION_ERROR_CODE, - EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, - "An employee group with the id $id could not be found", - id - ) + NotFoundException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, + "An employee group with the id $id could not be found", + id + ) fun employeeGroupNameNotFoundException(name: String) = - NotFoundException( - EMPLOYEE_EXCEPTION_ERROR_CODE, - EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, - "An employee group with the name $name could not be found", - name, - "name" - ) + NotFoundException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE, + "An employee group with the name $name could not be found", + name, + "name" + ) + +fun employeeGroupIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee group with the id $id already exists", + id, + ) fun employeeGroupNameAlreadyExistsException(name: String) = - AlreadyExistsException( - EMPLOYEE_EXCEPTION_ERROR_CODE, - EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, - "An employee group with the name $name already exists", - name, - "name" - ) - + AlreadyExistsException( + EMPLOYEE_EXCEPTION_ERROR_CODE, + EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE, + "An employee group with the name $name already exists", + name, + "name" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt index 3bcfd42..a12ae7b 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt @@ -4,8 +4,8 @@ import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority enum class EmployeePermission( - val impliedPermissions: List = listOf(), - val deprecated: Boolean = false + val impliedPermissions: List = listOf(), + val deprecated: Boolean = false ) { VIEW_RECIPES, VIEW_CATALOG, @@ -32,17 +32,17 @@ enum class EmployeePermission( REMOVE_CATALOG(listOf(REMOVE_MATERIALS, REMOVE_MATERIAL_TYPES, REMOVE_COMPANIES)), ADMIN( - listOf( - EDIT_CATALOG, + listOf( + EDIT_CATALOG, - REMOVE_RECIPES, - REMOVE_USERS, - REMOVE_CATALOG, + REMOVE_RECIPES, + REMOVE_USERS, + REMOVE_CATALOG, - PRINT_MIXES, - ADD_TO_INVENTORY, - DEDUCT_FROM_INVENTORY - ) + PRINT_MIXES, + ADD_TO_INVENTORY, + DEDUCT_FROM_INVENTORY + ) ), // deprecated permissions diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt index c28e1cd..3b060a1 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt @@ -24,103 +24,103 @@ private const val MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE = "La quantité do @Entity @Table(name = "material") data class Material( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - @Column(unique = true) - override var name: String, + @Column(unique = true) + override var name: String, - @Column(name = "inventory_quantity") - var inventoryQuantity: Float, + @Column(name = "inventory_quantity") + var inventoryQuantity: Float, - @Column(name = "mix_type") - val isMixType: Boolean, + @Column(name = "mix_type") + val isMixType: Boolean, - @ManyToOne - @JoinColumn(name = "material_type_id") - var materialType: MaterialType? + @ManyToOne + @JoinColumn(name = "material_type_id") + var materialType: MaterialType? ) : NamedModel open class MaterialSaveDto( - @field:NotBlank(message = MATERIAL_NAME_NULL_MESSAGE) - val name: String, + @field:NotBlank(message = MATERIAL_NAME_NULL_MESSAGE) + val name: String, - @field:NotNull(message = MATERIAL_INVENTORY_QUANTITY_NULL_MESSAGE) - @field:Min(value = 0, message = MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE) - val inventoryQuantity: Float, + @field:NotNull(message = MATERIAL_INVENTORY_QUANTITY_NULL_MESSAGE) + @field:Min(value = 0, message = MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE) + val inventoryQuantity: Float, - @field:NotNull(message = MATERIAL_TYPE_NULL_MESSAGE) - val materialTypeId: Long, + @field:NotNull(message = MATERIAL_TYPE_NULL_MESSAGE) + val materialTypeId: Long, - val simdutFile: MultipartFile? = null + val simdutFile: MultipartFile? = null ) : EntityDto open class MaterialUpdateDto( - @field:NotNull(message = MATERIAL_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = MATERIAL_ID_NULL_MESSAGE) + val id: Long, - @field:NullOrNotBlank(message = MATERIAL_NAME_NULL_MESSAGE) - val name: String?, + @field:NullOrNotBlank(message = MATERIAL_NAME_NULL_MESSAGE) + val name: String?, - @field:NullOrSize(min = 0, message = MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE) - val inventoryQuantity: Float?, + @field:NullOrSize(min = 0, message = MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE) + val inventoryQuantity: Float?, - val materialTypeId: Long?, + val materialTypeId: Long?, - val simdutFile: MultipartFile? = null + val simdutFile: MultipartFile? = null ) : EntityDto data class MaterialQuantityDto( - @field:NotNull(message = MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE) - val material: Long, + @field:NotNull(message = MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE) + val material: Long, - @field:NotNull(message = MATERIAL_QUANTITY_QUANTITY_NULL_MESSAGE) - @field:Min(value = 0, message = MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE) - val quantity: Float + @field:NotNull(message = MATERIAL_QUANTITY_QUANTITY_NULL_MESSAGE) + @field:Min(value = 0, message = MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE) + val quantity: Float ) // === DSL === fun material( - id: Long? = null, - name: String = "name", - inventoryQuantity: Float = 0f, - isMixType: Boolean = false, - materialType: MaterialType? = materialType(), - op: Material.() -> Unit = {} + id: Long? = null, + name: String = "name", + inventoryQuantity: Float = 0f, + isMixType: Boolean = false, + materialType: MaterialType? = materialType(), + op: Material.() -> Unit = {} ) = Material(id, name, inventoryQuantity, isMixType, materialType).apply(op) fun material( - material: Material, - id: Long? = null, - name: String? = null, + material: Material, + id: Long? = null, + name: String? = null, ) = Material( - id ?: material.id, name - ?: material.name, material.inventoryQuantity, material.isMixType, material.materialType + id ?: material.id, name + ?: material.name, material.inventoryQuantity, material.isMixType, material.materialType ) fun materialSaveDto( - name: String = "name", - inventoryQuantity: Float = 0f, - materialTypeId: Long = 0L, - simdutFile: MultipartFile? = null, - op: MaterialSaveDto.() -> Unit = {} + name: String = "name", + inventoryQuantity: Float = 0f, + materialTypeId: Long = 0L, + simdutFile: MultipartFile? = null, + op: MaterialSaveDto.() -> Unit = {} ) = MaterialSaveDto(name, inventoryQuantity, materialTypeId, simdutFile).apply(op) fun materialUpdateDto( - id: Long = 0L, - name: String? = "name", - inventoryQuantity: Float? = 0f, - materialTypeId: Long? = 0L, - simdutFile: MultipartFile? = null, - op: MaterialUpdateDto.() -> Unit = {} + id: Long = 0L, + name: String? = "name", + inventoryQuantity: Float? = 0f, + materialTypeId: Long? = 0L, + simdutFile: MultipartFile? = null, + op: MaterialUpdateDto.() -> Unit = {} ) = MaterialUpdateDto(id, name, inventoryQuantity, materialTypeId, simdutFile).apply(op) fun materialQuantityDto( - materialId: Long, - quantity: Float, - op: MaterialQuantityDto.() -> Unit = {} + materialId: Long, + quantity: Float, + op: MaterialQuantityDto.() -> Unit = {} ) = MaterialQuantityDto(materialId, quantity).apply(op) // ==== Exceptions ==== @@ -130,34 +130,42 @@ private const val MATERIAL_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete materi private const val MATERIAL_EXCEPTION_ERROR_CODE = "material" fun materialIdNotFoundException(id: Long) = - NotFoundException( - MATERIAL_EXCEPTION_ERROR_CODE, - MATERIAL_NOT_FOUND_EXCEPTION_TITLE, - "A material with the id $id could not be found", - id - ) + NotFoundException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_NOT_FOUND_EXCEPTION_TITLE, + "A material with the id $id could not be found", + id + ) fun materialNameNotFoundException(name: String) = - NotFoundException( - MATERIAL_EXCEPTION_ERROR_CODE, - MATERIAL_NOT_FOUND_EXCEPTION_TITLE, - "A material with the name $name could not be found", - name, - "name" - ) + NotFoundException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_NOT_FOUND_EXCEPTION_TITLE, + "A material with the name $name could not be found", + name, + "name" + ) + +fun materialIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE, + "A material with the id $id already exists", + id + ) fun materialNameAlreadyExistsException(name: String) = - AlreadyExistsException( - MATERIAL_EXCEPTION_ERROR_CODE, - MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE, - "A material with the name $name already exists", - name, - "name" - ) + AlreadyExistsException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE, + "A material with the name $name already exists", + name, + "name" + ) fun cannotDeleteMaterialException(material: Material) = - CannotDeleteException( - MATERIAL_EXCEPTION_ERROR_CODE, - MATERIAL_CANNOT_DELETE_EXCEPTION_TITLE, - "Cannot delete the material ${material.name} because one or more recipes depends on it" - ) + CannotDeleteException( + MATERIAL_EXCEPTION_ERROR_CODE, + MATERIAL_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the material ${material.name} because one or more recipes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt index 30beff6..c8f5e80 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt @@ -19,90 +19,90 @@ private const val MATERIAL_TYPE_PREFIX_SIZE_MESSAGE = "Le préfixe doit faire ex @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) + override 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 + @Column(name = "system_type") + @ColumnDefault("false") + val systemType: Boolean = false ) : NamedModel open class MaterialTypeSaveDto( - @field:NotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE) - val name: String, + @field:NotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE) + val name: String, - @field:NotBlank(message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE) - @field:Size(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_SIZE_MESSAGE) - val prefix: String, + @field:NotBlank(message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE) + @field:Size(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_SIZE_MESSAGE) + val prefix: String, - val usePercentages: Boolean = false + val usePercentages: Boolean = false ) : EntityDto { override fun toEntity(): MaterialType = - MaterialType(null, name, prefix, usePercentages) + MaterialType(null, name, prefix, usePercentages) } open class MaterialTypeUpdateDto( - @field:NotNull(message = MATERIAL_TYPE_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = MATERIAL_TYPE_ID_NULL_MESSAGE) + val id: Long, - @field:NullOrNotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE) - val name: String?, + @field:NullOrNotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE) + val name: String?, - @field:NullOrSize(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE) - val prefix: String? + @field:NullOrSize(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE) + val prefix: String? ) : EntityDto { override fun toEntity(): MaterialType = - MaterialType(id, name ?: "", prefix ?: "") + MaterialType(id, name ?: "", prefix ?: "") } // ==== 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 = {} + name: String = "name", + prefix: String = "PRE", + usePercentages: Boolean = false, + op: MaterialTypeSaveDto.() -> Unit = {} ) = MaterialTypeSaveDto(name, prefix, usePercentages).apply(op) fun materialTypeUpdateDto( - id: Long = 0L, - name: String? = null, - prefix: String? = null, - op: MaterialTypeUpdateDto.() -> Unit = {} + id: Long = 0L, + name: String? = null, + prefix: String? = null, + op: MaterialTypeUpdateDto.() -> Unit = {} ) = MaterialTypeUpdateDto(id, name, prefix).apply(op) // ==== Exceptions ==== @@ -112,43 +112,51 @@ private const val MATERIAL_TYPE_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete m 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 - ) + 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" - ) + 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" - ) + 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" - ) + AlreadyExistsException( + MATERIAL_TYPE_EXCEPTION_ERROR_CODE, + MATERIAL_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, + "A material type with the prefix $prefix already exists", + prefix, + "prefix" + ) 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" - ) + 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" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt index 6178a54..4dcb08f 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt @@ -1,6 +1,7 @@ package dev.fyloz.colorrecipesexplorer.model import com.fasterxml.jackson.annotation.JsonIgnore +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank @@ -21,125 +22,134 @@ private const val MIX_DEDUCT_RATION_NEGATIVE_MESSAGE = "Le ratio doit être éga @Entity @Table(name = "mix") data class Mix( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - var location: String?, + var location: String?, - @JsonIgnore - @ManyToOne - @JoinColumn(name = "recipe_id") - val recipe: Recipe, + @JsonIgnore + @ManyToOne + @JoinColumn(name = "recipe_id") + val recipe: Recipe, - @ManyToOne - @JoinColumn(name = "mix_type_id") - var mixType: MixType, + @ManyToOne + @JoinColumn(name = "mix_type_id") + var mixType: MixType, - @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) - @JoinColumn(name = "mix_id") - var mixMaterials: MutableSet, + @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) + @JoinColumn(name = "mix_id") + var mixMaterials: MutableSet, ) : Model open class MixSaveDto( - @field:NotBlank(message = MIX_NAME_NULL_MESSAGE) - val name: String, + @field:NotBlank(message = MIX_NAME_NULL_MESSAGE) + val name: String, - @field:NotNull(message = MIX_RECIPE_NULL_MESSAGE) - val recipeId: Long, + @field:NotNull(message = MIX_RECIPE_NULL_MESSAGE) + val recipeId: Long, - @field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE) - val materialTypeId: Long, + @field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE) + val materialTypeId: Long, - val mixMaterials: Set? + val mixMaterials: Set? ) : EntityDto { override fun toEntity(): Mix = throw UnsupportedOperationException() } open class MixUpdateDto( - @field:NotNull(message = MIX_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = MIX_ID_NULL_MESSAGE) + val id: Long, - @field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE) - val name: String?, + @field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE) + val name: String?, - val materialTypeId: Long?, + val materialTypeId: Long?, - var mixMaterials: Set? + var mixMaterials: Set? ) : EntityDto { override fun toEntity(): Mix = throw UnsupportedOperationException() } data class MixDeductDto( - @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) + val id: Long, - @field:NotNull(message = MIX_DEDUCT_RATIO_NULL_MESSAGE) - @field:Min(value = 0, message = MIX_DEDUCT_RATION_NEGATIVE_MESSAGE) - val ratio: Float + @field:NotNull(message = MIX_DEDUCT_RATIO_NULL_MESSAGE) + @field:Min(value = 0, message = MIX_DEDUCT_RATION_NEGATIVE_MESSAGE) + val ratio: Float ) data class MixLocationDto( - @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) - val mixId: Long, + @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) + val mixId: Long, - val location: String? + val location: String? ) // ==== DSL ==== fun mix( - id: Long? = null, - location: String? = "location", - recipe: Recipe = recipe(), - mixType: MixType = mixType(), - mixMaterials: MutableSet = mutableSetOf(), - op: Mix.() -> Unit = {} + id: Long? = null, + location: String? = "location", + recipe: Recipe = recipe(), + mixType: MixType = mixType(), + mixMaterials: MutableSet = mutableSetOf(), + op: Mix.() -> Unit = {} ) = Mix(id, location, recipe, mixType, mixMaterials).apply(op) fun mixSaveDto( - name: String = "name", - recipeId: Long = 0L, - materialTypeId: Long = 0L, - mixMaterials: Set? = setOf(), - op: MixSaveDto.() -> Unit = {} + name: String = "name", + recipeId: Long = 0L, + materialTypeId: Long = 0L, + mixMaterials: Set? = setOf(), + op: MixSaveDto.() -> Unit = {} ) = MixSaveDto(name, recipeId, materialTypeId, mixMaterials).apply(op) fun mixUpdateDto( - id: Long = 0L, - name: String? = "name", - materialTypeId: Long? = 0L, - mixMaterials: Set? = setOf(), - op: MixUpdateDto.() -> Unit = {} + id: Long = 0L, + name: String? = "name", + materialTypeId: Long? = 0L, + mixMaterials: Set? = setOf(), + op: MixUpdateDto.() -> Unit = {} ) = MixUpdateDto(id, name, materialTypeId, mixMaterials).apply(op) fun mixRatio( - id: Long = 0L, - ratio: Float = 1f, - op: MixDeductDto.() -> Unit = {} + id: Long = 0L, + ratio: Float = 1f, + op: MixDeductDto.() -> Unit = {} ) = MixDeductDto(id, ratio).apply(op) fun mixLocationDto( - mixId: Long = 0L, - location: String? = "location", - op: MixLocationDto.() -> Unit = {} + mixId: Long = 0L, + location: String? = "location", + op: MixLocationDto.() -> Unit = {} ) = MixLocationDto(mixId, location).apply(op) // ==== Exceptions ==== private const val MIX_NOT_FOUND_EXCEPTION_TITLE = "Mix not found" +private const val MIX_ALREADY_EXISTS_EXCEPTION_TITLE = "Mix already exists" private const val MIX_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete mix" private const val MIX_EXCEPTION_ERROR_CODE = "mix" fun mixIdNotFoundException(id: Long) = - NotFoundException( - MIX_EXCEPTION_ERROR_CODE, - MIX_NOT_FOUND_EXCEPTION_TITLE, - "A mix with the id $id could not be found", - id - ) + NotFoundException( + MIX_EXCEPTION_ERROR_CODE, + MIX_NOT_FOUND_EXCEPTION_TITLE, + "A mix with the id $id could not be found", + id + ) + +fun mixIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + MIX_EXCEPTION_ERROR_CODE, + MIX_ALREADY_EXISTS_EXCEPTION_TITLE, + "A mix with the id $id already exists", + id + ) fun cannotDeleteMixException(mix: Mix) = - CannotDeleteException( - MIX_EXCEPTION_ERROR_CODE, - MIX_CANNOT_DELETE_EXCEPTION_TITLE, - "Cannot delete the mix ${mix.mixType.name} because one or more mixes depends on it" - ) + CannotDeleteException( + MIX_EXCEPTION_ERROR_CODE, + MIX_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the mix ${mix.mixType.name} because one or more mixes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt index 90d9614..090a9bd 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import javax.persistence.* import javax.validation.constraints.Min @@ -12,54 +13,63 @@ private const val MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE = "La quantité ne @Entity @Table(name = "mix_material") data class MixMaterial( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - @ManyToOne - @JoinColumn(name = "material_id") - val material: Material, + @ManyToOne + @JoinColumn(name = "material_id") + val material: Material, - var quantity: Float, + var quantity: Float, - var position: Int + var position: Int ) : Model data class MixMaterialDto( - @field:NotNull(message = MIX_MATERIAL_DTO_MATERIAL_ID_NULL_MESSAGE) - val materialId: Long, + @field:NotNull(message = MIX_MATERIAL_DTO_MATERIAL_ID_NULL_MESSAGE) + val materialId: Long, - @field:NotNull(message = MIX_MATERIAL_DTO_QUANTITY_NULL_MESSAGE) - @field:Min(value = 0, message = MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE) - val quantity: Float, + @field:NotNull(message = MIX_MATERIAL_DTO_QUANTITY_NULL_MESSAGE) + @field:Min(value = 0, message = MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE) + val quantity: Float, - val position: Int + val position: Int ) // ==== DSL ==== fun mixMaterial( - id: Long? = null, - material: Material = material(), - quantity: Float = 0f, - position: Int = 0, - op: MixMaterial.() -> Unit = {} + id: Long? = null, + material: Material = material(), + quantity: Float = 0f, + position: Int = 0, + op: MixMaterial.() -> Unit = {} ) = MixMaterial(id, material, quantity, position).apply(op) fun mixMaterialDto( - materialId: Long = 0L, - quantity: Float = 0f, - position: Int = 0, - op: MixMaterialDto.() -> Unit = {} + materialId: Long = 0L, + quantity: Float = 0f, + position: Int = 0, + op: MixMaterialDto.() -> Unit = {} ) = MixMaterialDto(materialId, quantity, position).apply(op) // ==== Exceptions ==== private const val MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE = "Mix material not found" +private const val MIX_MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE = "Mix material already exists" private const val MIX_MATERIAL_EXCEPTION_ERROR_CODE = "mixmaterial" fun mixMaterialIdNotFoundException(id: Long) = - NotFoundException( - MIX_MATERIAL_EXCEPTION_ERROR_CODE, - MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE, - "A mix material with the id $id could not be found", - id - ) + NotFoundException( + MIX_MATERIAL_EXCEPTION_ERROR_CODE, + MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE, + "A mix material with the id $id could not be found", + id + ) + +fun mixMaterialIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + MIX_MATERIAL_EXCEPTION_ERROR_CODE, + MIX_MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE, + "A mix material with the id $id already exists", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt index 8fa0bfc..6281099 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixType.kt @@ -10,34 +10,34 @@ import javax.persistence.* @Entity @Table(name = "mix_type") data class MixType( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - @Column(unique = true) - override var name: String, + @Column(unique = true) + override var name: String, - @OneToOne(cascade = [CascadeType.ALL]) - @JoinColumn(name = "material_id") - var material: Material + @OneToOne(cascade = [CascadeType.ALL]) + @JoinColumn(name = "material_id") + var material: Material ) : NamedModel // ==== DSL ==== fun mixType( - id: Long? = null, - name: String = "name", - material: Material = material(), - op: MixType.() -> Unit = {} + id: Long? = null, + name: String = "name", + material: Material = material(), + op: MixType.() -> Unit = {} ) = MixType(id, name, material).apply(op) fun mixType( - name: String = "name", - materialType: MaterialType = materialType(), - op: MixType.() -> Unit = {} + name: String = "name", + materialType: MaterialType = materialType(), + op: MixType.() -> Unit = {} ) = mixType( - id = null, - name, - material = material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType) + id = null, + name, + material = material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType) ).apply(op) // ==== Exceptions ==== @@ -47,46 +47,54 @@ private const val MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete mix ty private const val MIX_TYPE_EXCEPTION_ERROR_CODE = "mixtype" class MixTypeNameAndMaterialTypeNotFoundException(name: String, materialType: MaterialType) : - RestException( - "notfound-mixtype-namematerialtype", - MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, - HttpStatus.NOT_FOUND, - "A mix type with the name $name and material type ${materialType.name} could not be found", - mapOf( - "name" to name, - "materialType" to materialType.name - ) + RestException( + "notfound-mixtype-namematerialtype", + MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, + HttpStatus.NOT_FOUND, + "A mix type with the name $name and material type ${materialType.name} could not be found", + mapOf( + "name" to name, + "materialType" to materialType.name ) + ) fun mixTypeIdNotFoundException(id: Long) = - NotFoundException( - MIX_TYPE_EXCEPTION_ERROR_CODE, - MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, - "A mix type with the id $id could not be found", - id - ) + NotFoundException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, + "A mix type with the id $id could not be found", + id + ) + +fun mixTypeIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, + "A mix type with the id $id already exists", + id + ) fun mixTypeNameNotFoundException(name: String) = - NotFoundException( - MIX_TYPE_EXCEPTION_ERROR_CODE, - MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, - "A mix type with the name $name could not be found", - name, - "name" - ) + NotFoundException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_NOT_FOUND_EXCEPTION_TITLE, + "A mix type with the name $name could not be found", + name, + "name" + ) fun mixTypeNameAlreadyExistsException(name: String) = - AlreadyExistsException( - MIX_TYPE_EXCEPTION_ERROR_CODE, - MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, - "A mix type with the name $name already exists", - name, - "name" - ) + AlreadyExistsException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_ALREADY_EXISTS_EXCEPTION_TITLE, + "A mix type with the name $name already exists", + name, + "name" + ) fun cannotDeleteMixTypeException(mixType: MixType) = - CannotDeleteException( - MIX_TYPE_EXCEPTION_ERROR_CODE, - MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE, - "Cannot delete the mix type ${mixType.name} because one or more mixes depends on it" - ) + CannotDeleteException( + MIX_TYPE_EXCEPTION_ERROR_CODE, + MIX_TYPE_CANNOT_DELETE_EXCEPTION_TITLE, + "Cannot delete the mix type ${mixType.name} because one or more mixes depends on it" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt index b047f8f..8d32a87 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt @@ -1,6 +1,7 @@ package dev.fyloz.colorrecipesexplorer.model import com.fasterxml.jackson.annotation.JsonIgnore +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.exception.RestException import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank @@ -27,39 +28,39 @@ private const val NOTE_GROUP_ID_NULL_MESSAGE = "Un identifiant de groupe est req @Entity @Table(name = "recipe") data class Recipe( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - /** The name of the recipe. It is not unique in the entire system, but is unique in the scope of a [Company]. */ - val name: String, + /** The name of the recipe. It is not unique in the entire system, but is unique in the scope of a [Company]. */ + val name: String, - val description: String, + val description: String, - /** The color produced by the recipe. The string should be formatted as a hexadecimal color without the sharp (#). */ - val color: String, + /** The color produced by the recipe. The string should be formatted as a hexadecimal color without the sharp (#). */ + val color: String, - /** The gloss of the color in percents. (0-100) */ - val gloss: Byte, + /** The gloss of the color in percents. (0-100) */ + val gloss: Byte, - val sample: Int?, + val sample: Int?, - @Column(name = "approbation_date") - val approbationDate: LocalDate?, + @Column(name = "approbation_date") + val approbationDate: LocalDate?, - /** A remark given by the creator of the recipe. */ - val remark: String, + /** A remark given by the creator of the recipe. */ + val remark: String, - @ManyToOne - @JoinColumn(name = "company_id") - val company: Company, + @ManyToOne + @JoinColumn(name = "company_id") + val company: Company, - @OneToMany(cascade = [CascadeType.ALL], mappedBy = "recipe") - val mixes: MutableList, + @OneToMany(cascade = [CascadeType.ALL], mappedBy = "recipe") + val mixes: MutableList, - @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) - @JoinColumn(name = "recipe_id") - val groupsInformation: Set + @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) + @JoinColumn(name = "recipe_id") + val groupsInformation: Set ) : Model { /** The mix types contained in this recipe. */ val mixTypes: Collection @@ -67,209 +68,218 @@ data class Recipe( get() = mixes.map { it.mixType } fun groupInformationForGroup(groupId: Long) = - groupsInformation.firstOrNull { it.group.id == groupId } + groupsInformation.firstOrNull { it.group.id == groupId } } open class RecipeSaveDto( - @field:NotBlank(message = RECIPE_NAME_NULL_MESSAGE) - val name: String, + @field:NotBlank(message = RECIPE_NAME_NULL_MESSAGE) + val name: String, - @field:NotBlank(message = RECIPE_DESCRIPTION_NULL_MESSAGE) - val description: String, + @field:NotBlank(message = RECIPE_DESCRIPTION_NULL_MESSAGE) + val description: String, - @field:NotBlank(message = RECIPE_COLOR_NULL_MESSAGE) - @field:Pattern(regexp = "^#([0-9a-f]{6})$") - val color: String, + @field:NotBlank(message = RECIPE_COLOR_NULL_MESSAGE) + @field:Pattern(regexp = "^#([0-9a-f]{6})$") + val color: String, - @field:NotNull(message = RECIPE_GLOSS_NULL_MESSAGE) - @field:Min(value = 0, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) - @field:Max(value = 100, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) - val gloss: Byte, + @field:NotNull(message = RECIPE_GLOSS_NULL_MESSAGE) + @field:Min(value = 0, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) + @field:Max(value = 100, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) + val gloss: Byte, - @field:Min(value = 0, message = RECIPE_SAMPLE_TOO_SMALL_MESSAGE) - val sample: Int?, + @field:Min(value = 0, message = RECIPE_SAMPLE_TOO_SMALL_MESSAGE) + val sample: Int?, - val approbationDate: LocalDate?, + val approbationDate: LocalDate?, - val remark: String?, + val remark: String?, - @field:Min(value = 0, message = RECIPE_COMPANY_NULL_MESSAGE) - val companyId: Long = -1L, + @field:Min(value = 0, message = RECIPE_COMPANY_NULL_MESSAGE) + val companyId: Long = -1L, ) : EntityDto { override fun toEntity(): Recipe = recipe( - name = name, - description = description, - sample = sample, - approbationDate = approbationDate, - remark = remark ?: "", - company = company(id = companyId) + name = name, + description = description, + sample = sample, + approbationDate = approbationDate, + remark = remark ?: "", + company = company(id = companyId) ) } open class RecipeUpdateDto( - @field:NotNull(message = RECIPE_ID_NULL_MESSAGE) - val id: Long, + @field:NotNull(message = RECIPE_ID_NULL_MESSAGE) + val id: Long, - @field:NullOrNotBlank(message = RECIPE_NAME_NULL_MESSAGE) - val name: String?, + @field:NullOrNotBlank(message = RECIPE_NAME_NULL_MESSAGE) + val name: String?, - @field:NullOrNotBlank(message = RECIPE_DESCRIPTION_NULL_MESSAGE) - val description: String?, + @field:NullOrNotBlank(message = RECIPE_DESCRIPTION_NULL_MESSAGE) + val description: String?, - @field:NullOrNotBlank(message = RECIPE_COLOR_NULL_MESSAGE) - @field:Pattern(regexp = "^#([0-9a-f]{6})$") - val color: String?, + @field:NullOrNotBlank(message = RECIPE_COLOR_NULL_MESSAGE) + @field:Pattern(regexp = "^#([0-9a-f]{6})$") + val color: String?, - @field:Min(value = 0, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) - @field:Max(value = 100, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) - val gloss: Byte?, + @field:Min(value = 0, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) + @field:Max(value = 100, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) + val gloss: Byte?, - @field:NullOrSize(min = 0, message = RECIPE_SAMPLE_TOO_SMALL_MESSAGE) - val sample: Int?, + @field:NullOrSize(min = 0, message = RECIPE_SAMPLE_TOO_SMALL_MESSAGE) + val sample: Int?, - val approbationDate: LocalDate?, + val approbationDate: LocalDate?, - val remark: String?, + val remark: String?, - val steps: Set? + val steps: Set? ) : EntityDto @Entity @Table(name = "recipe_group_information") data class RecipeGroupInformation( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long?, - @ManyToOne - @JoinColumn(name = "group_id") - val group: EmployeeGroup, + @ManyToOne + @JoinColumn(name = "group_id") + val group: EmployeeGroup, - var note: String?, + var note: String?, - @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) - @JoinColumn(name = "recipe_group_information_id") - var steps: MutableSet? + @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) + @JoinColumn(name = "recipe_group_information_id") + var steps: MutableSet? ) data class RecipeStepsDto( - @field:NotNull(message = RECIPE_STEPS_DTO_GROUP_ID_NULL_MESSAGE) - val groupId: Long, + @field:NotNull(message = RECIPE_STEPS_DTO_GROUP_ID_NULL_MESSAGE) + val groupId: Long, - @field:NotNull(message = RECIPE_STEPS_DTO_MESSAGES_NULL_MESSAGE) - val steps: Set + @field:NotNull(message = RECIPE_STEPS_DTO_MESSAGES_NULL_MESSAGE) + val steps: Set ) data class RecipePublicDataDto( - @field:NotNull(message = RECIPE_ID_NULL_MESSAGE) - val recipeId: Long, + @field:NotNull(message = RECIPE_ID_NULL_MESSAGE) + val recipeId: Long, - val notes: Set?, + val notes: Set?, - val mixesLocation: Set? + val mixesLocation: Set? ) data class NoteDto( - @field:NotNull(message = NOTE_GROUP_ID_NULL_MESSAGE) - val groupId: Long, + @field:NotNull(message = NOTE_GROUP_ID_NULL_MESSAGE) + val groupId: Long, - val content: String? + val content: String? ) // ==== DSL ==== fun recipe( - id: Long? = null, - name: String = "name", - description: String = "description", - color: String = "ffffff", - gloss: Byte = 0, - sample: Int? = -1, - approbationDate: LocalDate? = LocalDate.MIN, - remark: String = "remark", - company: Company = company(), - mixes: MutableList = mutableListOf(), - groupsInformation: Set = setOf(), - op: Recipe.() -> Unit = {} + id: Long? = null, + name: String = "name", + description: String = "description", + color: String = "ffffff", + gloss: Byte = 0, + sample: Int? = -1, + approbationDate: LocalDate? = LocalDate.MIN, + remark: String = "remark", + company: Company = company(), + mixes: MutableList = mutableListOf(), + groupsInformation: Set = setOf(), + op: Recipe.() -> Unit = {} ) = Recipe( - id, - name, - description, - color, - gloss, - sample, - approbationDate, - remark, - company, - mixes, - groupsInformation + id, + name, + description, + color, + gloss, + sample, + approbationDate, + remark, + company, + mixes, + groupsInformation ).apply(op) fun recipeSaveDto( - name: String = "name", - description: String = "description", - color: String = "ffffff", - gloss: Byte = 0, - sample: Int? = -1, - approbationDate: LocalDate? = LocalDate.MIN, - remark: String = "remark", - companyId: Long = 0L, - op: RecipeSaveDto.() -> Unit = {} + name: String = "name", + description: String = "description", + color: String = "ffffff", + gloss: Byte = 0, + sample: Int? = -1, + approbationDate: LocalDate? = LocalDate.MIN, + remark: String = "remark", + companyId: Long = 0L, + op: RecipeSaveDto.() -> Unit = {} ) = RecipeSaveDto(name, description, color, gloss, sample, approbationDate, remark, companyId).apply(op) fun recipeUpdateDto( - id: Long = 0L, - name: String? = "name", - description: String? = "description", - color: String? = "ffffff", - gloss: Byte? = 0, - sample: Int? = -1, - approbationDate: LocalDate? = LocalDate.MIN, - remark: String? = "remark", - steps: Set? = setOf(), - op: RecipeUpdateDto.() -> Unit = {} + id: Long = 0L, + name: String? = "name", + description: String? = "description", + color: String? = "ffffff", + gloss: Byte? = 0, + sample: Int? = -1, + approbationDate: LocalDate? = LocalDate.MIN, + remark: String? = "remark", + steps: Set? = setOf(), + op: RecipeUpdateDto.() -> Unit = {} ) = RecipeUpdateDto(id, name, description, color, gloss, sample, approbationDate, remark, steps).apply(op) fun recipeGroupInformation( - id: Long? = null, - group: EmployeeGroup = employeeGroup(), - note: String? = null, - steps: MutableSet? = mutableSetOf(), - op: RecipeGroupInformation.() -> Unit = {} + id: Long? = null, + group: EmployeeGroup = employeeGroup(), + note: String? = null, + steps: MutableSet? = mutableSetOf(), + op: RecipeGroupInformation.() -> Unit = {} ) = RecipeGroupInformation(id, group, note, steps).apply(op) fun recipePublicDataDto( - recipeId: Long = 0L, - notes: Set? = null, - mixesLocation: Set? = null, - op: RecipePublicDataDto.() -> Unit = {} + recipeId: Long = 0L, + notes: Set? = null, + mixesLocation: Set? = null, + op: RecipePublicDataDto.() -> Unit = {} ) = RecipePublicDataDto(recipeId, notes, mixesLocation).apply(op) fun noteDto( - groupId: Long = 0L, - content: String? = "note", - op: NoteDto.() -> Unit = {} + groupId: Long = 0L, + content: String? = "note", + op: NoteDto.() -> Unit = {} ) = NoteDto(groupId, content).apply(op) // ==== Exceptions ==== private const val RECIPE_NOT_FOUND_EXCEPTION_TITLE = "Recipe not found" +private const val RECIPE_ALREADY_EXISTS_EXCEPTION_TITLE = "Recipe already exists" private const val RECIPE_EXCEPTION_ERROR_CODE = "recipe" class RecipeImageNotFoundException(id: Long, recipe: Recipe) : - RestException( - "notfound-recipeimage-id", - "Recipe image not found", - HttpStatus.NOT_FOUND, - "A recipe image with the id $id could no be found for the recipe ${recipe.name}", - mapOf( - "id" to id, - "recipe" to recipe.name - ) + RestException( + "notfound-recipeimage-id", + "Recipe image not found", + HttpStatus.NOT_FOUND, + "A recipe image with the id $id could no be found for the recipe ${recipe.name}", + mapOf( + "id" to id, + "recipe" to recipe.name ) + ) fun recipeIdNotFoundException(id: Long) = - NotFoundException( - RECIPE_EXCEPTION_ERROR_CODE, - RECIPE_NOT_FOUND_EXCEPTION_TITLE, - "A recipe with the id $id could not be found", - id - ) + NotFoundException( + RECIPE_EXCEPTION_ERROR_CODE, + RECIPE_NOT_FOUND_EXCEPTION_TITLE, + "A recipe with the id $id could not be found", + id + ) + +fun recipeIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + RECIPE_EXCEPTION_ERROR_CODE, + RECIPE_ALREADY_EXISTS_EXCEPTION_TITLE, + "A recipe with the id $id already exists", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt index 830c348..1f5a3a7 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/RecipeStep.kt @@ -1,36 +1,46 @@ package dev.fyloz.colorrecipesexplorer.model +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import javax.persistence.* @Entity @Table(name = "recipe_step") data class RecipeStep( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - override val id: Long?, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, - val position: Int, + val position: Int, - val message: String + val message: String ) : Model // ==== DSL ==== fun recipeStep( - id: Long? = null, - position: Int = 0, - message: String = "message", - op: RecipeStep.() -> Unit = {} + id: Long? = null, + position: Int = 0, + message: String = "message", + op: RecipeStep.() -> Unit = {} ) = RecipeStep(id, position, message).apply(op) // ==== Exceptions ==== private const val RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE = "Recipe step not found" +private const val RECIPE_STEP_ALREADY_EXISTS_EXCEPTION_TITLE = "Recipe step already exists" private const val RECIPE_STEP_EXCEPTION_ERROR_CODE = "recipestep" fun recipeStepIdNotFoundException(id: Long) = - NotFoundException( - RECIPE_STEP_EXCEPTION_ERROR_CODE, - RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE, - "A recipe step with the id $id could not be found", - id - ) + NotFoundException( + RECIPE_STEP_EXCEPTION_ERROR_CODE, + RECIPE_STEP_NOT_FOUND_EXCEPTION_TITLE, + "A recipe step with the id $id could not be found", + id + ) + +fun recipeStepIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + RECIPE_STEP_EXCEPTION_ERROR_CODE, + RECIPE_STEP_ALREADY_EXISTS_EXCEPTION_TITLE, + "A recipe step with the id $id already exists", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrNotBlank.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrNotBlank.kt index 6ad2763..9a381b9 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrNotBlank.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrNotBlank.kt @@ -14,9 +14,9 @@ private const val MESSAGE = "must be null or not blank" @MustBeDocumented @Constraint(validatedBy = [NullOrNotBlankValidator::class]) annotation class NullOrNotBlank( - val message: String = MESSAGE, - val groups: Array> = [], - @Suppress("unused") val payload: Array> = [] + val message: String = MESSAGE, + val groups: Array> = [], + @Suppress("unused") val payload: Array> = [] ) class NullOrNotBlankValidator : ConstraintValidator { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrSize.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrSize.kt index a546664..e6c4208 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrSize.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/validation/NullOrSize.kt @@ -14,11 +14,11 @@ private const val MESSAGE = "must be null or have a correct length" @MustBeDocumented @Constraint(validatedBy = [NullOrSizeValidator::class]) annotation class NullOrSize( - val min: Long = MIN_SIZE, - val max: Long = MAX_SIZE, - val message: String = MESSAGE, - val groups: Array> = [], - @Suppress("unused") val payload: Array> = [] + val min: Long = MIN_SIZE, + val max: Long = MAX_SIZE, + val message: String = MESSAGE, + val groups: Array> = [], + @Suppress("unused") val payload: Array> = [] ) class NullOrSizeValidator : ConstraintValidator { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/CompanyRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/CompanyRepository.kt index 2d2ec80..b0b4142 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/CompanyRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/CompanyRepository.kt @@ -7,7 +7,7 @@ import org.springframework.stereotype.Repository @Repository interface CompanyRepository : NamedJpaRepository { @Query( - """ + """ select case when(count(r.id) > 0) then false else true end from Company c left join Recipe r on c.id = r.company.id diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt index 63c633d..03ed9c3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepository.kt @@ -17,7 +17,7 @@ interface MaterialRepository : NamedJpaRepository { fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) @Query( - """ + """ select case when(count(mm.id) + count(mt.id) > 0) then false else true end from Material m left join MixMaterial mm on m.id = mm.material.id diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt index e07c248..a664876 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialTypeRepository.kt @@ -16,7 +16,7 @@ interface MaterialTypeRepository : NamedJpaRepository { fun findByPrefix(prefix: String): MaterialType? @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 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepository.kt index b3a4f0a..51c39a9 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepository.kt @@ -16,7 +16,7 @@ interface MixRepository : JpaRepository { fun updateLocationById(id: Long, location: String?) @Query( - """ + """ select case when(count(mm.id) > 0) then false else true end from Mix m left join MixMaterial mm on m.mixType.material.id = mm.material.id diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixTypeRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixTypeRepository.kt index 732e57c..7298eb6 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixTypeRepository.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixTypeRepository.kt @@ -19,7 +19,7 @@ interface MixTypeRepository : NamedJpaRepository { fun findByNameAndMaterialType(name: String, materialType: MaterialType): MixType? @Query( - """ + """ select case when(count(m.id) > 0) then false else true end from MixType t left join Mix m on t.id = m.mixType.id diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt index 8e89f85..0776aa5 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt @@ -23,52 +23,52 @@ class EmployeeController(private val employeeService: EmployeeService) { @GetMapping @PreAuthorizeViewUsers fun getAll() = - ok(employeeService.getAll()) + ok(employeeService.getAll()) @GetMapping("{id}") @PreAuthorizeViewUsers fun getById(@PathVariable id: Long) = - ok(employeeService.getById(id)) + ok(employeeService.getById(id)) @GetMapping("current") fun getCurrent(loggedInEmployee: Principal?) = - if (loggedInEmployee != null) - ok( - employeeService.getById( - loggedInEmployee.name.toLong(), - ignoreDefaultGroupUsers = false, - ignoreSystemUsers = false - ) + if (loggedInEmployee != null) + ok( + employeeService.getById( + loggedInEmployee.name.toLong(), + ignoreDefaultGroupUsers = false, + ignoreSystemUsers = false ) - else - forbidden() + ) + else + forbidden() @PostMapping @PreAuthorizeEditUsers fun save(@Valid @RequestBody employee: EmployeeSaveDto) = - created(EMPLOYEE_CONTROLLER_PATH) { - employeeService.save(employee) - } + created(EMPLOYEE_CONTROLLER_PATH) { + employeeService.save(employee) + } @PutMapping @PreAuthorizeEditUsers fun update(@Valid @RequestBody employee: EmployeeUpdateDto) = - noContent { - employeeService.update(employee) - } + noContent { + employeeService.update(employee) + } @PutMapping("{id}/password", consumes = [MediaType.TEXT_PLAIN_VALUE]) @PreAuthorizeEditUsers fun updatePassword(@PathVariable id: Long, @RequestBody password: String) = - noContent { - employeeService.updatePassword(id, password) - } + noContent { + employeeService.updatePassword(id, password) + } @PutMapping("{employeeId}/permissions/{permission}") @PreAuthorizeEditUsers fun addPermission( - @PathVariable employeeId: Long, - @PathVariable permission: EmployeePermission + @PathVariable employeeId: Long, + @PathVariable permission: EmployeePermission ) = noContent { employeeService.addPermission(employeeId, permission) } @@ -76,8 +76,8 @@ class EmployeeController(private val employeeService: EmployeeService) { @DeleteMapping("{employeeId}/permissions/{permission}") @PreAuthorizeEditUsers fun removePermission( - @PathVariable employeeId: Long, - @PathVariable permission: EmployeePermission + @PathVariable employeeId: Long, + @PathVariable permission: EmployeePermission ) = noContent { employeeService.removePermission(employeeId, permission) } @@ -85,7 +85,7 @@ class EmployeeController(private val employeeService: EmployeeService) { @DeleteMapping("{id}") @PreAuthorizeRemoveUsers fun deleteById(@PathVariable id: Long) = - employeeService.deleteById(id) + employeeService.deleteById(id) } @RestController @@ -94,50 +94,50 @@ class GroupsController(private val groupService: EmployeeGroupServiceImpl) { @GetMapping @PreAuthorize("hasAnyAuthority('VIEW_RECIPES', 'VIEW_USERS')") fun getAll() = - ok(groupService.getAll()) + ok(groupService.getAll()) @GetMapping("{id}") @PreAuthorizeViewUsers fun getById(@PathVariable id: Long) = - ok(groupService.getById(id)) + ok(groupService.getById(id)) @GetMapping("{id}/employees") @PreAuthorizeViewUsers fun getEmployeesForGroup(@PathVariable id: Long) = - ok(groupService.getEmployeesForGroup(id)) + ok(groupService.getEmployeesForGroup(id)) @PostMapping("default/{groupId}") @PreAuthorizeViewUsers fun setDefaultGroup(@PathVariable groupId: Long, response: HttpServletResponse) = - noContent { - groupService.setResponseDefaultGroup(groupId, response) - } + noContent { + groupService.setResponseDefaultGroup(groupId, response) + } @GetMapping("default") @PreAuthorizeViewUsers fun getRequestDefaultGroup(request: HttpServletRequest) = - ok(groupService.getRequestDefaultGroup(request)) + ok(groupService.getRequestDefaultGroup(request)) @PostMapping @PreAuthorizeEditUsers fun save(@Valid @RequestBody group: EmployeeGroupSaveDto) = - created(EMPLOYEE_GROUP_CONTROLLER_PATH) { - groupService.save(group) - } + created(EMPLOYEE_GROUP_CONTROLLER_PATH) { + groupService.save(group) + } @PutMapping @PreAuthorizeEditUsers fun update(@Valid @RequestBody group: EmployeeGroupUpdateDto) = - noContent { - groupService.update(group) - } + noContent { + groupService.update(group) + } @DeleteMapping("{id}") @PreAuthorizeRemoveUsers fun deleteById(@PathVariable id: Long) = - noContent { - groupService.deleteById(id) - } + noContent { + groupService.deleteById(id) + } } @RestController @@ -145,7 +145,7 @@ class GroupsController(private val groupService: EmployeeGroupServiceImpl) { class LogoutController(private val employeeService: EmployeeService) { @GetMapping("logout") fun logout(request: HttpServletRequest) = - ok { - employeeService.logout(request) - } + ok { + employeeService.logout(request) + } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt index bf690b4..ef59303 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt @@ -17,30 +17,30 @@ private const val COMPANY_CONTROLLER_PATH = "api/company" class CompanyController(private val companyService: CompanyService) { @GetMapping fun getAll() = - ok(companyService.getAll()) + ok(companyService.getAll()) @GetMapping("{id}") fun getById(@PathVariable id: Long) = - ok(companyService.getById(id)) + ok(companyService.getById(id)) @PostMapping @PreAuthorize("hasAuthority('EDIT_COMPANIES')") fun save(@Valid @RequestBody company: CompanySaveDto) = - created(COMPANY_CONTROLLER_PATH) { - companyService.save(company) - } + created(COMPANY_CONTROLLER_PATH) { + companyService.save(company) + } @PutMapping @PreAuthorize("hasAuthority('EDIT_COMPANIES')") fun update(@Valid @RequestBody company: CompanyUpdateDto) = - noContent { - companyService.update(company) - } + noContent { + companyService.update(company) + } @DeleteMapping("{id}") @PreAuthorize("hasAuthority('REMOVE_COMPANIES')") fun deleteById(@PathVariable id: Long) = - noContent { - companyService.deleteById(id) - } + noContent { + companyService.deleteById(id) + } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/InventoryController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/InventoryController.kt index 5971d86..832e14d 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/InventoryController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/InventoryController.kt @@ -15,7 +15,7 @@ private const val INVENTORY_CONTROLLER_PATH = "api/inventory" @RestController @RequestMapping(INVENTORY_CONTROLLER_PATH) class InventoryController( - private val inventoryService: InventoryService + private val inventoryService: InventoryService ) { @PutMapping("add") @PreAuthorize("hasAuthority('ADD_TO_INVENTORY')") @@ -26,10 +26,10 @@ class InventoryController( @PutMapping("deduct") @PreAuthorize("hasAuthority('DEDUCT_FROM_INVENTORY')") fun deduct(@RequestBody quantities: Collection) = - ok(inventoryService.deduct(quantities)) + ok(inventoryService.deduct(quantities)) @PutMapping("deduct/mix") @PreAuthorize("hasAuthority('DEDUCT_FROM_INVENTORY')") fun deduct(@RequestBody mixRatio: MixDeductDto) = - ok(inventoryService.deductMix(mixRatio)) + ok(inventoryService.deductMix(mixRatio)) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt index 39df895..a44d493 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt @@ -18,55 +18,55 @@ private const val MATERIAL_CONTROLLER_PATH = "api/material" class MaterialController(private val materialService: MaterialService) { @GetMapping fun getAll() = - ok(materialService.getAll()) + ok(materialService.getAll()) @GetMapping("notmixtype") fun getAllNotMixType() = - ok(materialService.getAllNotMixType()) + ok(materialService.getAllNotMixType()) @GetMapping("{id}") fun getById(@PathVariable id: Long) = - ok(materialService.getById(id)) + ok(materialService.getById(id)) @PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) @PreAuthorize("hasAuthority('EDIT_MATERIALS')") fun save(@Valid material: MaterialSaveDto, simdutFile: MultipartFile?) = - created(MATERIAL_CONTROLLER_PATH) { - materialService.save( - materialSaveDto( - name = material.name, - inventoryQuantity = material.inventoryQuantity, - materialTypeId = material.materialTypeId, - simdutFile = simdutFile - ) + created(MATERIAL_CONTROLLER_PATH) { + materialService.save( + materialSaveDto( + name = material.name, + inventoryQuantity = material.inventoryQuantity, + materialTypeId = material.materialTypeId, + simdutFile = simdutFile ) - } + ) + } @PutMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) @PreAuthorize("hasAuthority('EDIT_MATERIALS')") fun update(@Valid material: MaterialUpdateDto, simdutFile: MultipartFile?) = - noContent { - materialService.update( - materialUpdateDto( - id = material.id, - name = material.name, - inventoryQuantity = material.inventoryQuantity, - materialTypeId = material.materialTypeId, - simdutFile = simdutFile - ) + noContent { + materialService.update( + materialUpdateDto( + id = material.id, + name = material.name, + inventoryQuantity = material.inventoryQuantity, + materialTypeId = material.materialTypeId, + simdutFile = simdutFile ) - } + ) + } @DeleteMapping("{id}") @PreAuthorize("hasAuthority('REMOVE_MATERIALS')") fun deleteById(@PathVariable id: Long) = - noContent { - materialService.deleteById(id) - } + noContent { + materialService.deleteById(id) + } @GetMapping("{id}/simdut/exists") fun hasSimdut(@PathVariable id: Long) = - ok(materialService.hasSimdut(id)) + ok(materialService.hasSimdut(id)) @GetMapping("{id}/simdut", produces = [MediaType.APPLICATION_PDF_VALUE]) fun getSimdut(@PathVariable id: Long): ResponseEntity = with(materialService.getSimdut(id)) { @@ -79,14 +79,14 @@ class MaterialController(private val materialService: MaterialService) { @GetMapping("/simdut") fun getAllIdsWithSimdut() = - ok(materialService.getAllIdsWithSimdut()) + ok(materialService.getAllIdsWithSimdut()) @GetMapping("mix/create/{recipeId}") fun getAllForMixCreation(@PathVariable recipeId: Long) = - ok(materialService.getAllForMixCreation(recipeId)) + ok(materialService.getAllForMixCreation(recipeId)) @GetMapping("mix/update/{mixId}") fun getAllForMixUpdate(@PathVariable mixId: Long) = - ok(materialService.getAllForMixUpdate(mixId)) + ok(materialService.getAllForMixUpdate(mixId)) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt index 6123066..3991873 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt @@ -17,31 +17,31 @@ private const val MATERIAL_TYPE_CONTROLLER_PATH = "api/materialtype" class MaterialTypeController(private val materialTypeService: MaterialTypeService) { @GetMapping fun getAll() = - ok(materialTypeService.getAll()) + ok(materialTypeService.getAll()) @GetMapping("{id}") fun getById(@PathVariable id: Long) = - ok(materialTypeService.getById(id)) + ok(materialTypeService.getById(id)) @PostMapping @PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')") fun save(@Valid @RequestBody materialType: MaterialTypeSaveDto) = - created(MATERIAL_TYPE_CONTROLLER_PATH) { - materialTypeService.save(materialType) - } + created(MATERIAL_TYPE_CONTROLLER_PATH) { + materialTypeService.save(materialType) + } @PutMapping @PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')") fun update(@Valid @RequestBody materialType: MaterialTypeUpdateDto) = - noContent { - materialTypeService.update(materialType) - } + noContent { + materialTypeService.update(materialType) + } @DeleteMapping("{id}") @PreAuthorize("hasAuthority('REMOVE_MATERIAL_TYPES')") fun deleteById(@PathVariable id: Long) = - noContent { - materialTypeService.deleteById(id) - } + noContent { + materialTypeService.deleteById(id) + } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt index 0d81d5d..a4f39fc 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt @@ -25,39 +25,39 @@ private const val MIX_CONTROLLER_PATH = "api/recipe/mix" class RecipeController(private val recipeService: RecipeService) { @GetMapping fun getAll() = - ok(recipeService.getAll()) + ok(recipeService.getAll()) @GetMapping("{id}") fun getById(@PathVariable id: Long) = - ok(recipeService.getById(id)) + ok(recipeService.getById(id)) @PostMapping @PreAuthorizeEditRecipes fun save(@Valid @RequestBody recipe: RecipeSaveDto) = - created(RECIPE_CONTROLLER_PATH) { - recipeService.save(recipe) - } + created(RECIPE_CONTROLLER_PATH) { + recipeService.save(recipe) + } @PutMapping @PreAuthorizeEditRecipes fun update(@Valid @RequestBody recipe: RecipeUpdateDto) = - noContent { - recipeService.update(recipe) - } + noContent { + recipeService.update(recipe) + } @PutMapping("public") @PreAuthorize("hasAuthority('EDIT_RECIPES_PUBLIC_DATA')") fun updatePublicData(@Valid @RequestBody publicDataDto: RecipePublicDataDto) = - noContent { - recipeService.updatePublicData(publicDataDto) - } + noContent { + recipeService.updatePublicData(publicDataDto) + } @DeleteMapping("{id}") @PreAuthorizeRemoveRecipes fun deleteById(@PathVariable id: Long) = - noContent { - recipeService.deleteById(id) - } + noContent { + recipeService.deleteById(id) + } } @RestController @@ -66,11 +66,11 @@ class RecipeController(private val recipeService: RecipeService) { class RecipeImageController(val recipeImageService: RecipeImageService) { @GetMapping("{recipeId}/image") fun getAllIdsForRecipe(@PathVariable recipeId: Long) = - ok(recipeImageService.getAllIdsForRecipe(recipeId)) + ok(recipeImageService.getAllIdsForRecipe(recipeId)) @GetMapping("{recipeId}/image/{id}", produces = [MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE]) fun getById(@PathVariable recipeId: Long, @PathVariable id: Long) = - ok(recipeImageService.getByIdForRecipe(id, recipeId)) + ok(recipeImageService.getByIdForRecipe(id, recipeId)) @PostMapping("{recipeId}/image", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) @PreAuthorizeEditRecipes @@ -82,9 +82,9 @@ class RecipeImageController(val recipeImageService: RecipeImageService) { @DeleteMapping("{recipeId}/image/{id}") @PreAuthorizeRemoveRecipes fun delete(@PathVariable recipeId: Long, @PathVariable id: Long) = - noContent { - recipeImageService.delete(id, recipeId) - } + noContent { + recipeImageService.delete(id, recipeId) + } } @RestController @@ -93,26 +93,26 @@ class RecipeImageController(val recipeImageService: RecipeImageService) { class MixController(private val mixService: MixService) { @GetMapping("{id}") fun getById(@PathVariable id: Long) = - ok(mixService.getById(id)) + ok(mixService.getById(id)) @PostMapping @PreAuthorizeEditRecipes fun save(@Valid @RequestBody mix: MixSaveDto) = - created(MIX_CONTROLLER_PATH) { - mixService.save(mix) - } + created(MIX_CONTROLLER_PATH) { + mixService.save(mix) + } @PutMapping @PreAuthorizeEditRecipes fun update(@Valid @RequestBody mix: MixUpdateDto) = - noContent { - mixService.update(mix) - } + noContent { + mixService.update(mix) + } @DeleteMapping("{id}") @PreAuthorizeRemoveRecipes fun deleteById(@PathVariable id: Long) = - noContent { - mixService.deleteById(id) - } + noContent { + mixService.deleteById(id) + } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RestUtils.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RestUtils.kt index 0c0de8a..46004d1 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RestUtils.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RestUtils.kt @@ -9,11 +9,11 @@ import java.net.URI /** Creates a HTTP OK [ResponseEntity] from the given [body]. */ fun ok(body: T): ResponseEntity = - ResponseEntity.ok(body) + ResponseEntity.ok(body) /** Creates a HTTP OK [ResponseEntity] from the given [body] and [headers]. */ fun ok(body: T, headers: HttpHeaders): ResponseEntity = - ResponseEntity(body, headers, HttpStatus.OK) + ResponseEntity(body, headers, HttpStatus.OK) /** Executes the given [action] then returns an HTTP OK [ResponseEntity] form the given [body]. */ fun ok(action: () -> Unit): ResponseEntity { @@ -23,19 +23,19 @@ fun ok(action: () -> Unit): ResponseEntity { /** Creates a HTTP CREATED [ResponseEntity] from the given [body] with the location set to [controllerPath]/id. */ fun created(controllerPath: String, body: T): ResponseEntity = - ResponseEntity.created(URI.create("$controllerPath/${body.id}")).body(body) + ResponseEntity.created(URI.create("$controllerPath/${body.id}")).body(body) /** Creates a HTTP CREATED [ResponseEntity] with the result of the given [producer] as its body. */ fun created(controllerPath: String, producer: () -> T): ResponseEntity = - created(controllerPath, producer()) + created(controllerPath, producer()) /** Creates a HTTP NOT FOUND [ResponseEntity]. */ fun notFound(): ResponseEntity = - ResponseEntity.notFound().build() + ResponseEntity.notFound().build() /** Creates a HTTP NO CONTENT [ResponseEntity]. */ fun noContent(): ResponseEntity = - ResponseEntity.noContent().build() + ResponseEntity.noContent().build() /** Executes the given [action] then returns an HTTP NO CONTENT [ResponseEntity]. */ fun noContent(action: () -> Unit): ResponseEntity { @@ -45,12 +45,12 @@ fun noContent(action: () -> Unit): ResponseEntity { /** Creates a HTTP FORBIDDEN [ResponseEntity]. */ fun forbidden(): ResponseEntity = - ResponseEntity.status(HttpStatus.FORBIDDEN).build() + ResponseEntity.status(HttpStatus.FORBIDDEN).build() /** Creates an [HttpHeaders] instance from the given options. */ fun httpHeaders( - contentType: MediaType = MediaType.APPLICATION_JSON, - op: HttpHeaders.() -> Unit = {} + contentType: MediaType = MediaType.APPLICATION_JSON, + op: HttpHeaders.() -> Unit = {} ) = HttpHeaders().apply { this.contentType = contentType diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt index 7462285..183ccd9 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt @@ -56,7 +56,7 @@ interface EmployeeService : ExternalModelService { + ExternalNamedModelService { /** Gets all the employees of the group with the given [id]. */ fun getEmployeesForGroup(id: Long): Collection @@ -74,49 +74,50 @@ interface EmployeeUserDetailsService : UserDetailsService { @Service class EmployeeServiceImpl( - employeeRepository: EmployeeRepository, - @Lazy val groupService: EmployeeGroupService, - @Lazy val passwordEncoder: PasswordEncoder, + employeeRepository: EmployeeRepository, + @Lazy val groupService: EmployeeGroupService, + @Lazy val passwordEncoder: PasswordEncoder, ) : AbstractExternalModelService(employeeRepository), - EmployeeService { + EmployeeService { override fun idNotFoundException(id: Long) = employeeIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = employeeIdAlreadyExistsException(id) override fun existsByFirstNameAndLastName(firstName: String, lastName: String): Boolean = - repository.existsByFirstNameAndLastName(firstName, lastName) + repository.existsByFirstNameAndLastName(firstName, lastName) override fun getAll(): Collection = - super.getAll().filter { !it.isSystemUser && !it.isDefaultGroupUser } + super.getAll().filter { !it.isSystemUser && !it.isDefaultGroupUser } override fun getById(id: Long): Employee = - getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true) + getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true) override fun getById(id: Long, ignoreDefaultGroupUsers: Boolean, ignoreSystemUsers: Boolean): Employee = - super.getById(id).apply { - if (ignoreSystemUsers && isSystemUser || ignoreDefaultGroupUsers && isDefaultGroupUser) - throw idNotFoundException(id) - } + super.getById(id).apply { + if (ignoreSystemUsers && isSystemUser || ignoreDefaultGroupUsers && isDefaultGroupUser) + throw idNotFoundException(id) + } override fun getByGroup(group: EmployeeGroup): Collection = - repository.findAllByGroup(group).filter { - !it.isSystemUser && !it.isDefaultGroupUser - } + repository.findAllByGroup(group).filter { + !it.isSystemUser && !it.isDefaultGroupUser + } override fun getDefaultGroupEmployee(group: EmployeeGroup): Employee = - repository.findByIsDefaultGroupUserIsTrueAndGroupIs(group) + repository.findByIsDefaultGroupUserIsTrueAndGroupIs(group) override fun save(entity: EmployeeSaveDto): Employee = - save(with(entity) { - Employee( - id, - firstName, - lastName, - passwordEncoder.encode(password), - isDefaultGroupUser = false, - isSystemUser = false, - group = if (groupId != null) groupService.getById(groupId) else null, - permissions = permissions - ) - }) + save(with(entity) { + Employee( + id, + firstName, + lastName, + passwordEncoder.encode(password), + isDefaultGroupUser = false, + isSystemUser = false, + group = if (groupId != null) groupService.getById(groupId) else null, + permissions = permissions + ) + }) override fun save(entity: Employee): Employee { if (existsById(entity.id)) @@ -128,14 +129,14 @@ class EmployeeServiceImpl( override fun saveDefaultGroupEmployee(group: EmployeeGroup) { save( - Employee( - id = 1000000L + group.id!!, - firstName = group.name, - lastName = "EmployeeModel", - password = passwordEncoder.encode(group.name), - group = group, - isDefaultGroupUser = true - ) + Employee( + id = 1000000L + group.id!!, + firstName = group.name, + lastName = "EmployeeModel", + password = passwordEncoder.encode(group.name), + group = group, + isDefaultGroupUser = true + ) ) } @@ -143,9 +144,9 @@ class EmployeeServiceImpl( val employee = getById(employeeId, ignoreDefaultGroupUsers = true, ignoreSystemUsers = false) employee.lastLoginTime = time return update( - employee, - ignoreDefaultGroupUsers = true, - ignoreSystemUsers = false + employee, + ignoreDefaultGroupUsers = true, + ignoreSystemUsers = false ) } @@ -153,21 +154,21 @@ class EmployeeServiceImpl( val persistedEmployee by lazy { getById(entity.id) } return update(with(entity) { Employee( - id = id, - firstName = firstName or persistedEmployee.firstName, - lastName = lastName or persistedEmployee.lastName, - password = persistedEmployee.password, - isDefaultGroupUser = false, - isSystemUser = false, - group = if (entity.groupId != null) groupService.getById(entity.groupId) else persistedEmployee.group, - permissions = permissions?.toMutableSet() ?: persistedEmployee.permissions, - lastLoginTime = persistedEmployee.lastLoginTime + id = id, + firstName = firstName or persistedEmployee.firstName, + lastName = lastName or persistedEmployee.lastName, + password = persistedEmployee.password, + isDefaultGroupUser = false, + isSystemUser = false, + group = if (entity.groupId != null) groupService.getById(entity.groupId) else persistedEmployee.group, + permissions = permissions?.toMutableSet() ?: persistedEmployee.permissions, + lastLoginTime = persistedEmployee.lastLoginTime ) }) } override fun update(entity: Employee): Employee = - update(entity, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true) + update(entity, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true) override fun update(entity: Employee, ignoreDefaultGroupUsers: Boolean, ignoreSystemUsers: Boolean): Employee { with(repository.findByFirstNameAndLastName(entity.firstName, entity.lastName)) { @@ -182,24 +183,24 @@ class EmployeeServiceImpl( val persistedEmployee = getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true) return super.update(with(persistedEmployee) { Employee( - id, - firstName, - lastName, - passwordEncoder.encode(password), - isDefaultGroupUser, - isSystemUser, - group, - permissions, - lastLoginTime + id, + firstName, + lastName, + passwordEncoder.encode(password), + isDefaultGroupUser, + isSystemUser, + group, + permissions, + lastLoginTime ) }) } override fun addPermission(employeeId: Long, permission: EmployeePermission): Employee = - super.update(getById(employeeId).apply { permissions += permission }) + super.update(getById(employeeId).apply { permissions += permission }) override fun removePermission(employeeId: Long, permission: EmployeePermission): Employee = - super.update(getById(employeeId).apply { permissions -= permission }) + super.update(getById(employeeId).apply { permissions -= permission }) override fun logout(request: HttpServletRequest) { val authorizationCookie = WebUtils.getCookie(request, "Authorization") @@ -216,19 +217,20 @@ const val defaultGroupCookieMaxAge = 10 * 365 * 24 * 60 * 60 // 10 ans @Service class EmployeeGroupServiceImpl( - val employeeService: EmployeeService, - employeeGroupRepository: EmployeeGroupRepository + private val employeeService: EmployeeService, + employeeGroupRepository: EmployeeGroupRepository ) : AbstractExternalNamedModelService( - employeeGroupRepository + employeeGroupRepository ), - EmployeeGroupService { + EmployeeGroupService { override fun idNotFoundException(id: Long) = employeeGroupIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = employeeGroupIdAlreadyExistsException(id) override fun nameNotFoundException(name: String) = employeeGroupNameNotFoundException(name) override fun nameAlreadyExistsException(name: String) = employeeGroupNameAlreadyExistsException(name) override fun existsByName(name: String): Boolean = repository.existsByName(name) override fun getEmployeesForGroup(id: Long): Collection = - employeeService.getByGroup(getById(id)) + employeeService.getByGroup(getById(id)) @Transactional override fun save(entity: EmployeeGroup): EmployeeGroup { @@ -241,9 +243,9 @@ class EmployeeGroupServiceImpl( val persistedGroup by lazy { getById(entity.id) } return update(with(entity) { EmployeeGroup( - entity.id, - if (name.isNotBlank()) entity.name else persistedGroup.name, - if (permissions.isNotEmpty()) entity.permissions else persistedGroup.permissions + entity.id, + if (name.isNotBlank()) entity.name else persistedGroup.name, + if (permissions.isNotEmpty()) entity.permissions else persistedGroup.permissions ) }) } @@ -256,11 +258,11 @@ class EmployeeGroupServiceImpl( override fun getRequestDefaultGroup(request: HttpServletRequest): EmployeeGroup { val defaultGroupCookie = WebUtils.getCookie(request, defaultGroupCookieName) - ?: throw NoDefaultGroupException() + ?: throw NoDefaultGroupException() val defaultGroupUser = employeeService.getById( - defaultGroupCookie.value.toLong(), - ignoreDefaultGroupUsers = false, - ignoreSystemUsers = true + defaultGroupCookie.value.toLong(), + ignoreDefaultGroupUsers = false, + ignoreSystemUsers = true ) return defaultGroupUser.group!! } @@ -269,17 +271,17 @@ class EmployeeGroupServiceImpl( val group = getById(groupId) val defaultGroupUser = employeeService.getDefaultGroupEmployee(group) response.addHeader( - "Set-Cookie", - "$defaultGroupCookieName=${defaultGroupUser.id}; Max-Age=${defaultGroupCookieMaxAge}; Path=/api; HttpOnly; Secure; SameSite=strict" + "Set-Cookie", + "$defaultGroupCookieName=${defaultGroupUser.id}; Max-Age=${defaultGroupCookieMaxAge}; Path=/api; HttpOnly; Secure; SameSite=strict" ) } } @Service class EmployeeUserDetailsServiceImpl( - val employeeService: EmployeeService + private val employeeService: EmployeeService ) : - EmployeeUserDetailsService { + EmployeeUserDetailsService { override fun loadUserByUsername(username: String): UserDetails { try { return loadUserByEmployeeId(username.toLong(), true) @@ -292,9 +294,9 @@ class EmployeeUserDetailsServiceImpl( override fun loadUserByEmployeeId(employeeId: Long, ignoreDefaultGroupUsers: Boolean): UserDetails { val employee = employeeService.getById( - employeeId, - ignoreDefaultGroupUsers = ignoreDefaultGroupUsers, - ignoreSystemUsers = false + employeeId, + ignoreDefaultGroupUsers = ignoreDefaultGroupUsers, + ignoreSystemUsers = false ) return User(employee.id.toString(), employee.password, employee.authorities) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt index 65f037f..5e2d495 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyService.kt @@ -12,12 +12,13 @@ interface CompanyService : ExternalNamedModelService(companyRepository), - CompanyService { + AbstractExternalNamedModelService(companyRepository), + CompanyService { override fun idNotFoundException(id: Long) = companyIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = companyIdAlreadyExistsException(id) override fun nameNotFoundException(name: String) = companyNameNotFoundException(name) override fun nameAlreadyExistsException(name: String) = companyNameAlreadyExistsException(name) @@ -29,8 +30,8 @@ class CompanyServiceImpl( return update(with(entity) { company( - id = id, - name = if (name != null && name.isNotBlank()) name else persistedCompany.name + id = id, + name = if (name != null && name.isNotBlank()) name else persistedCompany.name ) }) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt index 0dc05b3..965425a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt @@ -26,20 +26,20 @@ interface InventoryService { @Service class InventoryServiceImpl( - private val materialService: MaterialService, - private val mixService: MixService + private val materialService: MaterialService, + private val mixService: MixService ) : InventoryService { @Transactional override fun add(materialQuantities: Collection) = - materialQuantities.map { - materialQuantityDto(materialId = it.material, quantity = add(it)) - } + materialQuantities.map { + materialQuantityDto(materialId = it.material, quantity = add(it)) + } override fun add(materialQuantity: MaterialQuantityDto) = - materialService.updateQuantity( - materialService.getById(materialQuantity.material), - materialQuantity.quantity - ) + materialService.updateQuantity( + materialService.getById(materialQuantity.material), + materialQuantity.quantity + ) @Transactional override fun deductMix(mixRatio: MixDeductDto): Collection { @@ -48,15 +48,15 @@ class InventoryServiceImpl( val adjustedFirstMaterialQuantity = firstMixMaterial.quantity * mixRatio.ratio fun adjustQuantity(mixMaterial: MixMaterial): Float = - if (!mixMaterial.material.materialType!!.usePercentages) - mixMaterial.quantity * mixRatio.ratio - else - (mixMaterial.quantity * adjustedFirstMaterialQuantity) / 100f + if (!mixMaterial.material.materialType!!.usePercentages) + mixMaterial.quantity * mixRatio.ratio + else + (mixMaterial.quantity * adjustedFirstMaterialQuantity) / 100f return deduct(mix.mixMaterials.map { materialQuantityDto( - materialId = it.material.id!!, - quantity = adjustQuantity(it) + materialId = it.material.id!!, + quantity = adjustQuantity(it) ) }) } @@ -65,11 +65,11 @@ class InventoryServiceImpl( override fun deduct(materialQuantities: Collection): Collection { val thrown = mutableListOf() val updatedQuantities = - materialQuantities.mapMayThrow( - { thrown.add(it) } - ) { - materialQuantityDto(materialId = it.material, quantity = deduct(it)) - } + materialQuantities.mapMayThrow( + { thrown.add(it) } + ) { + materialQuantityDto(materialId = it.material, quantity = deduct(it)) + } if (thrown.isNotEmpty()) { throw MultiplesNotEnoughInventoryException(thrown) @@ -78,35 +78,35 @@ class InventoryServiceImpl( } override fun deduct(materialQuantity: MaterialQuantityDto): Float = - with(materialService.getById(materialQuantity.material)) { - if (this.inventoryQuantity >= materialQuantity.quantity) { - materialService.updateQuantity(this, -materialQuantity.quantity) - } else { - throw NotEnoughInventoryException(materialQuantity.quantity, this) - } - } + with(materialService.getById(materialQuantity.material)) { + if (this.inventoryQuantity >= materialQuantity.quantity) { + materialService.updateQuantity(this, -materialQuantity.quantity) + } else { + throw NotEnoughInventoryException(materialQuantity.quantity, this) + } + } } class NotEnoughInventoryException(quantity: Float, material: Material) : - RestException( - "notenoughinventory", - "Not enough inventory", - HttpStatus.BAD_REQUEST, - "Cannot deduct ${quantity}mL of ${material.name} because there is only ${material.inventoryQuantity}mL in inventory", - mapOf( - "material" to material.name, - "requestQuantity" to quantity, - "availableQuantity" to material.inventoryQuantity - ) + RestException( + "notenoughinventory", + "Not enough inventory", + HttpStatus.BAD_REQUEST, + "Cannot deduct ${quantity}mL of ${material.name} because there is only ${material.inventoryQuantity}mL in inventory", + mapOf( + "material" to material.name, + "requestQuantity" to quantity, + "availableQuantity" to material.inventoryQuantity ) + ) class MultiplesNotEnoughInventoryException(exceptions: List) : - RestException( - "notenoughinventory-multiple", - "Not enough inventory", - HttpStatus.BAD_REQUEST, - "Cannot deduct requested quantities because there is no enough of them in inventory", - mapOf( - "lowQuantities" to exceptions.map { it.extensions } - ) + RestException( + "notenoughinventory-multiple", + "Not enough inventory", + HttpStatus.BAD_REQUEST, + "Cannot deduct requested quantities because there is no enough of them in inventory", + mapOf( + "lowQuantities" to exceptions.map { it.extensions } ) + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt index 384e04c..0b9831b 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MaterialRepository import dev.fyloz.colorrecipesexplorer.service.files.SimdutService @@ -8,7 +9,7 @@ import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Service interface MaterialService : - ExternalNamedModelService { + ExternalNamedModelService { /** Checks if a material with the given [materialType] exists. */ fun existsByMaterialType(materialType: MaterialType): Boolean @@ -36,43 +37,44 @@ interface MaterialService : @Service class MaterialServiceImpl( - materialRepository: MaterialRepository, - val simdutService: SimdutService, - val recipeService: RecipeService, - val mixService: MixService, - @Lazy val materialTypeService: MaterialTypeService + materialRepository: MaterialRepository, + val simdutService: SimdutService, + val recipeService: RecipeService, + val mixService: MixService, + @Lazy val materialTypeService: MaterialTypeService ) : - AbstractExternalNamedModelService( - materialRepository - ), - MaterialService { + AbstractExternalNamedModelService( + materialRepository + ), + MaterialService { override fun idNotFoundException(id: Long) = materialIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = materialIdAlreadyExistsException(id) override fun nameNotFoundException(name: String) = materialNameNotFoundException(name) override fun nameAlreadyExistsException(name: String) = materialNameAlreadyExistsException(name) override fun existsByMaterialType(materialType: MaterialType): Boolean = - repository.existsByMaterialType(materialType) + repository.existsByMaterialType(materialType) override fun hasSimdut(id: Long): Boolean = simdutService.exists(getById(id)) override fun getSimdut(id: Long): ByteArray = simdutService.read(getById(id)) override fun getAllNotMixType(): Collection = getAll().filter { !it.isMixType } override fun getAllIdsWithSimdut(): Collection = - getAllNotMixType() - .filter { simdutService.exists(it) } - .map { it.id!! } + getAllNotMixType() + .filter { simdutService.exists(it) } + .map { it.id!! } override fun save(entity: MaterialSaveDto): Material = - save(with(entity) { - material( - name = entity.name, - inventoryQuantity = entity.inventoryQuantity, - materialType = materialTypeService.getById(materialTypeId), - isMixType = false - ) - }).apply { - if (entity.simdutFile != null && !entity.simdutFile.isEmpty) simdutService.write(this, entity.simdutFile) - } + save(with(entity) { + material( + name = entity.name, + inventoryQuantity = entity.inventoryQuantity, + materialType = materialTypeService.getById(materialTypeId), + isMixType = false + ) + }).apply { + if (entity.simdutFile != null && !entity.simdutFile.isEmpty) simdutService.write(this, entity.simdutFile) + } override fun update(entity: MaterialUpdateDto): Material { val persistedMaterial by lazy { @@ -81,11 +83,11 @@ class MaterialServiceImpl( return update(with(entity) { material( - id = id, - name = if (name != null && name.isNotBlank()) name else persistedMaterial.name, - inventoryQuantity = if (inventoryQuantity != null && inventoryQuantity != Float.MIN_VALUE) inventoryQuantity else persistedMaterial.inventoryQuantity, - isMixType = persistedMaterial.isMixType, - materialType = if (materialTypeId != null) materialTypeService.getById(materialTypeId) else persistedMaterial.materialType + id = id, + name = if (name != null && name.isNotBlank()) name else persistedMaterial.name, + inventoryQuantity = if (inventoryQuantity != null && inventoryQuantity != Float.MIN_VALUE) inventoryQuantity else persistedMaterial.inventoryQuantity, + isMixType = persistedMaterial.isMixType, + materialType = if (materialTypeId != null) materialTypeService.getById(materialTypeId) else persistedMaterial.materialType ) }).apply { if (entity.simdutFile != null && !entity.simdutFile.isEmpty) simdutService.update(entity.simdutFile, this) @@ -101,15 +103,15 @@ class MaterialServiceImpl( override fun getAllForMixCreation(recipeId: Long): Collection { val recipesMixTypes = recipeService.getById(recipeId).mixTypes return getAll() - .filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } } + .filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } } } override fun getAllForMixUpdate(mixId: Long): Collection { val mix = mixService.getById(mixId) val recipesMixTypes = mix.recipe.mixTypes return getAll() - .filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } } - .filter { it.id != mix.mixType.material.id } + .filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } } + .filter { it.id != mix.mixType.material.id } } private fun assertPersistedMaterial(material: Material) { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt index 192c9e1..3a9f108 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeService.kt @@ -1,13 +1,14 @@ package dev.fyloz.colorrecipesexplorer.service import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.model.validation.isNotNullAndNotBlank import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository import org.springframework.stereotype.Service interface MaterialTypeService : - ExternalNamedModelService { + ExternalNamedModelService { /** Checks if a material type with the given [prefix] exists. */ fun existsByPrefix(prefix: String): Boolean @@ -26,16 +27,17 @@ interface MaterialTypeService : @Service class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val materialService: MaterialService) : - AbstractExternalNamedModelService( - repository - ), MaterialTypeService { + AbstractExternalNamedModelService( + repository + ), MaterialTypeService { 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) override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix) override fun isUsedByMaterial(materialType: MaterialType): Boolean = - materialService.existsByMaterialType(materialType) + materialService.existsByMaterialType(materialType) override fun getAllSystemTypes(): Collection = repository.findAllBySystemTypeIs(true) override fun getAllNonSystemType(): Collection = repository.findAllBySystemTypeIs(false) @@ -51,10 +53,10 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma 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 + id = id, + name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name, + prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix, + systemType = false ) }) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt index 2431802..3bcab80 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialService.kt @@ -21,24 +21,25 @@ interface MixMaterialService : ModelService @Service class MixMaterialServiceImpl( - mixMaterialRepository: MixMaterialRepository, - @Lazy val materialService: MaterialService + mixMaterialRepository: MixMaterialRepository, + @Lazy val materialService: MaterialService ) : AbstractModelService(mixMaterialRepository), MixMaterialService { override fun idNotFoundException(id: Long) = mixMaterialIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = mixMaterialIdAlreadyExistsException(id) override fun existsByMaterial(material: Material): Boolean = repository.existsByMaterial(material) override fun create(mixMaterials: Set): Set = - mixMaterials.map(::create).toSet() + mixMaterials.map(::create).toSet() override fun create(mixMaterial: MixMaterialDto): MixMaterial = - mixMaterial( - material = materialService.getById(mixMaterial.materialId), - quantity = mixMaterial.quantity, - position = mixMaterial.position - ) + mixMaterial( + material = materialService.getById(mixMaterial.materialId), + quantity = mixMaterial.quantity, + position = mixMaterial.position + ) override fun updateQuantity(mixMaterial: MixMaterial, quantity: Float) = - update(mixMaterial.apply { - this.quantity = quantity - }) + update(mixMaterial.apply { + this.quantity = quantity + }) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt index b1cafd6..65edbe3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixService.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MixRepository import org.springframework.context.annotation.Lazy @@ -22,14 +23,15 @@ interface MixService : ExternalModelService(mixRepository), - MixService { + MixService { override fun idNotFoundException(id: Long) = mixIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = mixIdAlreadyExistsException(id) override fun getAllByMixType(mixType: MixType): Collection = repository.findAllByMixType(mixType) override fun mixTypeIsShared(mixType: MixType): Boolean = getAllByMixType(mixType).count() > 1 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt index 5b6e682..3cc83ab 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeService.kt @@ -27,30 +27,31 @@ interface MixTypeService : NamedModelService { @Service class MixTypeServiceImpl( - mixTypeRepository: MixTypeRepository, - @Lazy val materialService: MaterialService, - @Lazy val mixService: MixService + mixTypeRepository: MixTypeRepository, + @Lazy val materialService: MaterialService, + @Lazy val mixService: MixService ) : - AbstractNamedModelService(mixTypeRepository), MixTypeService { + AbstractNamedModelService(mixTypeRepository), MixTypeService { override fun idNotFoundException(id: Long) = mixTypeIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = mixTypeIdAlreadyExistsException(id) override fun nameNotFoundException(name: String) = mixTypeNameNotFoundException(name) override fun nameAlreadyExistsException(name: String) = mixTypeNameAlreadyExistsException(name) override fun existsByNameAndMaterialType(name: String, materialType: MaterialType): Boolean = - repository.existsByNameAndMaterialType(name, materialType) + repository.existsByNameAndMaterialType(name, materialType) override fun getByMaterial(material: Material): MixType = - repository.findByMaterial(material) ?: throw nameNotFoundException(material.name) + repository.findByMaterial(material) ?: throw nameNotFoundException(material.name) override fun getByNameAndMaterialType(name: String, materialType: MaterialType): MixType = - repository.findByNameAndMaterialType(name, materialType) - ?: throw MixTypeNameAndMaterialTypeNotFoundException(name, materialType) + repository.findByNameAndMaterialType(name, materialType) + ?: throw MixTypeNameAndMaterialTypeNotFoundException(name, materialType) override fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialType): MixType = - if (existsByNameAndMaterialType(name, materialType)) - getByNameAndMaterialType(name, materialType) - else - saveForNameAndMaterialType(name, materialType) + if (existsByNameAndMaterialType(name, materialType)) + getByNameAndMaterialType(name, materialType) + else + saveForNameAndMaterialType(name, materialType) override fun save(entity: MixType): MixType { if (materialService.existsByName(entity.name)) @@ -59,24 +60,24 @@ class MixTypeServiceImpl( } override fun saveForNameAndMaterialType(name: String, materialType: MaterialType): MixType = - save( - mixType( - name = name, - material = material( - name = name, - inventoryQuantity = Float.MIN_VALUE, - isMixType = true, - materialType = materialType - ) - ) + save( + mixType( + name = name, + material = material( + name = name, + inventoryQuantity = Float.MIN_VALUE, + isMixType = true, + materialType = materialType + ) ) + ) override fun updateForNameAndMaterialType(mixType: MixType, name: String, materialType: MaterialType): MixType = - update(mixType.apply { - this.name = name - material.name = name - material.materialType = materialType - }) + update(mixType.apply { + this.name = name + material.name = name + material.materialType = materialType + }) override fun delete(entity: MixType) { if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMixTypeException(entity) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt index b8db07c..39585f2 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt @@ -1,5 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.model.validation.or import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository @@ -30,14 +31,15 @@ interface RecipeService : ExternalModelService(recipeRepository), - RecipeService { + AbstractExternalModelService(recipeRepository), + RecipeService { override fun idNotFoundException(id: Long) = recipeIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = recipeIdAlreadyExistsException(id) override fun existsByCompany(company: Company): Boolean = repository.existsByCompany(company) override fun getAllByCompany(company: Company): Collection = repository.findAllByCompany(company) @@ -46,14 +48,14 @@ class RecipeServiceImpl( // TODO checks if name is unique in the scope of the [company] return save(with(entity) { recipe( - name = name, - description = description, - color = color, - gloss = gloss, - sample = sample, - approbationDate = approbationDate, - remark = remark ?: "", - company = companyService.getById(companyId) + name = name, + description = description, + color = color, + gloss = gloss, + sample = sample, + approbationDate = approbationDate, + remark = remark ?: "", + company = companyService.getById(companyId) ) }) } @@ -64,17 +66,17 @@ class RecipeServiceImpl( return update(with(entity) { recipe( - id = id, - name = name or persistedRecipe.name, - description = description or persistedRecipe.description, - color = color or persistedRecipe.color, - gloss = gloss ?: persistedRecipe.gloss, - sample = sample ?: persistedRecipe.sample, - approbationDate = approbationDate ?: persistedRecipe.approbationDate, - remark = remark or persistedRecipe.remark, - company = persistedRecipe.company, - mixes = persistedRecipe.mixes, - groupsInformation = updateGroupsInformationSteps(persistedRecipe, entity.steps) + id = id, + name = name or persistedRecipe.name, + description = description or persistedRecipe.description, + color = color or persistedRecipe.color, + gloss = gloss ?: persistedRecipe.gloss, + sample = sample ?: persistedRecipe.sample, + approbationDate = approbationDate ?: persistedRecipe.approbationDate, + remark = remark or persistedRecipe.remark, + company = persistedRecipe.company, + mixes = persistedRecipe.mixes, + groupsInformation = updateGroupsInformationSteps(persistedRecipe, entity.steps) ) }) } @@ -86,17 +88,17 @@ class RecipeServiceImpl( steps.forEach { with(recipe.groupInformationForGroup(it.groupId)) { updatedGroupsInformation.add( - this?.apply { - if (this.steps != null) { - this.steps!!.clear() - this.steps!!.addAll(it.steps) - } else { - this.steps = it.steps.toMutableSet() - } - } ?: recipeGroupInformation( - group = groupService.getById(it.groupId), - steps = it.steps.toMutableSet() - ) + this?.apply { + if (this.steps != null) { + this.steps!!.clear() + this.steps!!.addAll(it.steps) + } else { + this.steps = it.steps.toMutableSet() + } + } ?: recipeGroupInformation( + group = groupService.getById(it.groupId), + steps = it.steps.toMutableSet() + ) ) } } @@ -109,7 +111,7 @@ class RecipeServiceImpl( val recipe = getById(publicDataDto.recipeId) fun noteForGroup(group: EmployeeGroup) = - publicDataDto.notes.firstOrNull { it.groupId == group.id }?.content + publicDataDto.notes.firstOrNull { it.groupId == group.id }?.content // Notes recipe.groupsInformation.map { @@ -128,10 +130,10 @@ class RecipeServiceImpl( } override fun addMix(recipe: Recipe, mix: Mix) = - update(recipe.apply { mixes.add(mix) }) + update(recipe.apply { mixes.add(mix) }) override fun removeMix(mix: Mix): Recipe = - update(mix.recipe.apply { mixes.remove(mix) }) + update(mix.recipe.apply { mixes.remove(mix) }) } const val RECIPE_IMAGES_DIRECTORY = "images/recipe" @@ -152,11 +154,11 @@ interface RecipeImageService { @Service class RecipeImageServiceImpl(val recipeService: RecipeService, val fileService: FileService) : RecipeImageService { override fun getByIdForRecipe(id: Long, recipeId: Long): ByteArray = - try { - fileService.readAsBytes(getPath(id, recipeId)) - } catch (ex: NoSuchFileException) { - throw RecipeImageNotFoundException(id, recipeService.getById(recipeId)) - } + try { + fileService.readAsBytes(getPath(id, recipeId)) + } catch (ex: NoSuchFileException) { + throw RecipeImageNotFoundException(id, recipeService.getById(recipeId)) + } override fun getAllIdsForRecipe(recipeId: Long): Collection { val recipe = recipeService.getById(recipeId) @@ -165,19 +167,19 @@ class RecipeImageServiceImpl(val recipeService: RecipeService, val fileService: return listOf() } return recipeDirectory.listFiles()!! // Should never be null because we check if recipeDirectory is a directory and exists before - .filterNotNull() - .map { it.name.toLong() } + .filterNotNull() + .map { it.name.toLong() } } override fun save(image: MultipartFile, recipeId: Long): Long { /** Gets the next id available for a new image for the recipe with the given [recipeId]. */ fun getNextAvailableId(): Long = - with(getAllIdsForRecipe(recipeId)) { - if (isEmpty()) - 0 - else - maxOrNull()!! + 1L // maxOrNull() cannot return null because existingIds cannot be empty at this point - } + with(getAllIdsForRecipe(recipeId)) { + if (isEmpty()) + 0 + else + maxOrNull()!! + 1L // maxOrNull() cannot return null because existingIds cannot be empty at this point + } val nextAvailableId = getNextAvailableId() fileService.write(image, getPath(nextAvailableId, recipeId)) @@ -185,7 +187,7 @@ class RecipeImageServiceImpl(val recipeService: RecipeService, val fileService: } override fun delete(id: Long, recipeId: Long) = - fileService.delete(getPath(id, recipeId)) + fileService.delete(getPath(id, recipeId)) /** Gets the images directory of the recipe with the given [recipeId]. */ fun getRecipeDirectory(recipeId: Long) = File(fileService.getPath("$RECIPE_IMAGES_DIRECTORY/$recipeId")) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt index 42d941a..d4d6adb 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepService.kt @@ -1,6 +1,7 @@ package dev.fyloz.colorrecipesexplorer.service import dev.fyloz.colorrecipesexplorer.model.RecipeStep +import dev.fyloz.colorrecipesexplorer.model.recipeStepIdAlreadyExistsException import dev.fyloz.colorrecipesexplorer.model.recipeStepIdNotFoundException import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository import org.springframework.stereotype.Service @@ -9,7 +10,8 @@ interface RecipeStepService : ModelService @Service class RecipeStepServiceImpl(recipeStepRepository: RecipeStepRepository) : - AbstractModelService(recipeStepRepository), - RecipeStepService { + AbstractModelService(recipeStepRepository), + RecipeStepService { override fun idNotFoundException(id: Long) = recipeStepIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = recipeStepIdAlreadyExistsException(id) } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt index 0414c96..918247c 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt @@ -65,15 +65,16 @@ abstract class AbstractService>(override val reposito } abstract class AbstractModelService>(repository: R) : - AbstractService(repository), ModelService { + AbstractService(repository), ModelService { protected abstract fun idNotFoundException(id: Long): NotFoundException + protected abstract fun idAlreadyExistsException(id: Long): AlreadyExistsException override fun existsById(id: Long): Boolean = repository.existsById(id) override fun getById(id: Long): E = repository.findByIdOrNull(id) ?: throw idNotFoundException(id) override fun save(entity: E): E { if (entity.id != null && existsById(entity.id!!)) - throw idNotFoundException(entity.id!!) + throw idAlreadyExistsException(entity.id!!) return super.save(entity) } @@ -85,7 +86,7 @@ abstract class AbstractModelService>(repos } override fun deleteById(id: Long) = - delete(getById(id)) // Use delete(entity) to prevent code duplication and to ease testing + delete(getById(id)) // Use delete(entity) to prevent code duplication and to ease testing protected fun assertId(id: Long?) { Assert.notNull(id, "${javaClass.simpleName}.update() was called with a null identifier") @@ -93,7 +94,7 @@ abstract class AbstractModelService>(repos } abstract class AbstractNamedModelService>(repository: R) : - AbstractModelService(repository), NamedModelService { + AbstractModelService(repository), NamedModelService { protected abstract fun nameNotFoundException(name: String): NotFoundException protected abstract fun nameAlreadyExistsException(name: String): AlreadyExistsException @@ -139,23 +140,23 @@ interface ExternalService, U : EntityDto, R : JpaReposito /** An [ExternalService] for entities implementing the [Model] interface. */ interface ExternalModelService, U : EntityDto, R : JpaRepository> : - ModelService, ExternalService + ModelService, ExternalService /** An [ExternalService] for entities implementing the [NamedModel] interface. */ interface ExternalNamedModelService, U : EntityDto, R : JpaRepository> : - NamedModelService, ExternalModelService + NamedModelService, ExternalModelService /** An [AbstractService] with the functionalities of a [ExternalService]. */ @Suppress("unused") abstract class AbstractExternalService, U : EntityDto, R : JpaRepository>(repository: R) : - AbstractService(repository), ExternalService + AbstractService(repository), ExternalService /** An [AbstractModelService] with the functionalities of a [ExternalService]. */ abstract class AbstractExternalModelService, U : EntityDto, R : JpaRepository>( - repository: R + repository: R ) : AbstractModelService(repository), ExternalModelService /** An [AbstractNamedModelService] with the functionalities of a [ExternalService]. */ abstract class AbstractExternalNamedModelService, U : EntityDto, R : NamedJpaRepository>( - repository: R + repository: R ) : AbstractNamedModelService(repository), ExternalNamedModelService diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt index f2d3c38..c6c11fd 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt @@ -10,13 +10,12 @@ import java.io.IOException import java.io.InputStream import java.nio.charset.StandardCharsets import java.nio.file.Files -import java.nio.file.Paths @Service class FileService( - private val resourcesLoader: ResourceLoader, - private val creProperties: CreProperties, - private val logger: Logger + private val resourcesLoader: ResourceLoader, + private val creProperties: CreProperties, + private val logger: Logger ) { /** Reads the resource at the given [path] as a [String]. */ fun readResource(path: String): String = try { @@ -35,18 +34,18 @@ class FileService( /** Reads the file at the given [path] as a [ByteArray]. */ fun readAsBytes(path: String) = - withFileAt(path) { this.readBytes() } + withFileAt(path) { this.readBytes() } /** Writes the given [multipartFile] to the file at the given [path]. */ fun write(multipartFile: MultipartFile, path: String): Boolean = - if (multipartFile.size <= 0) true - else try { - multipartFile.transferTo(create(path).toPath()) - true - } catch (ex: IOException) { - logger.error("Unable to write multipart file", ex) - false - } + if (multipartFile.size <= 0) true + else try { + multipartFile.transferTo(create(path).toPath()) + true + } catch (ex: IOException) { + logger.error("Unable to write multipart file", ex) + false + } /** Creates a new file at the given [path]. If the file already exists, nothing will be done. */ fun create(path: String) = withFileAt(path) { @@ -77,8 +76,8 @@ class FileService( /** Runs the given [block] in the context of a file with the given [path]. */ fun withFileAt(path: String, block: File.() -> T) = - File(path).block() + File(path).block() fun getPath(fileName: String): String = - "${creProperties.workingDirectory}/$fileName" + "${creProperties.workingDirectory}/$fileName" } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt index c483d12..e13c501 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt @@ -12,12 +12,12 @@ const val SIMDUT_DIRECTORY = "simdut" @Service class SimdutService( - private val fileService: FileService, - private val logger: Logger + private val fileService: FileService, + private val logger: Logger ) { /** Checks if the given [material] has a SIMDUT file. */ fun exists(material: Material) = - fileService.exists(getPath(material)) + fileService.exists(getPath(material)) /** Reads the SIMDUT file of the given [material]. */ fun read(material: Material): ByteArray { @@ -46,21 +46,21 @@ class SimdutService( /** Deletes the SIMDUT file of the given [material]. */ fun delete(material: Material) = - fileService.delete(getPath(material)) + fileService.delete(getPath(material)) /** Gets the path of the SIMDUT file of the given [material]. */ fun getPath(material: Material) = - fileService.getPath("$SIMDUT_DIRECTORY/${getSimdutFileName(material)}") + fileService.getPath("$SIMDUT_DIRECTORY/${getSimdutFileName(material)}") /** Gets the name of the SIMDUT file of the given [material]. */ fun getSimdutFileName(material: Material) = - material.id.toString() + material.id.toString() } class SimdutWriteException(material: Material) : - RestException( - "simdut-write", - "Could not write SIMDUT file", - HttpStatus.INTERNAL_SERVER_ERROR, - "Could not write the SIMDUT file for the material ${material.name} to the disk" - ) + RestException( + "simdut-write", + "Could not write SIMDUT file", + HttpStatus.INTERNAL_SERVER_ERROR, + "Could not write the SIMDUT file for the material ${material.name} to the disk" + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/utils/Collections.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/utils/Collections.kt index b28e138..1eab8ff 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/utils/Collections.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/utils/Collections.kt @@ -2,8 +2,8 @@ package dev.fyloz.colorrecipesexplorer.service.utils /** Returns a list containing the result of the given [transform] applied to each item of the [Iterable]. If the given [transform] throws, the [Throwable] will be passed to the given [throwableConsumer]. */ inline fun Iterable.mapMayThrow( - throwableConsumer: (E) -> Unit = {}, - transform: (T) -> R + throwableConsumer: (E) -> Unit = {}, + transform: (T) -> R ): List = this.mapNotNull { try { transform(it) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 44a584f..492010d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -7,7 +7,6 @@ cre.security.jwt-duration=18000000 # Root user cre.security.root.id=9999 cre.security.root.password=password - # Default material types entities.material-types.systemTypes[0].name=Aucun entities.material-types.systemTypes[0].prefix= @@ -16,11 +15,9 @@ entities.material-types.systemTypes[1].name=Base entities.material-types.systemTypes[1].prefix=BAS entities.material-types.systemTypes[1].usepercentages=false entities.material-types.baseName=Base - # Database manager databaseupdater.username=root databaseupdater.password=pass - # DEBUG spring.jpa.show-sql=true # Do not modify diff --git a/src/main/resources/junit-platform.properties b/src/main/resources/junit-platform.properties index d265fd8..2af5bf8 100644 --- a/src/main/resources/junit-platform.properties +++ b/src/main/resources/junit-platform.properties @@ -1 +1 @@ -junit.jupiter.testinstance.lifecycle.default = per_class +junit.jupiter.testinstance.lifecycle.default=per_class diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepositoryTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepositoryTest.kt index fa622f0..7394f85 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepositoryTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MaterialRepositoryTest.kt @@ -10,8 +10,8 @@ import kotlin.test.assertEquals @DataJpaTest(excludeAutoConfiguration = [LiquibaseAutoConfiguration::class]) class MaterialRepositoryTest @Autowired constructor( - private val materialRepository: MaterialRepository, - private val entityManager: TestEntityManager + private val materialRepository: MaterialRepository, + private val entityManager: TestEntityManager ) { // updateInventoryQuantityById() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepositoryTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepositoryTest.kt index e87c425..2362e33 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepositoryTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/repository/MixRepositoryTest.kt @@ -10,8 +10,8 @@ import kotlin.test.assertEquals @DataJpaTest(excludeAutoConfiguration = [LiquibaseAutoConfiguration::class]) class MixRepositoryTest @Autowired constructor( - private val mixRepository: MixRepository, - private val entityManager: TestEntityManager + private val mixRepository: MixRepository, + private val entityManager: TestEntityManager ) { // updateLocationById() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AbstractServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AbstractServiceTest.kt index 3f960a4..f81f41b 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AbstractServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AbstractServiceTest.kt @@ -1,8 +1,9 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException +import dev.fyloz.colorrecipesexplorer.exception.RestException import dev.fyloz.colorrecipesexplorer.model.EntityDto import dev.fyloz.colorrecipesexplorer.model.Model import dev.fyloz.colorrecipesexplorer.model.NamedModel @@ -26,8 +27,8 @@ abstract class AbstractServiceTest, R : JpaRepository protected val entityList: List get() = listOf( - entity, - anotherEntity + entity, + anotherEntity ) @AfterEach @@ -90,7 +91,7 @@ abstract class AbstractServiceTest, R : JpaRepository } abstract class AbstractModelServiceTest, R : JpaRepository> : - AbstractServiceTest1() { + AbstractServiceTest1() { // existsById() @@ -124,23 +125,21 @@ abstract class AbstractModelServiceTest, R : J } @Test - open fun `getById() throws EntityNotFoundException when no entity with the given id exists in the repository`() { + open fun `getById() throws NotFoundException when no entity with the given id exists in the repository`() { whenever(repository.findById(entity.id!!)).doReturn(Optional.empty()) - val exception = assertThrows { service.getById(entity.id!!) } - assertTrue(exception.value is Long) - assertEquals(entity.id, exception.value as Long) + assertThrows { service.getById(entity.id!!) } + .assertErrorCode() } // save() @Test - open fun `save() throws EntityAlreadyExistsException when an entity with the given id exists in the repository`() { - doReturn(true).whenever(repository).existsById(entity.id!!) + open fun `save() throws AlreadyExistsException when an entity with the given id exists in the repository`() { + doReturn(true).whenever(service).existsById(entity.id!!) - val exception = assertThrows { service.save(entity) } - assertTrue(exception.id is Long) - assertEquals(entity.id, exception.id as Long) + assertThrows { service.save(entity) } + .assertErrorCode() } // update() @@ -158,12 +157,11 @@ abstract class AbstractModelServiceTest, R : J } @Test - open fun `update() throws EntityNotFoundException when no entity with the given id exists in the repository`() { + open fun `update() throws NotFoundException when no entity with the given id exists in the repository`() { doReturn(false).whenever(service).existsById(entity.id!!) - val exception = assertThrows { service.update(entity) } - assertTrue(exception.value is Long) - assertEquals(entity.id, exception.value as Long) + assertThrows { service.update(entity) } + .assertErrorCode() } // deleteById() @@ -179,7 +177,7 @@ abstract class AbstractModelServiceTest, R : J } abstract class AbstractNamedModelServiceTest, R : NamedJpaRepository> : - AbstractModelServiceTest() { + AbstractModelServiceTest() { protected abstract val entityWithEntityName: E // existsByName() @@ -214,21 +212,21 @@ abstract class AbstractNamedModelServiceTest { service.getByName(entity.name) } - assertEquals(entity.name, exception.value) + assertThrows { service.getByName(entity.name) } + .assertErrorCode("name") } // save() @Test - open fun `save() throws EntityAlreadyExistsException when an entity with the given name exists`() { + open fun `save() throws AlreadyExistsException when an entity with the given name exists`() { doReturn(true).whenever(service).existsByName(entity.name) - val exception = assertThrows { service.save(entity) } - assertEquals(entity.name, exception.id) + assertThrows { service.save(entity) } + .assertErrorCode("name") } // update() @@ -247,23 +245,21 @@ abstract class AbstractNamedModelServiceTest { service.update(entity) } - assertTrue(exception.value is Long) - assertEquals(entity.id, exception.value as Long) - + assertThrows { service.update(entity) } + .assertErrorCode("name") } @Test - open fun `update() throws EntityAlreadyExistsException when an entity with the updated name exists`() { + open fun `update() throws AlreadyExistsException when an entity with the updated name exists`() { whenever(repository.findByName(entity.name)).doReturn(entityWithEntityName) doReturn(entity).whenever(service).getById(entity.id!!) - val exception = assertThrows { service.update(entity) } - assertEquals(entity.name, exception.id) + assertThrows { service.update(entity) } + .assertErrorCode("name") } // deleteByName() @@ -284,7 +280,7 @@ interface ExternalModelServiceTest { // ==== IMPLEMENTATIONS FOR EXTERNAL SERVICES ==== // Lots of code duplication but I don't have a better solution for now abstract class AbstractExternalModelServiceTest, U : EntityDto, S : ExternalModelService, R : JpaRepository> : - AbstractModelServiceTest(), ExternalModelServiceTest { + AbstractModelServiceTest(), ExternalModelServiceTest { protected abstract val entitySaveDto: N protected abstract val entityUpdateDto: U @@ -296,7 +292,7 @@ abstract class AbstractExternalModelServiceTest, U : } abstract class AbstractExternalNamedModelServiceTest, U : EntityDto, S : ExternalNamedModelService, R : NamedJpaRepository> : - AbstractNamedModelServiceTest(), ExternalModelServiceTest { + AbstractNamedModelServiceTest(), ExternalModelServiceTest { protected abstract val entitySaveDto: N protected abstract val entityUpdateDto: U @@ -307,12 +303,25 @@ abstract class AbstractExternalNamedModelServiceTest> withBaseSaveDtoTest( - entity: E, - entitySaveDto: N, - service: ExternalService, - saveMockMatcher: () -> E = { entity }, - op: () -> Unit = {} + entity: E, + entitySaveDto: N, + service: ExternalService, + saveMockMatcher: () -> E = { entity }, + op: () -> Unit = {} ) { doReturn(entity).whenever(service).save(saveMockMatcher()) doReturn(entity).whenever(entitySaveDto).toEntity() @@ -326,11 +335,11 @@ fun > withBaseSaveDtoTest( } fun > withBaseUpdateDtoTest( - entity: E, - entityUpdateDto: U, - service: ExternalModelService, - updateMockMatcher: () -> E, - op: E.() -> Unit = {} + entity: E, + entityUpdateDto: U, + service: ExternalModelService, + updateMockMatcher: () -> E, + op: E.() -> Unit = {} ) { doAnswer { it.arguments[0] }.whenever(service).update(updateMockMatcher()) doReturn(entity).whenever(entityUpdateDto).toEntity() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt index ac536f8..a844d91 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt @@ -2,13 +2,15 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* import dev.fyloz.colorrecipesexplorer.config.defaultGroupCookieName -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.EmployeeGroupRepository import dev.fyloz.colorrecipesexplorer.repository.EmployeeRepository -import org.junit.jupiter.api.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.springframework.mock.web.MockHttpServletResponse import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UsernameNotFoundException @@ -16,10 +18,13 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import java.util.* import javax.servlet.http.Cookie import javax.servlet.http.HttpServletRequest -import kotlin.test.* +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertTrue class EmployeeServiceTest : - AbstractExternalModelServiceTest() { + AbstractExternalModelServiceTest() { private val passwordEncoder = BCryptPasswordEncoder() override val entity: Employee = employee(passwordEncoder, id = 0L) @@ -31,22 +36,23 @@ class EmployeeServiceTest : override val entityUpdateDto: EmployeeUpdateDto = spy(employeeUpdateDto(id = 0L)) override val repository: EmployeeRepository = mock() - override val service: EmployeeService = spy(EmployeeServiceImpl(repository, passwordEncoder)) + private val employeeGroupService: EmployeeGroupService = mock() + override val service: EmployeeService = spy(EmployeeServiceImpl(repository, employeeGroupService, passwordEncoder)) private val entitySaveDtoEmployee = Employee( - entitySaveDto.id, - entitySaveDto.firstName, - entitySaveDto.lastName, - passwordEncoder.encode(entitySaveDto.password), - isDefaultGroupUser = false, - isSystemUser = false, - group = null, - permissions = entitySaveDto.permissions + entitySaveDto.id, + entitySaveDto.firstName, + entitySaveDto.lastName, + passwordEncoder.encode(entitySaveDto.password), + isDefaultGroupUser = false, + isSystemUser = false, + group = null, + permissions = entitySaveDto.permissions ) @AfterEach override fun afterEach() { - reset(entitySaveDto, entityUpdateDto) + reset(employeeGroupService) super.afterEach() } @@ -73,33 +79,29 @@ class EmployeeServiceTest : // getById() @Test - fun `getById() throws EntityNotFoundException when the corresponding employee is a default group user`() { + fun `getById() throws NotFoundException when the corresponding employee is a default group user`() { whenever(repository.findById(entityDefaultGroupUser.id)).doReturn(Optional.of(entityDefaultGroupUser)) - val exception = assertThrows { + assertThrows { service.getById( - entityDefaultGroupUser.id, - ignoreDefaultGroupUsers = true, - ignoreSystemUsers = false + entityDefaultGroupUser.id, + ignoreDefaultGroupUsers = true, + ignoreSystemUsers = false ) - } - assertTrue(exception.value is Long) - assertEquals(entityDefaultGroupUser.id, exception.value as Long) + }.assertErrorCode() } @Test - fun `getById() throws EntityNotFoundException when the corresponding employee is a system user`() { + fun `getById() throws NotFoundException when the corresponding employee is a system user`() { whenever(repository.findById(entitySystemUser.id)).doReturn(Optional.of(entitySystemUser)) - val exception = assertThrows { + assertThrows { service.getById( - entitySystemUser.id, - ignoreDefaultGroupUsers = false, - ignoreSystemUsers = true + entitySystemUser.id, + ignoreDefaultGroupUsers = false, + ignoreSystemUsers = true ) - } - assertTrue(exception.value is Long) - assertEquals(entitySystemUser.id, exception.value as Long) + }.assertErrorCode() } // getByGroup() @@ -147,18 +149,20 @@ class EmployeeServiceTest : } @Test - fun `save() throws EntityAlreadyExistsException when firstName and lastName exists`() { + fun `save() throws AlreadyExistsException when firstName and lastName exists`() { doReturn(true).whenever(repository).existsByFirstNameAndLastName(entity.firstName, entity.lastName) - val exception = assertThrows { service.save(entity) } - assertEquals("${entity.firstName} ${entity.lastName}", exception.id) + assertThrows { service.save(entity) } + .assertErrorCode("fullName") } @Test override fun `save(dto) calls and returns save() with the created entity`() { - withBaseSaveDtoTest(entity, entitySaveDto, service, {argThat { - this.id == entity.id && this.firstName == entity.firstName && this.lastName == entity.lastName - }}) + withBaseSaveDtoTest(entity, entitySaveDto, service, { + argThat { + this.id == entity.id && this.firstName == entity.firstName && this.lastName == entity.lastName + } + }) } @Test @@ -175,29 +179,27 @@ class EmployeeServiceTest : @Test override fun `update(dto) calls and returns update() with the created entity`() = - withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) + withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) @Test - fun `update() throws EntityAlreadyExistsException when a different employee with the given first name and last name exists`() { + fun `update() throws AlreadyExistsException when a different employee with the given first name and last name exists`() { whenever(repository.findByFirstNameAndLastName(entity.firstName, entity.lastName)).doReturn( - entityDefaultGroupUser + entityDefaultGroupUser ) doReturn(entity).whenever(service).getById(eq(entity.id), any(), any()) - val exception = assertThrows { + assertThrows { service.update( - entity, - true, - ignoreSystemUsers = true + entity, + true, + ignoreSystemUsers = true ) - } - assertTrue(exception.id is String) - assertEquals("${entity.firstName} ${entity.lastName}", exception.id as String) + }.assertErrorCode("fullName") } } class EmployeeGroupServiceTest : - AbstractExternalNamedModelServiceTest() { + AbstractExternalNamedModelServiceTest() { private val employeeService: EmployeeService = mock() override val repository: EmployeeGroupRepository = mock() override val service: EmployeeGroupServiceImpl = spy(EmployeeGroupServiceImpl(employeeService, repository)) @@ -257,13 +259,12 @@ class EmployeeGroupServiceTest : } @Test - fun `getRequestDefaultGroup() throws EntityNotFoundException when the HTTP request does not contains a cookie for the default group`() { + fun `getRequestDefaultGroup() throws NoDefaultGroupException when the HTTP request does not contains a cookie for the default group`() { val request: HttpServletRequest = mock() whenever(request.cookies).doReturn(arrayOf()) - val exception = assertThrows { service.getRequestDefaultGroup(request) } - assertEquals("defaultGroup", exception.value) + assertThrows { service.getRequestDefaultGroup(request) } } // setResponseDefaultGroup() @@ -297,7 +298,7 @@ class EmployeeGroupServiceTest : @Test override fun `update(dto) calls and returns update() with the created entity`() = - withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) + withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) } class EmployeeUserDetailsServiceTest { @@ -317,7 +318,7 @@ class EmployeeUserDetailsServiceTest { fun `loadUserByUsername() calls loadUserByEmployeeId() with the given username as an id`() { whenever(employeeService.getById(eq(employee.id), any(), any())).doReturn(employee) doReturn(User(employee.id.toString(), employee.password, listOf())).whenever(service) - .loadUserByEmployeeId(employee.id) + .loadUserByEmployeeId(employee.id) service.loadUserByUsername(employee.id.toString()) @@ -327,9 +328,7 @@ class EmployeeUserDetailsServiceTest { @Test fun `loadUserByUsername() throws UsernameNotFoundException when no employee with the given id exists`() { whenever(employeeService.getById(eq(employee.id), any(), any())).doThrow( - NotFoundException( - employee.id - ) + employeeIdNotFoundException(employee.id) ) assertThrows { service.loadUserByUsername(employee.id.toString()) } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt index 265151f..a2ebbc7 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt @@ -9,7 +9,7 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue class CompanyServiceTest : - AbstractExternalNamedModelServiceTest() { + AbstractExternalNamedModelServiceTest() { private val recipeService: RecipeService = mock() override val repository: CompanyRepository = mock() override val service: CompanyService = spy(CompanyServiceImpl(repository, recipeService)) @@ -57,7 +57,7 @@ class CompanyServiceTest : @Test override fun `update(dto) calls and returns update() with the created entity`() = - withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) + withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) // delete() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt index 694eb90..aae3b92 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryServiceTest.kt @@ -1,7 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.LowQuantitiesException import dev.fyloz.colorrecipesexplorer.model.* import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test @@ -24,15 +23,15 @@ class InventoryServiceTest { @Test fun `add(materialQuantities) calls add() for each MaterialQuantityDto`() { val materialQuantities = listOf( - materialQuantityDto(materialId = 1, quantity = 1234f), - materialQuantityDto(materialId = 2, quantity = 2345f), - materialQuantityDto(materialId = 3, quantity = 3456f), - materialQuantityDto(materialId = 4, quantity = 4567f) + materialQuantityDto(materialId = 1, quantity = 1234f), + materialQuantityDto(materialId = 2, quantity = 2345f), + materialQuantityDto(materialId = 3, quantity = 3456f), + materialQuantityDto(materialId = 4, quantity = 4567f) ) val storedQuantity = 2000f doAnswer { storedQuantity + (it.arguments[0] as MaterialQuantityDto).quantity }.whenever(service) - .add(any()) + .add(any()) val found = service.add(materialQuantities) @@ -51,8 +50,8 @@ class InventoryServiceTest { val found = service.add(this) verify(materialService).updateQuantity( - argThat { this.id == this@withGivenQuantities.material }, - eq(this.quantity) + argThat { this.id == this@withGivenQuantities.material }, + eq(this.quantity) ) assertEquals(updatedQuantity, found) } @@ -66,15 +65,15 @@ class InventoryServiceTest { val materialPercents = material(id = 1L, materialType = materialType(usePercentages = true)) val mixRatio = mixRatio(ratio = 1.5f) val mix = mix( - id = mixRatio.id, - mixMaterials = mutableSetOf( - mixMaterial(id = 0L, material = material, quantity = 1000f, position = 0), - mixMaterial(id = 1L, material = materialPercents, quantity = 50f, position = 1) - ) + id = mixRatio.id, + mixMaterials = mutableSetOf( + mixMaterial(id = 0L, material = material, quantity = 1000f, position = 0), + mixMaterial(id = 1L, material = materialPercents, quantity = 50f, position = 1) + ) ) val expectedQuantities = mapOf( - 0L to 1500f, - 1L to 750f + 0L to 1500f, + 1L to 750f ) whenever(mixService.getById(mix.id!!)).doReturn(mix) @@ -98,15 +97,15 @@ class InventoryServiceTest { @Test fun `deduct(materialQuantities) calls deduct() for each MaterialQuantityDto`() { val materialQuantities = listOf( - materialQuantityDto(materialId = 1, quantity = 1234f), - materialQuantityDto(materialId = 2, quantity = 2345f), - materialQuantityDto(materialId = 3, quantity = 3456f), - materialQuantityDto(materialId = 4, quantity = 4567f) + materialQuantityDto(materialId = 1, quantity = 1234f), + materialQuantityDto(materialId = 2, quantity = 2345f), + materialQuantityDto(materialId = 3, quantity = 3456f), + materialQuantityDto(materialId = 4, quantity = 4567f) ) val storedQuantity = 5000f doAnswer { storedQuantity - (it.arguments[0] as MaterialQuantityDto).quantity }.whenever(service) - .deduct(any()) + .deduct(any()) val found = service.deduct(materialQuantities) @@ -117,22 +116,20 @@ class InventoryServiceTest { } @Test - fun `deduct(materialQuantities) throws LowQuantitiesException when there is not enough inventory of a given material`() { + fun `deduct(materialQuantities) throws MultiplesNotEnoughInventoryException when there is not enough inventory of a given material`() { val materialQuantities = listOf( - materialQuantityDto(materialId = 1, quantity = 1234f), - materialQuantityDto(materialId = 2, quantity = 2345f), - materialQuantityDto(materialId = 3, quantity = 3456f), - materialQuantityDto(materialId = 4, quantity = 4567f) + materialQuantityDto(materialId = 1, quantity = 1234f), + materialQuantityDto(materialId = 2, quantity = 2345f), + materialQuantityDto(materialId = 3, quantity = 3456f), + materialQuantityDto(materialId = 4, quantity = 4567f) ) val inventoryQuantity = 3000f - val lowQuantities = materialQuantities.filter { it.quantity > inventoryQuantity } materialQuantities.forEach { withGivenQuantities(inventoryQuantity, it) } - val exception = assertThrows { service.deduct(materialQuantities) } - assertTrue { exception.materialQuantities.containsAll(lowQuantities) } + assertThrows { service.deduct(materialQuantities) } } @Test @@ -144,26 +141,25 @@ class InventoryServiceTest { val found = service.deduct(this) verify(materialService).updateQuantity( - argThat { this.id == this@withGivenQuantities.material }, - eq(-this.quantity) + argThat { this.id == this@withGivenQuantities.material }, + eq(-this.quantity) ) assertEquals(updatedQuantity, found) } } @Test - fun `deduct(materialQuantity) throws LowQuantityException when there is not enough inventory of the given material`() { + fun `deduct(materialQuantity) throws NotEnoughInventoryException when there is not enough inventory of the given material`() { withGivenQuantities(0f, 1000f) { - val exception = assertThrows { service.deduct(this) } - assertEquals(this, exception.materialQuantity) + assertThrows { service.deduct(this) } } } private fun withGivenQuantities( - stored: Float, - quantity: Float, - materialId: Long = 0L, - test: MaterialQuantityDto.(Float) -> Unit = {} + stored: Float, + quantity: Float, + materialId: Long = 0L, + test: MaterialQuantityDto.(Float) -> Unit = {} ) { val materialQuantity = materialQuantityDto(materialId = materialId, quantity = quantity) @@ -171,9 +167,9 @@ class InventoryServiceTest { } private fun withGivenQuantities( - stored: Float, - materialQuantity: MaterialQuantityDto, - test: MaterialQuantityDto.(Float) -> Unit = {} + stored: Float, + materialQuantity: MaterialQuantityDto, + test: MaterialQuantityDto.(Float) -> Unit = {} ) { val material = material(id = materialQuantity.material, inventoryQuantity = stored) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt index 43a1b76..6da874e 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt @@ -1,7 +1,7 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MaterialRepository import dev.fyloz.colorrecipesexplorer.service.files.SimdutService @@ -14,14 +14,14 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue class MaterialServiceTest : - AbstractExternalNamedModelServiceTest() { + AbstractExternalNamedModelServiceTest() { override val repository: MaterialRepository = mock() private val simdutService: SimdutService = mock() private val recipeService: RecipeService = mock() private val mixService: MixService = mock() private val materialTypeService: MaterialTypeService = mock() override val service: MaterialService = - spy(MaterialServiceImpl(repository, simdutService, recipeService, mixService, materialTypeService)) + spy(MaterialServiceImpl(repository, simdutService, recipeService, mixService, materialTypeService)) override val entity: Material = material(id = 0L, name = "material") override val anotherEntity: Material = material(id = 1L, name = "another material") @@ -99,19 +99,19 @@ class MaterialServiceTest : @Test fun `getAllIdsWithSimdut() returns a list containing the identifier of every material with a SIMDUT file`() { val materials = listOf( - material(id = 0L), - material(id = 1L), - material(id = 2L), - material(id = 3L) + material(id = 0L), + material(id = 1L), + material(id = 2L), + material(id = 3L) ) val hasSimdut = mapOf( - *materials - .map { it.id!! to it.evenId } - .toTypedArray() + *materials + .map { it.id!! to it.evenId } + .toTypedArray() ) val expectedIds = hasSimdut - .filter { it.value } - .map { it.key } + .filter { it.value } + .map { it.key } whenever(simdutService.exists(any())).doAnswer { hasSimdut[(it.arguments[0] as Material).id] } doReturn(materials).whenever(service).getAllNotMixType() @@ -124,11 +124,11 @@ class MaterialServiceTest : // save() @Test - fun `save() throws EntityAlreadyExistsException when a material with the given name exists in the repository`() { + fun `save() throws AlreadyExistsException when a material with the given name exists in the repository`() { doReturn(true).whenever(service).existsByName(entity.name) - val exception = assertThrows { service.save(entity) } - assertEquals(entity.name, exception.id) + assertThrows { service.save(entity) } + .assertErrorCode("name") } @Test @@ -152,15 +152,15 @@ class MaterialServiceTest : // update() @Test - fun `update() throws EntityAlreadyExistsException when another material with the updated name exists in the repository`() { + fun `update() throws AlreadyExistsException when another material with the updated name exists in the repository`() { val material = material(id = 0L, name = "name") val anotherMaterial = material(id = 1L, name = "name") whenever(repository.findByName(material.name)).doReturn(anotherMaterial) doReturn(entity).whenever(service).getById(material.id!!) - val exception = assertThrows { service.update(material) } - assertEquals(material.name, exception.id) + assertThrows { service.update(material) } + .assertErrorCode("name") } // updateQuantity() @@ -186,7 +186,7 @@ class MaterialServiceTest : val anotherMixTypeMaterial = material(id = 2L, isMixType = true) val materials = listOf(normalMaterial, mixTypeMaterial, anotherMixTypeMaterial) val recipe = - recipe(id = 0L, mixes = mutableListOf(mix(mixType = mixType(id = 0L, material = mixTypeMaterial)))) + recipe(id = 0L, mixes = mutableListOf(mix(mixType = mixType(id = 0L, material = mixTypeMaterial)))) whenever(recipeService.getById(recipe.id!!)).doReturn(recipe) doReturn(materials).whenever(service).getAll() @@ -244,6 +244,6 @@ class MaterialServiceTest : } /** Utility property to check if the identifier of the given [Material] is even. */ - val Material.evenId: Boolean + private val Material.evenId: Boolean get() = this.id!! % 2 == 0L } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt index 608e180..6846394 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt @@ -1,7 +1,7 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.EntityAlreadyExistsException +import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository @@ -13,7 +13,7 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue class MaterialTypeServiceTest : - AbstractExternalNamedModelServiceTest() { + AbstractExternalNamedModelServiceTest() { override val repository: MaterialTypeRepository = mock() private val materialService: MaterialService = mock() override val service: MaterialTypeService = spy(MaterialTypeServiceImpl(repository, materialService)) @@ -24,7 +24,7 @@ class MaterialTypeServiceTest : 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")) + spy(materialTypeUpdateDto(id = 0L, name = "material type", prefix = "MAT")) @AfterEach override fun afterEach() { @@ -106,18 +106,18 @@ class MaterialTypeServiceTest : // saveMaterialType() @Test - fun `saveMaterialType() throws EntityAlreadyExistsException when a material type with the given prefix already exists`() { + fun `saveMaterialType() throws AlreadyExistsException when a material type with the given prefix already exists`() { doReturn(true).whenever(service).existsByPrefix(entity.prefix) - val exception = assertThrows { service.save(entity) } - assertEquals(entity.prefix, exception.id) + assertThrows { service.save(entity) } + .assertErrorCode("prefix") } // update() @Test override fun `update(dto) calls and returns update() with the created entity`() = - withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) + withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) override fun `update() saves in the repository and returns the updated value`() { whenever(repository.save(entity)).doReturn(entity) @@ -132,35 +132,34 @@ class MaterialTypeServiceTest : assertEquals(entity, found) } - override fun `update() throws EntityNotFoundException when no entity with the given id exists in the repository`() { + 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(service).existsById(entity.id!!) doReturn(null).whenever(service).getById(entity.id!!) - val exception = assertThrows { service.update(entity) } - assertTrue(exception.value is Long) - assertEquals(entity.id, exception.value as Long) + assertThrows { service.update(entity) } + .assertErrorCode() } - override fun `update() throws EntityAlreadyExistsException when an entity with the updated name exists`() { + 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(service).existsById(entity.id!!) doReturn(entity).whenever(service).getById(entity.id!!) - val exception = assertThrows { service.update(entity) } - assertEquals(entity.name, exception.id) + assertThrows { service.update(entity) } + .assertErrorCode("name") } @Test - fun `update() throws EntityAlreadyExistsException when an entity with the updated prefix exists`() { + 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(service).getById(entity.id!!) - val exception = assertThrows { service.update(entity) } - assertEquals(entity.prefix, exception.id) + assertThrows { service.update(entity) } + .assertErrorCode("prefix") } // delete() @@ -173,8 +172,4 @@ class MaterialTypeServiceTest : verify(repository).delete(entity) } - - override fun `deleteById() deletes the entity with the given id in the repository`() { - super.`deleteById() deletes the entity with the given id in the repository`() - } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialServiceTest.kt index 464aff2..35259e4 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixMaterialServiceTest.kt @@ -43,18 +43,18 @@ class MixMaterialServiceTest : AbstractModelServiceTest()) @@ -81,8 +81,8 @@ class MixMaterialServiceTest : AbstractModelServiceTest()) @@ -85,9 +85,9 @@ class MixServiceTest : AbstractExternalModelServiceTest Unit + scope: MixUpdateDtoTestScope = MixUpdateDtoTestScope(), + sharedMixType: Boolean = false, + op: MixUpdateDtoTestScope.() -> Unit ) { with(scope) { doReturn(true).whenever(service).existsById(mix.id!!) @@ -127,7 +127,7 @@ class MixServiceTest : AbstractExternalModelServiceTest>())).doAnswer { (it.arguments[0] as Set).map { dto -> mixMaterial( - material = material(id = dto.materialId), - quantity = dto.quantity, - position = dto.position + material = material(id = dto.materialId), + quantity = dto.quantity, + position = dto.position ) }.toSet() } @@ -189,10 +189,10 @@ class MixServiceTest : AbstractExternalModelServiceTest { service.getByMaterial(material) } - assertEquals(material.name, exception.value) + assertThrows { service.getByMaterial(material) } + .assertErrorCode("name") } // getByNameAndMaterialType() @@ -100,11 +100,11 @@ class MixTypeServiceTest : AbstractNamedModelServiceTest { service.save(entity) } - assertEquals(entity.name, exception.id) + assertThrows { service.save(entity) } + .assertErrorCode("name") } // saveForNameAndMaterialType() diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt index 3851792..3bb751e 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt @@ -1,7 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.* import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository import dev.fyloz.colorrecipesexplorer.service.files.FileService @@ -16,7 +15,7 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue class RecipeServiceTest : - AbstractExternalModelServiceTest() { + AbstractExternalModelServiceTest() { override val repository: RecipeRepository = mock() private val companyService: CompanyService = mock() private val mixService: MixService = mock() @@ -79,22 +78,22 @@ class RecipeServiceTest : @Test override fun `update(dto) calls and returns update() with the created entity`() = - withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) + withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() }) // updatePublicData() @Test fun `updatePublicData() updates the notes of a recipe groups information according to the RecipePublicDataDto`() { val recipe = recipe( - id = 0L, groupsInformation = setOf( - recipeGroupInformation(id = 0L, group = employeeGroup(id = 1L), note = "Old note"), - recipeGroupInformation(id = 1L, group = employeeGroup(id = 2L), note = "Another note"), - recipeGroupInformation(id = 2L, group = employeeGroup(id = 3L), note = "Up to date note") - ) + id = 0L, groupsInformation = setOf( + recipeGroupInformation(id = 0L, group = employeeGroup(id = 1L), note = "Old note"), + recipeGroupInformation(id = 1L, group = employeeGroup(id = 2L), note = "Another note"), + recipeGroupInformation(id = 2L, group = employeeGroup(id = 3L), note = "Up to date note") + ) ) val notes = setOf( - noteDto(groupId = 1, content = "Note 1"), - noteDto(groupId = 2, content = null) + noteDto(groupId = 1, content = "Note 1"), + noteDto(groupId = 2, content = null) ) val publicData = recipePublicDataDto(recipeId = recipe.id!!, notes = notes) @@ -114,10 +113,12 @@ class RecipeServiceTest : @Test fun `updatePublicData() update the location of a recipe mixes in the mix service according to the RecipePublicDataDto`() { - val publicData = recipePublicDataDto(mixesLocation = setOf( + val publicData = recipePublicDataDto( + mixesLocation = setOf( mixLocationDto(mixId = 0L, location = "Loc 1"), mixLocationDto(mixId = 1L, location = "Loc 2") - )) + ) + ) service.updatePublicData(publicData) @@ -193,13 +194,11 @@ class RecipeImageServiceTest { } @Test - fun `getByIdForRecipe() throws EntityNotFoundException when no image with the given recipe and image id exists`() { + fun `getByIdForRecipe() throws RecipeImageNotFoundException when no image with the given recipe and image id exists`() { doReturn(imagePath).whenever(service).getPath(imageId, recipeId) whenever(fileService.readAsBytes(imagePath)).doAnswer { throw NoSuchFileException(imagePath) } - val exception = - assertThrows { service.getByIdForRecipe(imageId, recipeId) } - assertEquals("$recipeId/$imageId", exception.value) + assertThrows { service.getByIdForRecipe(imageId, recipeId) } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepServiceTest.kt index d7180aa..714b4a7 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeStepServiceTest.kt @@ -7,7 +7,7 @@ import dev.fyloz.colorrecipesexplorer.model.recipeStep import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository class RecipeStepServiceTest : - AbstractModelServiceTest() { + AbstractModelServiceTest() { override val repository: RecipeStepRepository = mock() override val service: RecipeStepService = spy(RecipeStepServiceImpl(repository)) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt index 9cfc0f0..c80391b 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt @@ -1,7 +1,6 @@ package dev.fyloz.colorrecipesexplorer.service.files import com.nhaarman.mockitokotlin2.* -import dev.fyloz.colorrecipesexplorer.exception.SimdutWriteException import dev.fyloz.colorrecipesexplorer.model.Material import dev.fyloz.colorrecipesexplorer.model.material import org.junit.jupiter.api.AfterEach @@ -26,7 +25,7 @@ class SimdutServiceTest { @JvmName("withNullableMaterialPath") private inline fun withMaterialPath(material: Material? = null, exists: Boolean = true, test: (String) -> Unit) = - withMaterialPath(material ?: this.material, exists, test) + withMaterialPath(material ?: this.material, exists, test) private inline fun withMaterialPath(material: Material, exists: Boolean = true, test: (String) -> Unit) { val path = "data/simdut/${material.id}" @@ -109,8 +108,7 @@ class SimdutServiceTest { whenever(fileService.write(simdutMultipart, path)).doReturn(false) - val exception = assertThrows { service.write(material, simdutMultipart) } - assertEquals(material, exception.material) + assertThrows { service.write(material, simdutMultipart) } } } From 689bd2a602bc27df82e2915a2a1f7b270f6daf2b Mon Sep 17 00:00:00 2001 From: FyloZ Date: Tue, 13 Apr 2021 11:11:14 -0400 Subject: [PATCH 3/4] Correction des tests --- .../config/WebSecurityConfig.kt | 3 ++- .../model/EmployeePermission.kt | 1 + .../colorrecipesexplorer/service/Service.kt | 4 ---- .../service/AbstractServiceTest.kt | 10 ---------- .../service/CompanyServiceTest.kt | 18 +++++++++++++++++- .../service/MaterialServiceTest.kt | 18 +++++++++++++++++- .../service/MaterialTypeServiceTest.kt | 19 ++++++++++++++----- .../service/MixServiceTest.kt | 19 +++++++++++++++++-- .../service/MixTypeServiceTest.kt | 19 +++++++++++++++++-- .../service/RecipeServiceTest.kt | 2 +- 10 files changed, 86 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt index 681bf0e..0d4834c 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/WebSecurityConfig.kt @@ -7,6 +7,7 @@ import dev.fyloz.colorrecipesexplorer.model.EmployeeLoginRequest import dev.fyloz.colorrecipesexplorer.model.EmployeePermission import dev.fyloz.colorrecipesexplorer.service.EmployeeService import dev.fyloz.colorrecipesexplorer.service.EmployeeServiceImpl +import dev.fyloz.colorrecipesexplorer.service.EmployeeUserDetailsService import dev.fyloz.colorrecipesexplorer.service.EmployeeUserDetailsServiceImpl import io.jsonwebtoken.ExpiredJwtException import io.jsonwebtoken.Jwts @@ -219,7 +220,7 @@ class JwtAuthenticationFilter( } class JwtAuthorizationFilter( - private val userDetailsService: EmployeeUserDetailsServiceImpl, + private val userDetailsService: EmployeeUserDetailsService, private val securityConfigurationProperties: SecurityConfigurationProperties, authenticationManager: AuthenticationManager ) : BasicAuthenticationFilter(authenticationManager) { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt index a12ae7b..e6ef1b7 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/EmployeePermission.kt @@ -2,6 +2,7 @@ package dev.fyloz.colorrecipesexplorer.model import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority +import java.util.* enum class EmployeePermission( val impliedPermissions: List = listOf(), diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt index 918247c..ef45f8e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/Service.kt @@ -51,9 +51,6 @@ interface NamedModelService> : ModelServ /** Gets the entity with the given [name]. */ fun getByName(name: String): E - - /** Deletes the entity with the given [name]. */ - fun deleteByName(name: String) } @@ -100,7 +97,6 @@ abstract class AbstractNamedModelService { service.update(entity) } - .assertErrorCode("name") } @Test @@ -261,15 +260,6 @@ abstract class AbstractNamedModelServiceTest { service.update(entity) } .assertErrorCode("name") } - - // deleteByName() - - @Test - open fun `deleteByName() deletes the entity with the given name in the repository`() { - service.deleteByName(entity.name) - - verify(repository).deleteByName(entity.name) - } } interface ExternalModelServiceTest { diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt index a2ebbc7..75dfba1 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/CompanyServiceTest.kt @@ -61,7 +61,23 @@ class CompanyServiceTest : // delete() + override fun `delete() deletes in the repository`() { + whenCanBeDeleted { + super.`delete() deletes in the repository`() + } + } + + // deleteById() + override fun `deleteById() deletes the entity with the given id in the repository`() { - super.`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() } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt index 6da874e..9def652 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialServiceTest.kt @@ -239,11 +239,27 @@ class MaterialServiceTest : // delete() + override fun `delete() deletes in the repository`() { + whenCanBeDeleted { + super.`delete() deletes in the repository`() + } + } + + // deleteById() + override fun `deleteById() deletes the entity with the given id in the repository`() { - super.`deleteById() deletes the entity with the given id in the repository`() + whenCanBeDeleted { + super.`deleteById() deletes the entity with the given id in the repository`() + } } /** Utility property to check if the identifier of the given [Material] is even. */ private val Material.evenId: Boolean get() = this.id!! % 2 == 0L + + private fun whenCanBeDeleted(id: Long = any(), test: () -> Unit) { + whenever(repository.canBeDeleted(id)).doReturn(true) + + test() + } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt index 6846394..797c1d0 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialTypeServiceTest.kt @@ -164,12 +164,21 @@ class MaterialTypeServiceTest : // delete() - @Test - fun `delete() calls delete() in the repository`() { - doReturn(false).whenever(service).isUsedByMaterial(entity) + override fun `delete() deletes in the repository`() { + whenCanBeDeleted { + super.`delete() deletes in the repository`() + } + } - service.delete(entity) + 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`() + } + } - verify(repository).delete(entity) + private fun whenCanBeDeleted(id: Long = any(), test: () -> Unit) { + whenever(repository.canBeDeleted(id)).doReturn(true) + + test() } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixServiceTest.kt index e4bdca0..22a41b0 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixServiceTest.kt @@ -215,10 +215,25 @@ class MixServiceTest : AbstractExternalModelServiceTest Unit) { + whenever(repository.canBeDeleted(id)).doReturn(true) + + test() } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt index 8dac13d..b57d4ce 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/MixTypeServiceTest.kt @@ -149,8 +149,23 @@ class MixTypeServiceTest : AbstractNamedModelServiceTest Unit) { + whenever(repository.canBeDeleted(id)).doReturn(true) + + test() } } diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt index 3bb751e..72f60d2 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeServiceTest.kt @@ -196,12 +196,12 @@ class RecipeImageServiceTest { @Test fun `getByIdForRecipe() throws RecipeImageNotFoundException when no image with the given recipe and image id exists`() { doReturn(imagePath).whenever(service).getPath(imageId, recipeId) + whenever(recipeService.getById(recipeId)).doReturn(recipe) whenever(fileService.readAsBytes(imagePath)).doAnswer { throw NoSuchFileException(imagePath) } assertThrows { service.getByIdForRecipe(imageId, recipeId) } } - // getAllIdsForRecipe() @Test From 11980d06ac786313c911e740820ad70b4d9df855 Mon Sep 17 00:00:00 2001 From: FyloZ Date: Tue, 13 Apr 2021 16:38:16 -0400 Subject: [PATCH 4/4] =?UTF-8?q?Ajout=20de=20l'identifiant=20des=20produits?= =?UTF-8?q?=20dans=20les=20errors=20de=20quantit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/fyloz/colorrecipesexplorer/service/AccountService.kt | 2 +- .../dev/fyloz/colorrecipesexplorer/service/InventoryService.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt index 183ccd9..d3b81ba 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt @@ -129,7 +129,7 @@ class EmployeeServiceImpl( override fun saveDefaultGroupEmployee(group: EmployeeGroup) { save( - Employee( + employee( id = 1000000L + group.id!!, firstName = group.name, lastName = "EmployeeModel", diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt index 965425a..cefb0b0 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/InventoryService.kt @@ -95,6 +95,7 @@ class NotEnoughInventoryException(quantity: Float, material: Material) : "Cannot deduct ${quantity}mL of ${material.name} because there is only ${material.inventoryQuantity}mL in inventory", mapOf( "material" to material.name, + "materialId" to material.id.toString(), "requestQuantity" to quantity, "availableQuantity" to material.inventoryQuantity )