Ajout du support des compagnies dans le backend REST.

This commit is contained in:
FyloZ 2020-12-27 01:04:02 -05:00
parent f98a0064ca
commit 6bd111a5e9
18 changed files with 210 additions and 77 deletions

View File

@ -1,40 +0,0 @@
package dev.fyloz.trial.colorrecipesexplorer.model;
import lombok.*;
import org.hibernate.validator.constraints.Length;
import org.jetbrains.annotations.Nullable;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.Objects;
@Entity
@Data
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
public class Company implements Model {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NonNull
@NotNull
@Length(min = 2, max = 50)
@Column(unique = true)
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Company company = (Company) o;
return Objects.equals(name, company.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}

View File

@ -1,11 +0,0 @@
package dev.fyloz.trial.colorrecipesexplorer.repository;
import dev.fyloz.trial.colorrecipesexplorer.model.Company;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CompanyRepository extends JpaRepository<Company, Long> {
boolean existsByName(String name);
}

View File

@ -4,8 +4,8 @@ import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsE
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityLinkedException;
import dev.fyloz.trial.colorrecipesexplorer.exception.model.ModelException;
import dev.fyloz.trial.colorrecipesexplorer.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.service.AbstractJavaService;
import dev.fyloz.trial.colorrecipesexplorer.repository.CompanyRepository;
import dev.fyloz.trial.colorrecipesexplorer.service.AbstractJavaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -13,11 +13,11 @@ import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
@Service
public class CompanyService extends AbstractJavaService<Company, CompanyRepository> {
public class CompanyJavaService extends AbstractJavaService<Company, CompanyRepository> {
private RecipeService recipeService;
public CompanyService() {
public CompanyJavaService() {
super(Company.class);
}

View File

@ -19,7 +19,7 @@ import java.util.stream.Collectors;
@Service
public class RecipeService extends AbstractJavaService<Recipe, RecipeRepository> {
private CompanyService companyService;
private CompanyJavaService companyService;
private MixService mixService;
private StepService stepService;
private ImagesService imagesService;
@ -34,7 +34,7 @@ public class RecipeService extends AbstractJavaService<Recipe, RecipeRepository>
}
@Autowired
public void setCompanyService(CompanyService companyService) {
public void setCompanyService(CompanyJavaService companyService) {
this.companyService = companyService;
}

View File

@ -1,11 +1,11 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.thymeleaf.creators;
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyJavaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.MediaType;
@ -23,10 +23,10 @@ import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.CREATOR_COMP
@Profile("thymeleaf")
public class CompanyCreatorController {
private CompanyService companyService;
private CompanyJavaService companyService;
@Autowired
public CompanyCreatorController(CompanyService companyService) {
public CompanyCreatorController(CompanyJavaService companyService) {
this.companyService = companyService;
}

View File

@ -4,7 +4,7 @@ import dev.fyloz.trial.colorrecipesexplorer.web.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyJavaService;
import dev.fyloz.trial.colorrecipesexplorer.service.model.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
@ -23,10 +23,10 @@ import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.EDITOR_RECIP
public class RecipeCreatorController {
private RecipeService recipeService;
private CompanyService companyService;
private CompanyJavaService companyService;
@Autowired
public RecipeCreatorController(RecipeService recipeService, CompanyService companyService) {
public RecipeCreatorController(RecipeService recipeService, CompanyJavaService companyService) {
this.recipeService = recipeService;
this.companyService = companyService;
}

View File

@ -6,7 +6,7 @@ import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.model.dto.RecipeEditorFormDto;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyJavaService;
import dev.fyloz.trial.colorrecipesexplorer.service.model.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
@ -25,10 +25,10 @@ import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.*;
public class RecipeEditorController {
private RecipeService recipeService;
private CompanyService companyService;
private CompanyJavaService companyService;
@Autowired
public RecipeEditorController(RecipeService recipeService, CompanyService companyService) {
public RecipeEditorController(RecipeService recipeService, CompanyJavaService companyService) {
this.recipeService = recipeService;
this.companyService = companyService;
}

View File

@ -2,11 +2,11 @@ package dev.fyloz.trial.colorrecipesexplorer.web.controller.thymeleaf.removers;
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityLinkedException;
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityNotFoundException;
import dev.fyloz.trial.colorrecipesexplorer.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.web.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.service.model.CompanyJavaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Controller;
@ -22,10 +22,10 @@ import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.REMOVER_COMP
@Profile("thymeleaf")
public class CompanyRemoverController {
private CompanyService companyService;
private CompanyJavaService companyService;
@Autowired
public CompanyRemoverController(CompanyService companyService) {
public CompanyRemoverController(CompanyJavaService companyService) {
this.companyService = companyService;
}

View File

@ -0,0 +1,60 @@
package dev.fyloz.trial.colorrecipesexplorer.model
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank
import java.util.*
import javax.persistence.*
import javax.validation.constraints.NotBlank
import javax.validation.constraints.NotNull
private const val COMPANY_ID_NULL_MESSAGE = "Un identifiant est requis"
private const val COMPANY_NAME_NULL_MESSAGE = "Un nom est requis"
@Entity
data class Company(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
override val id: Long?,
@Column(unique = true)
override val name: String
) : NamedModel {
override fun equals(other: Any?): Boolean = other is Company && name == other.name
override fun hashCode(): Int = Objects.hash(name)
}
open class CompanySaveDto(
@field:NotBlank(message = COMPANY_NAME_NULL_MESSAGE)
val name: String
) : EntityDto<Company> {
override fun toEntity(): Company = Company(null, name)
}
open class CompanyUpdateDto(
@field:NotNull(message = COMPANY_ID_NULL_MESSAGE)
val id: Long,
@field:NullOrNotBlank(message = COMPANY_NAME_NULL_MESSAGE)
val name: String?
) : EntityDto<Company> {
override fun toEntity(): Company = Company(id, name ?: "")
}
// ==== DSL ====
fun company(
id: Long? = null,
name: String = "name",
op: Company.() -> Unit = {}
) = Company(id, name).apply(op)
fun companySaveDto(
name: String = "name",
op: CompanySaveDto.() -> Unit = {}
) = CompanySaveDto(name).apply(op)
fun companyUpdateDto(
id: Long = 0L,
name: String? = "name",
op: CompanyUpdateDto.() -> Unit = {}
) = CompanyUpdateDto(id, name).apply(op)

View File

@ -0,0 +1,7 @@
package dev.fyloz.trial.colorrecipesexplorer.repository
import dev.fyloz.trial.colorrecipesexplorer.model.Company
import org.springframework.stereotype.Repository
@Repository
interface CompanyRepository : NamedJpaRepository<Company>

View File

@ -0,0 +1,16 @@
package dev.fyloz.trial.colorrecipesexplorer.rest
import dev.fyloz.trial.colorrecipesexplorer.model.Company
import dev.fyloz.trial.colorrecipesexplorer.model.CompanySaveDto
import dev.fyloz.trial.colorrecipesexplorer.model.CompanyUpdateDto
import dev.fyloz.trial.colorrecipesexplorer.service.CompanyService
import org.springframework.context.annotation.Profile
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
private const val COMPANY_CONTROLLER_PATH = "api/company"
@RestController
@RequestMapping(COMPANY_CONTROLLER_PATH)
@Profile("rest")
class CompanyController(companyService: CompanyService) : AbstractRestModelApiController<Company, CompanySaveDto, CompanyUpdateDto, CompanyService>(companyService, COMPANY_CONTROLLER_PATH)

View File

@ -49,5 +49,5 @@ class MaterialController(materialService: MaterialService) : AbstractRestModelAp
@PutMapping("oldupdate")
override fun update(entity: MaterialUpdateDto): ResponseEntity<Void> =
super.update(entity)
ResponseEntity.notFound().build()
}

View File

@ -0,0 +1,34 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import dev.fyloz.trial.colorrecipesexplorer.model.Company
import dev.fyloz.trial.colorrecipesexplorer.model.CompanySaveDto
import dev.fyloz.trial.colorrecipesexplorer.model.CompanyUpdateDto
import dev.fyloz.trial.colorrecipesexplorer.model.company
import dev.fyloz.trial.colorrecipesexplorer.repository.CompanyRepository
import dev.fyloz.trial.colorrecipesexplorer.service.model.RecipeService
import org.springframework.stereotype.Service
import org.springframework.util.Assert
interface CompanyService : NamedModelService<Company, CompanySaveDto, CompanyUpdateDto, CompanyRepository> {
/** Checks if the given [company] is used by one or more recipes. */
fun isLinkedToRecipes(company: Company): Boolean
}
@Service
class CompanyServiceImpl(companyRepository: CompanyRepository, val recipeService: RecipeService) :
AbstractNamedModelService<Company, CompanySaveDto, CompanyUpdateDto, CompanyRepository>(companyRepository),
CompanyService {
override fun isLinkedToRecipes(company: Company): Boolean = recipeService.existsByCompany(company)
override fun update(entity: Company): Company {
Assert.notNull(entity.id, "CompanyService.update() was called with a null identifier")
val persistedCompany = getById(entity.id!!)
return super.update(with(entity) {
company(
id = id!!,
name = if (name.isNotBlank()) name else persistedCompany.name
)
})
}
}

View File

@ -1,6 +1,5 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsRestException
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.MaterialRepository
import dev.fyloz.trial.colorrecipesexplorer.service.files.SimdutService
@ -12,7 +11,7 @@ interface MaterialService : NamedModelService<Material, MaterialSaveDto, Materia
/** Checks if a material with the given [materialType] exists. */
fun existsByMaterialType(materialType: MaterialType): Boolean
/** Checks if the given [material] is used by at least one mix. */
/** Checks if the given [material] is used by one or more mixes. */
fun isLinkedToMixes(material: Material): Boolean
/** Checks if the material with the given [id] has a SIMDUT file. */

View File

@ -0,0 +1,11 @@
package dev.fyloz.trial.colorrecipesexplorer.repository
import dev.fyloz.trial.colorrecipesexplorer.model.Company
import dev.fyloz.trial.colorrecipesexplorer.model.company
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
class CompanyRepositoryTest @Autowired constructor(companyRepository: CompanyRepository, entityManager: TestEntityManager) :
AbstractNamedJpaRepositoryTest<Company, CompanyRepository>(companyRepository, entityManager) {
override fun entity(name: String): Company = company(name = name)
}

View File

@ -1,13 +1,19 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import com.nhaarman.mockitokotlin2.*
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.reset
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityAlreadyExistsRestException
import dev.fyloz.trial.colorrecipesexplorer.exception.model.EntityNotFoundRestException
import dev.fyloz.trial.colorrecipesexplorer.model.EntityDto
import dev.fyloz.trial.colorrecipesexplorer.model.Model
import dev.fyloz.trial.colorrecipesexplorer.model.NamedModel
import dev.fyloz.trial.colorrecipesexplorer.repository.NamedJpaRepository
import org.junit.jupiter.api.*
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.data.jpa.repository.JpaRepository
import java.util.*
import kotlin.test.assertEquals
@ -203,9 +209,11 @@ abstract class AbstractModelServiceTest<E : Model, N : EntityDto<E>, U : EntityD
inner class DeleteById {
@Test
fun `calls deleteById() in the repository with the given id`() {
doReturn(entity).whenever(service).getById(entity.id!!)
service.deleteById(entity.id!!)
verify(repository).deleteById(entity.id!!)
verify(repository).delete(entity)
}
}
}
@ -272,6 +280,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, N : EntityDto<E>, U
whenever(repository.save(entity)).doReturn(entity)
whenever(repository.findByName(entity.name)).doReturn(null)
doReturn(true).whenever(service).existsById(entity.id!!)
doReturn(entity).whenever(service).getById(entity.id!!)
val found = service.update(entity)
@ -292,6 +301,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, N : EntityDto<E>, U
@Test
fun `throws EntityAlreadyExistsRestException when an entity with the updated name exists`() {
whenever(repository.findByName(entity.name)).doReturn(entityWithEntityName)
doReturn(entity).whenever(service).getById(entity.id!!)
val exception = assertThrows<EntityAlreadyExistsRestException> { service.update(entity) }
assertEquals(entity.name, exception.value)

View File

@ -0,0 +1,47 @@
package dev.fyloz.trial.colorrecipesexplorer.service
import com.nhaarman.mockitokotlin2.*
import dev.fyloz.trial.colorrecipesexplorer.model.*
import dev.fyloz.trial.colorrecipesexplorer.repository.CompanyRepository
import dev.fyloz.trial.colorrecipesexplorer.service.model.RecipeService
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Nested
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class CompanyServiceTest : AbstractNamedModelServiceTest<Company, CompanySaveDto, CompanyUpdateDto, CompanyService, CompanyRepository>() {
private val recipeService: RecipeService = mock()
override val repository: CompanyRepository = mock()
override val service: CompanyService = spy(CompanyServiceImpl(repository, recipeService))
override val entity: Company = company(id = 0L, name = "company")
override val anotherEntity: Company = company(id = 1L, name = "another company")
override val entityWithEntityName: Company = company(id = 2L, name = "company")
override val entitySaveDto: CompanySaveDto = spy(companySaveDto())
override val entityUpdateDto: CompanyUpdateDto = spy(companyUpdateDto(id = 0L))
@AfterEach
override fun afterEach() {
reset(recipeService)
super.afterEach()
}
@Nested
inner class IsLinkedToRecipes {
fun `returns true when a given company is linked to one or more recipes`() {
whenever(recipeService.existsByCompany(entity)).doReturn(true)
val found = service.isLinkedToRecipes(entity)
assertTrue(found)
}
fun `returns false when a given company is not linked to any recipe`() {
whenever(recipeService.existsByCompany(entity)).doReturn(false)
val found = service.isLinkedToRecipes(entity)
assertFalse(found)
}
}
}

View File

@ -163,7 +163,7 @@ class MaterialServiceTest : AbstractNamedModelServiceTest<Material, MaterialSave
inner class UpdateDto {
@Test
fun `calls simdutService_update() with the updated entity`() {
val mockSimdutFile = MockMultipartFile("simdut", byteArrayOf())
val mockSimdutFile = MockMultipartFile("simdut", byteArrayOf(0))
val materialUpdateDto = spy(materialUpdateDto(id = 0L, simdutFile = mockSimdutFile))
doReturn(entity).whenever(service).update(entity)