Continue transition vers exceptions + DTO

This commit is contained in:
FyloZ 2020-02-18 19:32:48 -05:00
parent ddcb5c9629
commit a1cc594acd
18 changed files with 357 additions and 364 deletions

View File

@ -0,0 +1,12 @@
package dev.fyloz.trial.colorrecipesexplorer.core.exception;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
public class SimdutException extends RuntimeException {
private Material material;
public SimdutException(Material material) {
this.material = material;
}
}

View File

@ -1,10 +1,18 @@
package dev.fyloz.trial.colorrecipesexplorer.core.exception.model;
import dev.fyloz.trial.colorrecipesexplorer.core.model.IModel;
import lombok.Getter;
import lombok.NonNull;
@Getter
public class EntityAlreadyExistsException extends ModelException {
@NonNull
private IdentifierType identifierType;
private String identifierName;
@NonNull
private Object requestedId;
public EntityAlreadyExistsException(Class<? extends IModel> type, IdentifierType identifierType, Object requestedId) {
@ -13,11 +21,10 @@ public class EntityAlreadyExistsException extends ModelException {
this.requestedId = requestedId;
}
public IdentifierType getIdentifierType() {
return identifierType;
}
public Object getRequestedId() {
return requestedId;
public EntityAlreadyExistsException(Class<? extends IModel> type, IdentifierType identifierType, String identifierName, Object requestedId) {
super(type);
this.identifierType = identifierType;
this.identifierName = identifierName != null ? identifierName : identifierType.getName();
this.requestedId = requestedId;
}
}

View File

@ -21,7 +21,18 @@ public class ModelException extends RuntimeException {
}
public enum IdentifierType {
ID,
NAME
ID("id"),
NAME("name"),
OTHER("");
private String name;
IdentifierType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}

View File

@ -35,6 +35,7 @@ public enum ResponseCode {
SUCCESS_SAVING_COMPANY(32, ResponseCodeType.SUCCESS, 1),
SUCCESS_DELETING_RECIPE(33, ResponseCodeType.SUCCESS, 1),
SUCCESS_DELETING_MATERIAL_TYPE(34, ResponseCodeType.SUCCESS, 1),
RECIPE_ALREADY_EXIST(35, ResponseCodeType.ERROR, 1),
// HTTP Errors
_500(100, ResponseCodeType.ERROR, 0),

View File

@ -17,6 +17,8 @@ public class MaterialType implements IModel {
public static final MaterialType DEFAULT_MATERIAL_TYPE;
public static final MaterialType BASE_MATERIAL_TYPE;
public static final String IDENTIFIER_PREFIX_NAME = "prefix";
static {
DEFAULT_MATERIAL_TYPE = new MaterialType("Aucun", "", false);
BASE_MATERIAL_TYPE = new MaterialType("Base", "BAS", false);

View File

@ -0,0 +1,12 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model.dto;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import lombok.Data;
@Data
public class MaterialTypeEditorDto {
private String oldName;
private MaterialType materialType;
}

View File

@ -1,18 +1,25 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.files;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
@Service
public class FilesService {
private ResourceLoader resources;
private Logger logger = ColorRecipesExplorerApplication.LOGGER;
@Autowired
public FilesService(ResourceLoader resources) {
@ -47,4 +54,58 @@ public class FilesService {
return new String(data, StandardCharsets.UTF_8);
}
/**
* Écrit un fichier Multipart sur le disque.
*
* @param multipartFile Le fichier à écrire
* @param path Le chemin vers le fichier
* @return Si le fichier a bien été créé
*/
public boolean writeMultiPartFile(MultipartFile multipartFile, String path) {
if (multipartFile.getSize() <= 0) return true;
try {
File file = createFile(path);
multipartFile.transferTo(file.toPath());
return true;
} catch (IOException ex) {
ColorRecipesExplorerApplication.LOGGER.error("Impossible d'écrire un fichier Multipart: " + ex.getMessage());
return false;
}
}
/**
* Crée un fichier sur le disque.
*
* @param path Le chemin vers le fichier
* @return Le fichier créé
* @throws IOException La création du fichier échoue
*/
public File createFile(String path) throws IOException {
File file = new File(path);
if (!file.exists() || file.isDirectory()) {
Files.createDirectories(file.getParentFile().toPath());
Files.createFile(file.toPath());
}
return file;
}
/**
* Supprime un fichier sur le disque.
*
* @param path Le chemin vers le fichier
* @throws IOException La suppression du fichier échoue
*/
public void deleteFile(String path) {
File file = new File(path);
try {
if (file.exists() && !file.isDirectory()) Files.delete(file.toPath());
} catch (IOException ex) {
logger.error("Impossible de supprimer un fichier: " + ex.getMessage());
}
}
}

View File

@ -1,16 +1,20 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.SimdutException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.ModelException;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.FileHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.files.FilesService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MaterialDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@ -21,11 +25,13 @@ import java.util.stream.Collectors;
public class MaterialService extends GenericService<Material, MaterialDao> {
private MixQuantityService mixQuantityService;
private FilesService filesService;
@Autowired
public MaterialService(MaterialDao materialDao, MixQuantityService mixQuantityService) {
super(materialDao);
public MaterialService(MaterialDao materialDao, MixQuantityService mixQuantityService, FilesService filesService) {
super(materialDao, Material.class);
this.mixQuantityService = mixQuantityService;
this.filesService = filesService;
}
public Optional<Material> getByName(String name) {
@ -38,12 +44,39 @@ public class MaterialService extends GenericService<Material, MaterialDao> {
return dao.findAllByMaterialType(materialType);
}
/**
* Récupère tous les produits qui ne sont pas des types de mélange.
*
* @return Tous les produits qui ne sont pas des types de mélange
*/
public List<Material> getAllNotMixType() {
return getAll()
.stream()
.filter(m -> !m.isMixType())
.collect(Collectors.toList());
}
public List<Material> getAllOrdered() {
return getAll().stream()
.sorted(Comparator.comparing(Material::getName))
.collect(Collectors.toList());
}
public Material save(@NotNull Material material, MultipartFile file) {
addSimdut(file, material);
return super.save(material);
}
@Override
public Material update(Material material) {
Optional<Material> materialByCode = dao.findByName(material.getName());
if (materialByCode.isPresent() && !material.getId().equals(materialByCode.get().getId()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.NAME, material.getName());
return super.update(material);
}
/**
* Vérifie si un produit est lié à un ou plusieurs mélanges.
*
@ -54,12 +87,11 @@ public class MaterialService extends GenericService<Material, MaterialDao> {
return mixQuantityService.existsByMaterial(material);
}
public boolean deleteIfNotLinked(Material material) {
@Deprecated(since = "1.3.0", forRemoval = true)
public void deleteIfNotLinked(Material material) {
if (!isLinkedToMixes(material)) {
return delete(material);
delete(material);
}
return false;
}
@Override
@ -75,11 +107,6 @@ public class MaterialService extends GenericService<Material, MaterialDao> {
return super.isValidForUpdate(material) && (materialByCode.isEmpty() || material.getId().equals(materialByCode.get().getId()));
}
@Deprecated(since = "1.2.0")
public List<Material> getAllBySearchString(String searchString) {
return dao.findAllByNameContainingIgnoreCase(searchString).stream().filter(m -> !m.isMixType()).collect(Collectors.toList());
}
/**
* Crée un FileHandler pour le produit passé en paramètre.
*
@ -91,6 +118,19 @@ public class MaterialService extends GenericService<Material, MaterialDao> {
return new FileHandler(filename, FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF);
}
/**
* Récupère le chemin vers le fichier SIMDUT d'un produit.
*
* @param material Le produit
* @return Le chemin vers le fichier SIMDUT du produit
*/
private String getSimdutPath(Material material) {
return String.format("%s/simdut/%s_%s.pdf",
ColorRecipesExplorerApplication.UPLOAD_LOCATION,
material.getId(),
material.getName());
}
/**
* Crée le fichier SIMDUT sur le disque et y transfert le contenu du MultipartFile passé en paramètre.
* <p>
@ -99,24 +139,20 @@ public class MaterialService extends GenericService<Material, MaterialDao> {
*
* @param simdut Le contenu du fichier SIMDUT
* @param material Le produit du SIMDUT
* @return Si le fichier SIMDUT à bien été créé ou non.
*/
public boolean addSimdut(MultipartFile simdut, Material material) {
if (simdut.getSize() <= 0) return false;
public void addSimdut(MultipartFile simdut, Material material) {
if (filesService.writeMultiPartFile(simdut, getSimdutPath(material))) throw new SimdutException(material);
}
FileHandler fileHandler = getFileHandlerForMaterial(material);
if (!fileHandler.createFile()) {
return false;
}
try {
simdut.transferTo(new File(fileHandler.getFile().getAbsolutePath()));
return true;
} catch (IOException e) {
logger.error("Erreur lors du transfert d'un fichier SIMDUT vers le disque", e);
return false;
}
/**
* Met à jour le fichier SIMDUT sur le disque.
*
* @param simdut Le contenu du fichier SIMDUT mis à jour
* @param material Le produit du SIMDUT
*/
public void updateSimdut(MultipartFile simdut, Material material) {
removeSimdut(material);
addSimdut(simdut, material);
}
/**
@ -125,15 +161,8 @@ public class MaterialService extends GenericService<Material, MaterialDao> {
* Cette méthode retournera true si le fichier SIMDUT du produit n'existe pas ou si la suppression est faîtes.
*
* @param material Le produit dont on veut supprimer le fichier SIMDUT
* @return Si la suppression du fichier SIMDUT s'est effectuée.
*/
public boolean removeSimdut(Material material) {
FileHandler fileHandler = getFileHandlerForMaterial(material);
if (fileHandler.getFile().exists()) {
return fileHandler.deleteFile();
}
return true;
public void removeSimdut(Material material) {
filesService.deleteFile(getSimdutPath(material));
}
}

View File

@ -1,13 +1,15 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityNotFoundException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.ModelException;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MaterialTypeEditorDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MaterialTypeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class MaterialTypeService extends GenericService<MaterialType, MaterialTypeDao> {
@ -15,7 +17,7 @@ public class MaterialTypeService extends GenericService<MaterialType, MaterialTy
@Autowired
public MaterialTypeService(MaterialTypeDao materialTypeDao, MaterialService materialService) {
super(materialTypeDao);
super(materialTypeDao, MaterialType.class);
this.materialService = materialService;
}
@ -23,48 +25,82 @@ public class MaterialTypeService extends GenericService<MaterialType, MaterialTy
return !materialService.getAllByMaterialType(materialType).isEmpty();
}
public boolean deleteIfNotLinked(MaterialType materialType) {
@Override
public MaterialType update(MaterialType entity) {
throw new UnsupportedOperationException("Cette méthode n'est pas supportée pour les types de produits. Utiliser MaterialTypeService::update(MaterialTypeEditorDto)");
}
public MaterialType update(MaterialTypeEditorDto materialTypeDto) {
MaterialType materialType = materialTypeDto.getMaterialType();
if (!existsByName(materialTypeDto.getOldName()))
throw new EntityNotFoundException(type, ModelException.IdentifierType.NAME, materialTypeDto.getOldName());
if (materialTypeDto.getOldName().equals(materialType.getName()) && existsByName(materialType.getName()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.NAME, materialType.getName());
if (existsByPrefix(materialType.getPrefix()) && !getByPrefix(materialType.getPrefix()).getId().equals(materialType.getId()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, MaterialType.IDENTIFIER_PREFIX_NAME, materialType.getPrefix());
return dao.save(materialType);
}
@Deprecated(since = "1.3.0", forRemoval = true)
public void deleteIfNotLinked(MaterialType materialType) {
if (!isLinkedToMaterials(materialType)) {
return delete(materialType);
delete(materialType);
}
return false;
}
@Override
public boolean isValidForCreation(MaterialType entity) {
return entity != null && !existsByName(entity.getName());
}
@Override
public boolean isValidForUpdate(MaterialType materialType) {
return super.isValidForUpdate(materialType)
&& isValidForUpdateName(materialType)
&& isValidForUpdatePrefix(materialType);
}
@Deprecated(since = "1.3.0", forRemoval = true)
public boolean isValidForUpdateName(MaterialType materialType) {
Optional<MaterialType> materialTypeByName = dao.findByName(materialType.getName());
MaterialType materialTypeByName = dao.findByName(materialType.getName());
return materialTypeByName.isEmpty() || materialType.getId().equals(materialTypeByName.get().getId());
return materialType.getId().equals(materialTypeByName.getId());
}
@Deprecated(since = "1.3.0", forRemoval = true)
public boolean isValidForUpdatePrefix(MaterialType materialType) {
Optional<MaterialType> materialTypeByPrefix = dao.findByPrefix(materialType.getPrefix());
MaterialType materialTypeByPrefix = dao.findByPrefix(materialType.getPrefix());
return materialTypeByPrefix.isEmpty() || materialType.getId().equals(materialTypeByPrefix.get().getId());
return materialType.getId().equals(materialTypeByPrefix.getId());
}
public Optional<MaterialType> getByName(String name) {
/**
* Récupère un type de produit par son nom.
*
* @param name Le nom du type de produit à récupérer
* @return Le type de produit correspondant au nom.
*/
public MaterialType getByName(String name) {
return dao.findByName(name);
}
// TODO Utilisé ?
public Optional<MaterialType> getDefaultMaterialType() {
return getByName(MaterialType.DEFAULT_MATERIAL_TYPE.getName());
/**
* Récupère un type de produit par son préfixe.
*
* @param prefix Le préfixe du type de produit à récupérer
* @return Le type de produit correspondant au préfixe
*/
public MaterialType getByPrefix(String prefix) {
return dao.findByPrefix(prefix);
}
/**
* Vérifie si un type de produit correspondant à un nom existe.
*
* @param name Le nom à vérifier
* @return Si un type de produit ayant le nom existe
*/
public boolean existsByName(String name) {
return getByName(name).isPresent();
return dao.existsByName(name);
}
/**
* Vérifie si un type de produit correspondant à un préfixe existe.
*
* @param prefix Le préfixe à vérifier
* @return Si un type de produit ayant le préfixe existe
*/
public boolean existsByPrefix(String prefix) {
return dao.existsByPrefix(prefix);
}
}

View File

@ -7,8 +7,12 @@ import java.util.Optional;
public interface MaterialTypeDao extends JpaRepository<MaterialType, Long> {
Optional<MaterialType> findByName(String name);
MaterialType findByName(String name);
Optional<MaterialType> findByPrefix(String prefix);
boolean existsByName(String name);
MaterialType findByPrefix(String prefix);
boolean existsByPrefix(String prefix);
}

View File

@ -1,5 +1,7 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.SimdutException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
@ -15,7 +17,6 @@ import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.CREATOR_MATERIAL;
@ -40,48 +41,21 @@ public class MaterialCreatorController {
.build();
}
/**
* Permet à l'utilisateur de créer un produit.
* <p>
* La création échouera si:
* - L'utilisateur n'est pas autorisé à effectuer cette action
* - Le produit existe déjà
* - Une erreur est survenue lors de la sauvegarde dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - materialCode: Contient le code de produit du produit créé
* <p>
* REQUIERT UNE AUTORISATION
*
* @param material Le produit à créer
* @param simdut Le fichier SIMDUT du produit (optionnel)
* @return La page à afficher.
*/
@PostMapping(value = CREATOR_MATERIAL, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ModelAndView create(@Valid Material material, MultipartFile simdut) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
if (!materialService.exists(material)) {
Optional<Material> savedMaterial = materialService.save(material);
if (savedMaterial.isPresent()) {
material = savedMaterial.get();
modelResponseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL, material.getName());
if (simdut.getSize() > 0 && !materialService.addSimdut(simdut, material)) {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
}
return showCreationPage(modelResponseBuilder
.addResponseData(ResponseDataType.MATERIAL_CODE, material.getName())
.build(), null);
} else {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
} else {
try {
materialService.save(material, simdut);
return showCreationPage(
modelResponseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL, material.getName()).build(),
null
);
} catch (EntityAlreadyExistsException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getName());
} catch (SimdutException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL, material.getName());
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
}
return showCreationPage(modelResponseBuilder.build(), material);

View File

@ -1,5 +1,6 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
@ -40,16 +41,13 @@ public class MaterialTypeCreatorController {
public ModelAndView create(@Valid MaterialType materialType) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(ControllerUtils.redirect(INDEX));
if (materialTypeService.isValidForCreation(materialType)) {
Optional<MaterialType> optionalMaterialType = materialTypeService.save(materialType);
if (optionalMaterialType.isPresent()) {
return getPage(modelResponseBuilder
.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL_TYPE, optionalMaterialType.get().getName())
.build(), null);
} else {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
} else {
try {
materialTypeService.save(materialType);
return getPage(
modelResponseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL_TYPE, materialType.getName()).build(),
null
);
} catch (EntityAlreadyExistsException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST, materialType.getName());
}

View File

@ -1,12 +1,12 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityNotFoundException;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixCreationFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.*;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
@ -17,7 +17,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.*;
@ -27,7 +26,6 @@ public class MixCreatorController {
private MixService mixService;
private RecipeService recipeService;
private MaterialService materialService;
private MixTypeService mixTypeService;
private MaterialTypeService materialTypeService;
@Autowired
@ -35,63 +33,32 @@ public class MixCreatorController {
this.mixService = mixService;
this.recipeService = recipeService;
this.materialService = materialService;
this.mixTypeService = mixTypeService;
this.materialTypeService = materialTypeService;
}
/**
* Affiche la page de création d'un mélange.
* Cette méthode requiert l'identifiant d'une recette dans l'URL.
*
* @param model Le Model injecté par Thymeleaf
* @param id L'identifiant de la recette
* @return La page à afficher.
*/
@GetMapping(CREATOR_MIX_SPECIFIC)
public ModelAndView showCreationPage(ModelAndView model, @PathVariable Long id) {
public ModelAndView getPage(ModelAndView model, @PathVariable Long id) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(model)
.withView(CREATOR_MIX);
Optional<Recipe> optionalRecipe = recipeService.getById(id);
if (optionalRecipe.isEmpty()) {
return modelResponseBuilder
.withView(ControllerUtils.redirect(EDITOR_RECIPE))
.build();
try {
Recipe recipe = recipeService.getById(id);
modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, recipe)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.addAttribute("materialsJson", materialService.asJson(mixService.getAvailableMaterialsForNewMix(recipe)));
if (materialService.getAll().isEmpty())
modelResponseBuilder.addResponseData(ResponseDataType.BLOCK_BUTTON, true);
} catch (EntityNotFoundException ex) {
modelResponseBuilder
.withRedirect(EDITOR_RECIPE);
}
Recipe recipe = optionalRecipe.get();
ModelResponseBuilder responseBuilder = modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, recipe)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.addAttribute("materialsJson", materialService.asJson(mixService.getAvailableMaterialsForNewMix(recipe)));
if (materialService.getAll().isEmpty()) {
responseBuilder.addResponseCode(ResponseCode.NO_MATERIAL)
.addResponseData(ResponseDataType.BLOCK_BUTTON, true);
}
return responseBuilder.build();
return modelResponseBuilder.build();
}
/**
* Permet à l'utilisateur de créer un mélange.
* <p>
* La création échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - La recette n'existe pas
* - Un des produits n'existe pas
* - Une erreur est survenue lors de la sauvegarde du type de mélange
* - Une erreur est survenue lors de la sauvegarde du mélange
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param form La formulaire du mélange entré par l'utilisateur
* @return La page à afficher.
*/
@PostMapping(value = CREATOR_MIX, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView createMix(@ModelAttribute @Valid MixCreationFormDto formDto) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
@ -104,7 +71,7 @@ public class MixCreatorController {
ModelResponseBuilder mixCreationResponse = mixService.create(formDto, recipe);
if (mixCreationResponse != null)
return showCreationPage(mixCreationResponse.build(), formDto.getRecipe().getId());
return getPage(mixCreationResponse.build(), formDto.getRecipe().getId());
return modelResponseBuilder
.withRedirect(EDITOR_RECIPE_SPECIFIC, recipe.getId())

View File

@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.CREATOR_RECIPE;
import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.EDITOR_RECIPE_SPECIFIC;
@ -45,33 +44,12 @@ public class RecipeCreatorController {
return responseBuilder.build();
}
/**
* Permet à l'utilisateur de créer une nouvelle recette.
* <p>
* La création échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - Une erreur est survenue lors de la sauvegarde dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param recipe La recette à créer
* @return La page à afficher.
*/
@PostMapping(value = CREATOR_RECIPE)
@PostMapping(CREATOR_RECIPE)
public ModelAndView createRecipe(@Valid Recipe recipe) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
Optional<Recipe> savedRecipe = recipeService.save(recipe);
if (savedRecipe.isPresent()) {
return modelResponseBuilder
.withRedirect(EDITOR_RECIPE_SPECIFIC, savedRecipe.get().getId())
.build();
}
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
long id = recipeService.save(recipe).getId();
return showCreationPage(modelResponseBuilder.build(), recipe);
return modelResponseBuilder.withRedirect(EDITOR_RECIPE_SPECIFIC, id).build();
}
}

View File

@ -1,12 +1,14 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.editors;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.SimdutException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityNotFoundException;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
@ -16,9 +18,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import java.util.Optional;
import java.util.stream.Collectors;
import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.*;
@Controller
@ -33,107 +32,50 @@ public class MaterialEditorController {
this.materialTypeService = materialTypeService;
}
/**
* Affiche la page de tous les produits.
* <p>
* Modèle de la page:
* - materials: La liste de tous les produits
*
* @param model Le Model injecté par Thymeleaf
* @return La page à afficher.
*/
@GetMapping(EDITOR_MATERIAL)
public ModelAndView listMaterials(ModelAndView model) {
public ModelAndView getPage(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL)
.addResponseData(ResponseDataType.MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()))
.addResponseData(ResponseDataType.MATERIALS, materialService.getAllNotMixType())
.build();
}
@GetMapping(EDITOR_MATERIAL_SPECIFIC)
public ModelAndView showEditPage(ModelAndView model, @PathVariable Long id, Material material) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL_EDITOR);
public ModelAndView getEditPage(ModelAndView model, @PathVariable Long id, Material material) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(model).withView(EDITOR_MATERIAL_EDITOR);
if (material.getName() == null) {
Optional<Material> optionalMaterial = materialService.getById(id);
if (optionalMaterial.isEmpty()) {
return listMaterials(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, id)
.build()
);
}
material = optionalMaterial.get();
try {
if (material.getName() == null) material = materialService.getById(id);
return modelResponseBuilder
.addResponseData(ResponseDataType.MATERIAL, material)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
} catch (EntityNotFoundException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, id);
}
return responseBuilder
.addResponseData(ResponseDataType.MATERIAL, material)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
return getPage(modelResponseBuilder.build());
}
/**
* Permet à l'utilisateur de modifier un produit.
* <p>
* La modification échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - Il n'y a aucun produit avec l'identifiant passé en paramètre
* - Une erreur est survenue lors de la mise à jour dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - materialCode: Contient le code du produit mit à jour
* <p>
* REQUIERT UNE AUTORISATION
*
* @param material Le produit à mettre à jour
* @return La page à afficher.
*/
@PostMapping(value = EDITOR_MATERIAL, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView saveEditedMaterial(Material material) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("");
Long id = material.getId();
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
if (!materialService.existsById(id)) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, id);
} else if (!materialService.isValidForUpdate(material)) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getName());
try {
material = materialService.update(material);
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getName())
.build(),
id, material);
} else {
Optional<Material> updatedMaterial = materialService.update(material);
if (updatedMaterial.isPresent()) {
responseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL, updatedMaterial.get().getName());
} else {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build(),
id, null);
}
return getPage(modelResponseBuilder
.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL, material.getName())
.build());
} catch (EntityNotFoundException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, material.getId());
} catch (EntityAlreadyExistsException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getName());
}
return listMaterials(responseBuilder.build());
return getEditPage(modelResponseBuilder.build(), material.getId(), material);
}
/**
* Affiche la page d'upload d'un fichier SIMDUT pour un produit
* Cette méthode requiert l'identifiant du produit dans l'URL
* <p>
* Modèle de la page:
* - id: Contient l'identifiant du produit
*
* @param model Le Model injecté par Thymeleaf
* @param id L'identifiant du produit à modifier le SIMDUT
* @return La page à afficher.
*/
@GetMapping(value = EDIT_MATERIAL_SIMDUT_SPECIFIC)
public ModelAndView chooseSIMDUTFile(ModelAndView model, @PathVariable Long id) {
return new ModelResponseBuilder(model)
@ -142,35 +84,20 @@ public class MaterialEditorController {
.build();
}
/**
* Sauvegarde le fichier SIMDUT d'un produit.
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param id L'identifiant du produit
* @param simdut Le fichier SIMDUT uploadé
* @return La page à afficher.
*/
@PostMapping(value = EDIT_MATERIAL_SIMDUT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ModelAndView saveSIMDUT(Long id, MultipartFile simdut) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(ControllerUtils.redirect("material/editor/" + id));
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder().withRedirect(EDITOR_MATERIAL_SPECIFIC, id);
Optional<Material> optionalMaterial = materialService.getById(id);
if (optionalMaterial.isEmpty()) {
return chooseSIMDUTFile(modelResponseBuilder
.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, id)
.build(),
id);
}
Material material = optionalMaterial.get();
if (!materialService.removeSimdut(material) || !materialService.addSimdut(simdut, material)) {
try {
Material material = materialService.getById(id);
materialService.updateSimdut(simdut, material);
} catch (EntityNotFoundException ex) {
return chooseSIMDUTFile(
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, id).build(),
id
);
} catch (SimdutException ex) {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
return chooseSIMDUTFile(modelResponseBuilder.build(), id);
}
return modelResponseBuilder.build();

View File

@ -1,9 +1,13 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.editors;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityAlreadyExistsException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.EntityNotFoundException;
import dev.fyloz.trial.colorrecipesexplorer.core.exception.model.ModelException;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MaterialTypeEditorDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@ -13,8 +17,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.WebsitePaths.*;
@Controller
@ -28,7 +30,7 @@ public class MaterialTypeEditorController {
}
@GetMapping(EDITOR_MATERIAL_TYPE)
public ModelAndView listMaterialTypes(ModelAndView model) {
public ModelAndView getPage(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL_TYPE)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
@ -36,67 +38,37 @@ public class MaterialTypeEditorController {
}
@GetMapping(EDITOR_MATERIAL_TYPE_SPECIFIC)
public ModelAndView showEditPage(ModelAndView model, @PathVariable Long id, MaterialType materialType) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL_TYPE_EDITOR);
public ModelAndView getEditPage(ModelAndView model, @PathVariable Long id, MaterialType materialType) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder(model).withView(EDITOR_MATERIAL_TYPE_EDITOR);
if (materialType.getName() == null) {
Optional<MaterialType> optionalMaterialType = materialTypeService.getById(id);
try {
if (materialType == null) materialType = materialTypeService.getById(id);
if (optionalMaterialType.isEmpty()) {
return listMaterialTypes(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, id)
.build()
);
}
materialType = optionalMaterialType.get();
return responseBuilder
.addResponseData(ResponseDataType.MATERIAL_TYPE, materialType)
.build();
} catch (EntityNotFoundException ex) {
return getPage(responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, id).build());
}
return responseBuilder
.addResponseData(ResponseDataType.MATERIAL_TYPE, materialType)
.build();
}
@PostMapping(value = EDITOR_MATERIAL_TYPE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView saveEditedMaterialType(MaterialType materialType) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("");
public ModelAndView updateMaterialType(MaterialTypeEditorDto materialTypeDto) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder();
// L'ID est 0 lors de la désérialisation, il faut utiliser le nom unique
String materialTypeName = materialType.getName();
Optional<MaterialType> optionalMaterialType = materialTypeService.getByName(materialTypeName);
try {
materialTypeService.update(materialTypeDto);
if (optionalMaterialType.isEmpty()) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, materialTypeName);
} else {
materialType = optionalMaterialType.get();
if (!materialTypeService.isValidForUpdateName(materialType)) {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST, materialType.getName())
.build(),
materialType.getId(), materialType);
} else if (!materialTypeService.isValidForUpdatePrefix(materialType)) {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST_PREFIX, materialType.getPrefix())
.build(),
materialType.getId(), materialType);
} else {
Optional<MaterialType> updatedMaterialType = materialTypeService.update(materialType);
if (updatedMaterialType.isPresent()) {
responseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL_TYPE, updatedMaterialType.get().getName());
} else {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build(),
materialType.getId(), materialType);
}
}
return getPage(responseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_MATERIAL_TYPE, materialTypeDto.getMaterialType().getName()).build());
} catch (EntityNotFoundException ex) {
return getPage(responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, materialTypeDto.getOldName()).build());
} catch (EntityAlreadyExistsException ex) {
if (ModelException.IdentifierType.NAME.equals(ex.getIdentifierType()))
responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST, materialTypeDto.getMaterialType().getName());
else if (ex.getIdentifierName().equals(MaterialType.IDENTIFIER_PREFIX_NAME))
responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST_PREFIX, materialTypeDto.getMaterialType().getPrefix());
}
return listMaterialTypes(responseBuilder.build());
return getEditPage(responseBuilder.build(), materialTypeDto.getMaterialType().getId(), materialTypeDto.getMaterialType());
}
}

View File

@ -35,3 +35,4 @@ response.31=The material {0} has been deleted
response.32=The banner {0} has been saved
response.33=The recipe {0} has been deleted
response.34=The material type {0} has been deleted
response.35=There is already a recipe with the ID {0}

View File

@ -35,3 +35,4 @@ response.31=Le produit {0} a bien été supprimée
response.32=La bannière {0} a bien été sauvegardée
response.33=La recette {0} a bien été supprimée
response.34=Le type de produit {0} a bien été supprimé
response.35=Il y a déjà une recette avec l''ID {0}