Ajout des conflits de nom entre les recettes d'une bannière (#42)
This commit is contained in:
parent
0871a728e3
commit
a59bad7a7a
|
@ -12,63 +12,62 @@ 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<String, Any> = mapOf()
|
||||
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,
|
||||
"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",
|
||||
extensions: MutableMap<String, Any> = mutableMapOf()
|
||||
) : 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 = extensions.apply { this[identifierName] = identifierValue }.toMap()
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -79,19 +78,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<Any> {
|
||||
val errors = hashMapOf<String, String>()
|
||||
ex.bindingResult.allErrors.forEach {
|
||||
|
|
|
@ -288,6 +288,8 @@ 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"
|
||||
|
||||
sealed class RecipeException
|
||||
|
||||
fun recipeIdNotFoundException(id: Long) =
|
||||
NotFoundException(
|
||||
RECIPE_EXCEPTION_ERROR_CODE,
|
||||
|
@ -303,3 +305,16 @@ fun recipeIdAlreadyExistsException(id: Long) =
|
|||
"A recipe with the id $id already exists",
|
||||
id
|
||||
)
|
||||
|
||||
fun recipeNameAlreadyExistsForCompanyException(name: String, company: Company) =
|
||||
AlreadyExistsException(
|
||||
"${RECIPE_EXCEPTION_ERROR_CODE}-company",
|
||||
RECIPE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A recipe with the name $name already exists for the company ${company.name}",
|
||||
name,
|
||||
"name",
|
||||
mutableMapOf(
|
||||
"company" to company.name,
|
||||
"companyId" to company.id!!
|
||||
)
|
||||
)
|
||||
|
|
|
@ -8,6 +8,9 @@ interface RecipeRepository : JpaRepository<Recipe, Long> {
|
|||
/** Checks if one or more recipes have the given [company]. */
|
||||
fun existsByCompany(company: Company): Boolean
|
||||
|
||||
/** Checks if a recipe exists with the given [name] and [company]. */
|
||||
fun existsByNameAndCompany(name: String, company: Company): Boolean
|
||||
|
||||
/** Gets all recipes with the given [company]. */
|
||||
fun findAllByCompany(company: Company): Collection<Recipe>
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ interface RecipeService :
|
|||
/** Checks if one or more recipes have the given [company]. */
|
||||
fun existsByCompany(company: Company): Boolean
|
||||
|
||||
/** Checks if a recipe exists with the given [name] and [company]. */
|
||||
fun existsByNameAndCompany(name: String, company: Company): Boolean
|
||||
|
||||
/** Gets all recipes with the given [company]. */
|
||||
fun getAllByCompany(company: Company): Collection<Recipe>
|
||||
|
||||
|
@ -67,10 +70,18 @@ class RecipeServiceImpl(
|
|||
)
|
||||
|
||||
override fun existsByCompany(company: Company): Boolean = repository.existsByCompany(company)
|
||||
override fun existsByNameAndCompany(name: String, company: Company) =
|
||||
repository.existsByNameAndCompany(name, company)
|
||||
|
||||
override fun getAllByCompany(company: Company): Collection<Recipe> = repository.findAllByCompany(company)
|
||||
|
||||
override fun save(entity: RecipeSaveDto): Recipe {
|
||||
// TODO checks if name is unique in the scope of the [company]
|
||||
val company = companyService.getById(entity.companyId)
|
||||
|
||||
if (existsByNameAndCompany(entity.name, company)) {
|
||||
throw recipeNameAlreadyExistsForCompanyException(entity.name, company)
|
||||
}
|
||||
|
||||
return save(with(entity) {
|
||||
recipe(
|
||||
name = name,
|
||||
|
@ -80,14 +91,23 @@ class RecipeServiceImpl(
|
|||
sample = sample,
|
||||
approbationDate = approbationDate,
|
||||
remark = remark ?: "",
|
||||
company = companyService.getById(companyId)
|
||||
company = company
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun update(entity: RecipeUpdateDto): Recipe {
|
||||
val persistedRecipe by lazy { getById(entity.id) }
|
||||
val persistedRecipe = getById(entity.id)
|
||||
val name = entity.name
|
||||
val company = persistedRecipe.company
|
||||
|
||||
if (name != null &&
|
||||
name != persistedRecipe.name &&
|
||||
existsByNameAndCompany(name, company)
|
||||
) {
|
||||
throw recipeNameAlreadyExistsForCompanyException(name, company)
|
||||
}
|
||||
|
||||
return update(with(entity) {
|
||||
recipe(
|
||||
|
@ -99,7 +119,7 @@ class RecipeServiceImpl(
|
|||
sample = sample ?: persistedRecipe.sample,
|
||||
approbationDate = approbationDate ?: persistedRecipe.approbationDate,
|
||||
remark = remark or persistedRecipe.remark,
|
||||
company = persistedRecipe.company,
|
||||
company = company,
|
||||
mixes = persistedRecipe.mixes,
|
||||
groupsInformation = updateGroupsInformation(persistedRecipe, entity)
|
||||
)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service
|
||||
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.files.FileService
|
||||
import io.mockk.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.springframework.mock.web.MockMultipartFile
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.io.File
|
||||
|
@ -56,6 +58,19 @@ class RecipeServiceTest :
|
|||
assertFalse(found)
|
||||
}
|
||||
|
||||
// existsByNameAndCompany()
|
||||
|
||||
@Test
|
||||
fun `existsByNameAndCompany() returns if a recipe exists for the given name and company in the repository`() {
|
||||
setOf(true, false).forEach {
|
||||
whenever(repository.existsByNameAndCompany(entity.name, company)).doReturn(it)
|
||||
|
||||
val exists = service.existsByNameAndCompany(entity.name, company)
|
||||
|
||||
assertEquals(it, exists)
|
||||
}
|
||||
}
|
||||
|
||||
// getAllByCompany()
|
||||
|
||||
@Test
|
||||
|
@ -73,14 +88,40 @@ class RecipeServiceTest :
|
|||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
whenever(companyService.getById(company.id!!)).doReturn(company)
|
||||
doReturn(false).whenever(service).existsByNameAndCompany(entity.name, company)
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service, { argThat { this.id == null && this.color == color } })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save(dto) throw AlreadyExistsException when a recipe with the given name and company exists in the repository`() {
|
||||
whenever(companyService.getById(company.id!!)).doReturn(company)
|
||||
doReturn(true).whenever(service).existsByNameAndCompany(entity.name, company)
|
||||
|
||||
with(assertThrows<AlreadyExistsException> { service.save(entitySaveDto) }) {
|
||||
this.assertErrorCode("company-name")
|
||||
}
|
||||
}
|
||||
|
||||
// update()
|
||||
|
||||
@Test
|
||||
override fun `update(dto) calls and returns update() with the created entity`() =
|
||||
override fun `update(dto) calls and returns update() with the created entity`() {
|
||||
doReturn(false).whenever(service).existsByNameAndCompany(entity.name, company)
|
||||
withBaseUpdateDtoTest(entity, entityUpdateDto, service, { any() })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update(dto) throws AlreadyExistsException when a recipe exists for the given name and company`() {
|
||||
val name = "another recipe"
|
||||
|
||||
doReturn(entity).whenever(service).getById(entity.id!!)
|
||||
doReturn(true).whenever(service).existsByNameAndCompany(name, company)
|
||||
doReturn(name).whenever(entityUpdateDto).name
|
||||
|
||||
with(assertThrows<AlreadyExistsException> { service.update(entityUpdateDto) }) {
|
||||
this.assertErrorCode("company-name")
|
||||
}
|
||||
}
|
||||
|
||||
// updatePublicData()
|
||||
|
||||
|
|
Loading…
Reference in New Issue