Ré-implémentation des erreurs pour suivre un standard.
This commit is contained in:
parent
211fdb1895
commit
0321dd45f6
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, Any> = 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<MaterialQuantityDto>) :
|
||||
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<Any> {
|
||||
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(
|
||||
|
|
|
@ -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<EmployeePermission> = mutableSetOf(),
|
||||
|
||||
@Column(name = "last_login_time")
|
||||
var lastLoginTime: LocalDateTime? = null
|
||||
) : Model {
|
||||
@get:JsonProperty("permissions")
|
||||
val flatPermissions: Set<EmployeePermission>
|
||||
get() = permissions
|
||||
.flatMap { it.flat() }
|
||||
.filter { !it.deprecated }
|
||||
.toMutableSet()
|
||||
.apply {
|
||||
if (group != null) this.addAll(group!!.flatPermissions)
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val authorities: Set<GrantedAuthority>
|
||||
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<EmployeePermission> = mutableSetOf()
|
||||
) : EntityDto<Employee>
|
||||
|
||||
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<EmployeePermission>?
|
||||
) : EntityDto<Employee>
|
||||
|
||||
|
||||
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<EmployeePermission> = mutableSetOf(),
|
||||
) : NamedModel {
|
||||
@get:JsonProperty("permissions")
|
||||
val flatPermissions: Set<EmployeePermission>
|
||||
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<EmployeePermission>
|
||||
) : EntityDto<EmployeeGroup> {
|
||||
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<EmployeePermission>
|
||||
) : EntityDto<EmployeeGroup> {
|
||||
override fun toEntity(): EmployeeGroup =
|
||||
EmployeeGroup(id, name, permissions)
|
||||
}
|
||||
|
||||
|
||||
data class EmployeeLoginRequest(val id: Long, val password: String)
|
||||
|
||||
|
||||
enum class EmployeePermission(
|
||||
val impliedPermissions: List<EmployeePermission> = 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<EmployeePermission> {
|
||||
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<EmployeePermission> = 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<EmployeePermission> = 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<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeUpdateDto.() -> Unit = {}
|
||||
) = EmployeeUpdateDto(id, firstName, lastName, groupId, permissions).apply(op)
|
||||
|
||||
fun employeeGroup(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroup.() -> Unit = {}
|
||||
) = EmployeeGroup(id, name, permissions).apply(op)
|
||||
|
||||
fun employeeGroupSaveDto(
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupSaveDto.() -> Unit = {}
|
||||
) = EmployeeGroupSaveDto(name, permissions).apply(op)
|
||||
|
||||
fun employeeGroupUpdateDto(
|
||||
id: Long = 0L,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupUpdateDto.() -> Unit = {}
|
||||
) = EmployeeGroupUpdateDto(id, name, permissions).apply(op)
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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<EmployeePermission> = mutableSetOf(),
|
||||
|
||||
@Column(name = "last_login_time")
|
||||
var lastLoginTime: LocalDateTime? = null
|
||||
) : Model {
|
||||
@get:JsonProperty("permissions")
|
||||
val flatPermissions: Set<EmployeePermission>
|
||||
get() = permissions
|
||||
.flatMap { it.flat() }
|
||||
.filter { !it.deprecated }
|
||||
.toMutableSet()
|
||||
.apply {
|
||||
if (group != null) this.addAll(group!!.flatPermissions)
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val authorities: Set<GrantedAuthority>
|
||||
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<EmployeePermission> = mutableSetOf()
|
||||
) : EntityDto<Employee>
|
||||
|
||||
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<EmployeePermission>?
|
||||
) : EntityDto<Employee>
|
||||
|
||||
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<EmployeePermission> = 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<EmployeePermission> = 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<EmployeePermission> = 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"
|
||||
)
|
|
@ -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<EmployeePermission> = mutableSetOf(),
|
||||
) : NamedModel {
|
||||
@get:JsonProperty("permissions")
|
||||
val flatPermissions: Set<EmployeePermission>
|
||||
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<EmployeePermission>
|
||||
) : EntityDto<EmployeeGroup> {
|
||||
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<EmployeePermission>
|
||||
) : EntityDto<EmployeeGroup> {
|
||||
override fun toEntity(): EmployeeGroup =
|
||||
EmployeeGroup(id, name, permissions)
|
||||
}
|
||||
|
||||
fun employeeGroup(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroup.() -> Unit = {}
|
||||
) = EmployeeGroup(id, name, permissions).apply(op)
|
||||
|
||||
fun employeeGroupSaveDto(
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupSaveDto.() -> Unit = {}
|
||||
) = EmployeeGroupSaveDto(name, permissions).apply(op)
|
||||
|
||||
fun employeeGroupUpdateDto(
|
||||
id: Long = 0L,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = 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"
|
||||
)
|
||||
|
|
@ -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<EmployeePermission> = 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<EmployeePermission> {
|
||||
return mutableSetOf(this).apply {
|
||||
impliedPermissions.forEach {
|
||||
addAll(it.flat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts the given [EmployeePermission] to a [GrantedAuthority]. */
|
||||
fun EmployeePermission.toAuthority(): GrantedAuthority {
|
||||
return SimpleGrantedAuthority(name)
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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<Employee, EmployeeSaveDto, EmployeeUpdateDto, EmployeeRepository>(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<Employee> =
|
||||
|
@ -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<AbstractExternalModelService>.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<AbstractExternalModelService>.update(entity)
|
||||
|
@ -219,11 +218,14 @@ const val defaultGroupCookieMaxAge = 10 * 365 * 24 * 60 * 60 // 10 ans
|
|||
class EmployeeGroupServiceImpl(
|
||||
val employeeService: EmployeeService,
|
||||
employeeGroupRepository: EmployeeGroupRepository
|
||||
) :
|
||||
AbstractExternalNamedModelService<EmployeeGroup, EmployeeGroupSaveDto, EmployeeGroupUpdateDto, EmployeeGroupRepository>(
|
||||
employeeGroupRepository
|
||||
),
|
||||
) : AbstractExternalNamedModelService<EmployeeGroup, EmployeeGroupSaveDto, EmployeeGroupUpdateDto, EmployeeGroupRepository>(
|
||||
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<Employee> =
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Company, CompanySaveDto, CompanyUpdateDto, CompanyRepository>(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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MaterialQuantityDto>): Collection<MaterialQuantityDto> {
|
||||
val thrown = mutableListOf<MaterialQuantityDto>()
|
||||
val thrown = mutableListOf<NotEnoughInventoryException>()
|
||||
val updatedQuantities =
|
||||
materialQuantities.mapMayThrow<MaterialQuantityDto, MaterialQuantityDto, LowQuantityException>(
|
||||
{ thrown.add(it.materialQuantity) }
|
||||
materialQuantities.mapMayThrow<MaterialQuantityDto, MaterialQuantityDto, NotEnoughInventoryException>(
|
||||
{ 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<NotEnoughInventoryException>) :
|
||||
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 }
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeRepository> {
|
||||
|
@ -38,6 +29,10 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma
|
|||
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeRepository>(
|
||||
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<AbstractExternalNamedModelService>.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<AbstractExternalNamedModelService>.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<MaterialTypeProperties.MaterialTypeProperty>) {
|
||||
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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<MixMaterial, MixMaterialRepository>(mixMaterialRepository), MixMaterialService {
|
||||
override fun idNotFoundException(id: Long) = mixMaterialIdNotFoundException(id)
|
||||
|
||||
override fun existsByMaterial(material: Material): Boolean = repository.existsByMaterial(material)
|
||||
override fun create(mixMaterials: Set<MixMaterialDto>): Set<MixMaterial> =
|
||||
mixMaterials.map(::create).toSet()
|
||||
|
|
|
@ -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<Mix, MixRepository>(mixRepository),
|
||||
MixService {
|
||||
override fun idNotFoundException(id: Long) = mixIdNotFoundException(id)
|
||||
|
||||
override fun getAllByMixType(mixType: MixType): Collection<Mix> = 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MixType, MixTypeRepository>(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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Recipe, RecipeSaveDto, RecipeUpdateDto, RecipeRepository>(recipeRepository),
|
||||
RecipeService {
|
||||
override fun idNotFoundException(id: Long) = recipeIdNotFoundException(id)
|
||||
|
||||
override fun existsByCompany(company: Company): Boolean = repository.existsByCompany(company)
|
||||
override fun getAllByCompany(company: Company): Collection<Recipe> = 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<Long> {
|
||||
|
|
|
@ -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<RecipeStep, RecipeStepRepository>
|
|||
@Service
|
||||
class RecipeStepServiceImpl(recipeStepRepository: RecipeStepRepository) :
|
||||
AbstractModelService<RecipeStep, RecipeStepRepository>(recipeStepRepository),
|
||||
RecipeStepService
|
||||
RecipeStepService {
|
||||
override fun idNotFoundException(id: Long) = recipeStepIdNotFoundException(id)
|
||||
}
|
||||
|
|
|
@ -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<E, R : JpaRepository<E, *>>(override val reposito
|
|||
|
||||
abstract class AbstractModelService<E : Model, R : JpaRepository<E, Long>>(repository: R) :
|
||||
AbstractService<E, R>(repository), ModelService<E, R> {
|
||||
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<E : Model, R : JpaRepository<E, Long>>(repos
|
|||
|
||||
abstract class AbstractNamedModelService<E : NamedModel, R : NamedJpaRepository<E>>(repository: R) :
|
||||
AbstractModelService<E, R>(repository), NamedModelService<E, R> {
|
||||
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<E : NamedModel, R : NamedJpaRepository<
|
|||
assertName(entity.name)
|
||||
with(repository.findByName(entity.name)) {
|
||||
if (this != null && id != entity.id)
|
||||
throw EntityAlreadyExistsException(entity.name)
|
||||
throw nameAlreadyExistsException(entity.name)
|
||||
}
|
||||
return super.update(entity)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service.files
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.exception.SimdutWriteException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||
import dev.fyloz.colorrecipesexplorer.model.Material
|
||||
import dev.fyloz.colorrecipesexplorer.service.MaterialService
|
||||
import org.slf4j.Logger
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.io.IOException
|
||||
|
@ -57,3 +56,11 @@ class SimdutService(
|
|||
fun getSimdutFileName(material: Material) =
|
||||
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"
|
||||
)
|
||||
|
|
|
@ -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.EntityDto
|
||||
import dev.fyloz.colorrecipesexplorer.model.Model
|
||||
import dev.fyloz.colorrecipesexplorer.model.NamedModel
|
||||
|
@ -127,7 +127,7 @@ abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, 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<EntityNotFoundException> { service.getById(entity.id!!) }
|
||||
val exception = assertThrows<NotFoundException> { service.getById(entity.id!!) }
|
||||
assertTrue(exception.value is Long)
|
||||
assertEquals(entity.id, exception.value as Long)
|
||||
}
|
||||
|
@ -139,8 +139,8 @@ abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, R : J
|
|||
doReturn(true).whenever(repository).existsById(entity.id!!)
|
||||
|
||||
val exception = assertThrows<EntityAlreadyExistsException> { 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<E : Model, S : ModelService<E, *>, 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<EntityNotFoundException> { service.update(entity) }
|
||||
val exception = assertThrows<NotFoundException> { service.update(entity) }
|
||||
assertTrue(exception.value is Long)
|
||||
assertEquals(entity.id, exception.value as Long)
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
open fun `getByName() throws EntityNotFoundException when no entity with the given name exists`() {
|
||||
whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
|
||||
val exception = assertThrows<EntityNotFoundException> { service.getByName(entity.name) }
|
||||
val exception = assertThrows<NotFoundException> { service.getByName(entity.name) }
|
||||
assertEquals(entity.name, exception.value)
|
||||
}
|
||||
|
||||
|
@ -228,7 +228,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
doReturn(true).whenever(service).existsByName(entity.name)
|
||||
|
||||
val exception = assertThrows<EntityAlreadyExistsException> { service.save(entity) }
|
||||
assertEquals(entity.name, exception.value)
|
||||
assertEquals(entity.name, exception.id)
|
||||
}
|
||||
|
||||
// update()
|
||||
|
@ -251,7 +251,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
doReturn(false).whenever(service).existsById(entity.id!!)
|
||||
|
||||
val exception = assertThrows<EntityNotFoundException> { service.update(entity) }
|
||||
val exception = assertThrows<NotFoundException> { service.update(entity) }
|
||||
assertTrue(exception.value is Long)
|
||||
assertEquals(entity.id, exception.value as Long)
|
||||
|
||||
|
@ -263,7 +263,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
doReturn(entity).whenever(service).getById(entity.id!!)
|
||||
|
||||
val exception = assertThrows<EntityAlreadyExistsException> { service.update(entity) }
|
||||
assertEquals(entity.name, exception.value)
|
||||
assertEquals(entity.name, exception.id)
|
||||
}
|
||||
|
||||
// deleteByName()
|
||||
|
|
|
@ -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<EntityNotFoundException> {
|
||||
val exception = assertThrows<NotFoundException> {
|
||||
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<EntityNotFoundException> {
|
||||
val exception = assertThrows<NotFoundException> {
|
||||
service.getById(
|
||||
entitySystemUser.id,
|
||||
ignoreDefaultGroupUsers = false,
|
||||
|
@ -151,7 +151,7 @@ class EmployeeServiceTest :
|
|||
doReturn(true).whenever(repository).existsByFirstNameAndLastName(entity.firstName, entity.lastName)
|
||||
|
||||
val exception = assertThrows<EntityAlreadyExistsException> { 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<EntityNotFoundException> { service.getRequestDefaultGroup(request) }
|
||||
val exception = assertThrows<NotFoundException> { 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
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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<LowQuantityException> { service.deduct(this) }
|
||||
val exception = assertThrows<NotEnoughInventoryException> { service.deduct(this) }
|
||||
assertEquals(this, exception.materialQuantity)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ class MaterialServiceTest :
|
|||
doReturn(true).whenever(service).existsByName(entity.name)
|
||||
|
||||
val exception = assertThrows<EntityAlreadyExistsException> { 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<EntityAlreadyExistsException> { service.update(material) }
|
||||
assertEquals(material.name, exception.value)
|
||||
assertEquals(material.name, exception.id)
|
||||
}
|
||||
|
||||
// updateQuantity()
|
||||
|
|
|
@ -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<EntityAlreadyExistsException> { 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<EntityNotFoundException> { service.update(entity) }
|
||||
val exception = assertThrows<NotFoundException> { 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<EntityAlreadyExistsException> { 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<EntityAlreadyExistsException> { service.update(entity) }
|
||||
assertEquals(entity.prefix, exception.value)
|
||||
assertEquals(entity.prefix, exception.id)
|
||||
}
|
||||
|
||||
// delete()
|
||||
|
|
|
@ -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<MixType, MixTypeService
|
|||
fun `getByMaterial() throws EntityNotFoundException when no mix type with the given material exists`() {
|
||||
whenever(repository.findByMaterial(material)).doReturn(null)
|
||||
|
||||
val exception = assertThrows<EntityNotFoundException> { service.getByMaterial(material) }
|
||||
val exception = assertThrows<NotFoundException> { service.getByMaterial(material) }
|
||||
assertEquals(material.name, exception.value)
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ class MixTypeServiceTest : AbstractNamedModelServiceTest<MixType, MixTypeService
|
|||
whenever(materialService.existsByName(entity.name)).doReturn(true)
|
||||
|
||||
val exception = assertThrows<EntityAlreadyExistsException> { service.save(entity) }
|
||||
assertEquals(entity.name, exception.value)
|
||||
assertEquals(entity.name, exception.id)
|
||||
}
|
||||
|
||||
// saveForNameAndMaterialType()
|
||||
|
|
|
@ -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<EntityNotFoundException> { service.getByIdForRecipe(imageId, recipeId) }
|
||||
assertThrows<NotFoundException> { service.getByIdForRecipe(imageId, recipeId) }
|
||||
assertEquals("$recipeId/$imageId", exception.value)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue