diff --git a/build.gradle.kts b/build.gradle.kts index 7538018..02b0630 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,6 @@ plugins { } repositories { - jcenter() mavenCentral() maven { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/annotations/PermissionAnnotations.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/annotations/PermissionAnnotations.kt index 04ff417..8f78572 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/annotations/PermissionAnnotations.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/annotations/PermissionAnnotations.kt @@ -14,12 +14,6 @@ annotation class PreAuthorizeViewRecipes @PreAuthorize("hasAuthority('EDIT_RECIPES')") annotation class PreAuthorizeEditRecipes -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -@MustBeDocumented -@PreAuthorize("hasAuthority('REMOVE_RECIPES')") -annotation class PreAuthorizeRemoveRecipes - @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @MustBeDocumented @@ -37,9 +31,3 @@ annotation class PreAuthorizeViewUsers @MustBeDocumented @PreAuthorize("hasAuthority('EDIT_USERS')") annotation class PreAuthorizeEditUsers - -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -@MustBeDocumented -@PreAuthorize("hasAuthority('REMOVE_USERS')") -annotation class PreAuthorizeRemoveUsers diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt index c6b1fdf..d2bcba7 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Company.kt @@ -8,9 +8,6 @@ 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 @Table(name = "company") data class Company( @@ -20,11 +17,15 @@ data class Company( @Column(unique = true) override val name: String -) : NamedModel +) : NamedModel { + override fun toString(): String { + return name + } +} open class CompanySaveDto( - @field:NotBlank(message = COMPANY_NAME_NULL_MESSAGE) + @field:NotBlank val name: String ) : EntityDto { override fun toEntity(): Company = Company(null, name) @@ -32,10 +33,9 @@ open class CompanySaveDto( open class CompanyUpdateDto( - @field:NotNull(message = COMPANY_ID_NULL_MESSAGE) val id: Long, - @field:NullOrNotBlank(message = COMPANY_NAME_NULL_MESSAGE) + @field:NotBlank val name: String? ) : EntityDto { override fun toEntity(): Company = Company(id, name ?: "") diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt index 5dc53de..76f505a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Material.kt @@ -4,27 +4,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException -import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank -import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize -import dev.fyloz.colorrecipesexplorer.rest.CRE_PROPERTIES -import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH import org.springframework.web.multipart.MultipartFile -import java.net.URLEncoder -import java.nio.charset.StandardCharsets import javax.persistence.* import javax.validation.constraints.Min import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull - -private const val MATERIAL_ID_NULL_MESSAGE = "Un identifiant est requis" -private const val MATERIAL_NAME_NULL_MESSAGE = "Un nom est requis" -private const val MATERIAL_INVENTORY_QUANTITY_NULL_MESSAGE = "Une quantité est requise" -private const val MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE = "La quantité doit être supérieure ou égale à 0" -private const val MATERIAL_TYPE_NULL_MESSAGE = "Un type de produit est requis" - -private const val MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE = "Un produit est requis" -private const val MATERIAL_QUANTITY_QUANTITY_NULL_MESSAGE = "Une quantité est requises" -private const val MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE = "La quantité doit être supérieure ou égale à 0" +import javax.validation.constraints.Size const val SIMDUT_FILES_PATH = "pdf/simdut" @@ -52,32 +36,27 @@ data class Material( @JsonIgnore @Transient get() = "$SIMDUT_FILES_PATH/$name.pdf" - - } open class MaterialSaveDto( - @field:NotBlank(message = MATERIAL_NAME_NULL_MESSAGE) + @field:NotBlank val name: String, - @field:NotNull(message = MATERIAL_INVENTORY_QUANTITY_NULL_MESSAGE) - @field:Min(value = 0, message = MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE) + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) val inventoryQuantity: Float, - @field:NotNull(message = MATERIAL_TYPE_NULL_MESSAGE) val materialTypeId: Long, val simdutFile: MultipartFile? = null ) : EntityDto open class MaterialUpdateDto( - @field:NotNull(message = MATERIAL_ID_NULL_MESSAGE) val id: Long, - @field:NullOrNotBlank(message = MATERIAL_NAME_NULL_MESSAGE) + @field:NotBlank val name: String?, - @field:NullOrSize(min = 0, message = MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE) + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) val inventoryQuantity: Float?, val materialTypeId: Long?, @@ -95,11 +74,9 @@ data class MaterialOutputDto( ) : Model data class MaterialQuantityDto( - @field:NotNull(message = MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE) val material: Long, - @field:NotNull(message = MATERIAL_QUANTITY_QUANTITY_NULL_MESSAGE) - @field:Min(value = 0, message = MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE) + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) val quantity: Float ) @@ -147,7 +124,7 @@ fun materialQuantityDto( ) = MaterialQuantityDto(materialId, quantity).apply(op) // ==== Exceptions ==== - private const +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" diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt index c8f5e80..d19ac34 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MaterialType.kt @@ -11,10 +11,7 @@ import javax.validation.constraints.NotBlank import javax.validation.constraints.NotNull import javax.validation.constraints.Size -private const val MATERIAL_TYPE_ID_NULL_MESSAGE = "Un identifiant est requis" -private const val MATERIAL_TYPE_NAME_NULL_MESSAGE = "Un nom est requis" -private const val MATERIAL_TYPE_PREFIX_NULL_MESSAGE = "Un préfixe est requis" -private const val MATERIAL_TYPE_PREFIX_SIZE_MESSAGE = "Le préfixe doit faire exactement 3 caractères" +private const val VALIDATION_PREFIX_SIZE = "Must contains exactly 3 characters" @Entity @Table(name = "material_type") @@ -39,11 +36,11 @@ data class MaterialType( ) : NamedModel open class MaterialTypeSaveDto( - @field:NotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE) + @field:NotBlank val name: String, - @field:NotBlank(message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE) - @field:Size(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_SIZE_MESSAGE) + @field:NotBlank + @field:Size(min = 3, max = 3, message = VALIDATION_PREFIX_SIZE) val prefix: String, val usePercentages: Boolean = false @@ -53,13 +50,12 @@ open class MaterialTypeSaveDto( } open class MaterialTypeUpdateDto( - @field:NotNull(message = MATERIAL_TYPE_ID_NULL_MESSAGE) val id: Long, - @field:NullOrNotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE) + @field:NotBlank val name: String?, - @field:NullOrSize(min = 3, max = 3, message = MATERIAL_TYPE_PREFIX_NULL_MESSAGE) + @field:Size(min = 3, max = 3, message = VALIDATION_PREFIX_SIZE) val prefix: String? ) : EntityDto { override fun toEntity(): MaterialType = diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt index e332b36..3622343 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Mix.kt @@ -4,20 +4,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException -import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank import javax.persistence.* import javax.validation.constraints.Min import javax.validation.constraints.NotBlank -import javax.validation.constraints.NotNull -private const val MIX_ID_NULL_MESSAGE = "Un identifiant est requis" -private const val MIX_NAME_NULL_MESSAGE = "Un nom est requis" -private const val MIX_RECIPE_NULL_MESSAGE = "Un recette est requise" -private const val MIX_MATERIAL_TYPE_NULL_MESSAGE = "Un type de produit est requis" - -private const val MIX_DEDUCT_MIX_ID_NULL_MESSAGE = "Un identifiant de mélange est requis" -private const val MIX_DEDUCT_RATIO_NULL_MESSAGE = "Un ratio est requis" -private const val MIX_DEDUCT_RATION_NEGATIVE_MESSAGE = "Le ratio doit être égal ou supérieur à 0" @Entity @Table(name = "mix") @@ -43,33 +33,26 @@ data class Mix( ) : Model open class MixSaveDto( - @field:NotBlank(message = MIX_NAME_NULL_MESSAGE) + @field:NotBlank val name: String, - @field:NotNull(message = MIX_RECIPE_NULL_MESSAGE) val recipeId: Long, - @field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE) val materialTypeId: Long, val mixMaterials: Set? -) : EntityDto { - override fun toEntity(): Mix = throw UnsupportedOperationException() -} +) : EntityDto open class MixUpdateDto( - @field:NotNull(message = MIX_ID_NULL_MESSAGE) val id: Long, - @field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE) + @field:NotBlank val name: String?, val materialTypeId: Long?, var mixMaterials: Set? -) : EntityDto { - override fun toEntity(): Mix = throw UnsupportedOperationException() -} +) : EntityDto data class MixOutputDto( val id: Long, @@ -79,16 +62,13 @@ data class MixOutputDto( ) data class MixDeductDto( - @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) val id: Long, - @field:NotNull(message = MIX_DEDUCT_RATIO_NULL_MESSAGE) - @field:Min(value = 0, message = MIX_DEDUCT_RATION_NEGATIVE_MESSAGE) + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) val ratio: Float ) data class MixLocationDto( - @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) val mixId: Long, val location: String? diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt index 97f751c..c48316b 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/MixMaterial.kt @@ -6,10 +6,6 @@ import javax.persistence.* import javax.validation.constraints.Min import javax.validation.constraints.NotNull -private const val MIX_MATERIAL_DTO_MATERIAL_ID_NULL_MESSAGE = "Un identifiant de produit est requis" -private const val MIX_MATERIAL_DTO_QUANTITY_NULL_MESSAGE = "Une quantité est requise" -private const val MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE = "La quantité ne peut pas être négative" - @Entity @Table(name = "mix_material") data class MixMaterial( @@ -26,6 +22,15 @@ data class MixMaterial( var position: Int ) : Model +data class MixMaterialDto( + val materialId: Long, + + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) + val quantity: Float, + + val position: Int +) + data class MixMaterialOutputDto( val id: Long, val material: MaterialOutputDto, @@ -33,17 +38,6 @@ data class MixMaterialOutputDto( val position: Int ) -data class MixMaterialDto( - @field:NotNull(message = MIX_MATERIAL_DTO_MATERIAL_ID_NULL_MESSAGE) - val materialId: Long, - - @field:NotNull(message = MIX_MATERIAL_DTO_QUANTITY_NULL_MESSAGE) - @field:Min(value = 0, message = MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE) - val quantity: Float, - - val position: Int -) - // ==== DSL ==== fun mixMaterial( id: Long? = null, diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Model.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Model.kt index 2a50349..147285f 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Model.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Model.kt @@ -15,3 +15,8 @@ interface EntityDto { throw UnsupportedOperationException() } } + +// GENERAL VALIDATION MESSAGES +const val VALIDATION_SIZE_GE_ZERO = "Must be greater or equals to 0" +const val VALIDATION_SIZE_GE_ONE = "Must be greater or equals to 1" +const val VALIDATION_RANGE_PERCENTS = "Must be between 0 and 100" diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt index cf0aad3..4f6551a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/Recipe.kt @@ -5,8 +5,6 @@ import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.model.account.Group import dev.fyloz.colorrecipesexplorer.model.account.group -import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank -import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize import dev.fyloz.colorrecipesexplorer.rest.CRE_PROPERTIES import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH import java.net.URLEncoder @@ -15,19 +13,7 @@ import java.time.LocalDate import javax.persistence.* import javax.validation.constraints.* -private const val RECIPE_ID_NULL_MESSAGE = "Un identifiant est requis" -private const val RECIPE_NAME_NULL_MESSAGE = "Un nom est requis" -private const val RECIPE_DESCRIPTION_NULL_MESSAGE = "Une description est requise" -private const val RECIPE_COLOR_NULL_MESSAGE = "Une couleur est requise" -private const val RECIPE_GLOSS_NULL_MESSAGE = "Le lustre de la couleur est requis" -private const val RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE = "Le lustre doit être entre 0 et 100" -private const val RECIPE_SAMPLE_TOO_SMALL_MESSAGE = "Le numéro d'échantillon doit être supérieur ou égal à 0" -private const val RECIPE_COMPANY_NULL_MESSAGE = "Une bannière est requise" - -private const val RECIPE_STEPS_DTO_GROUP_ID_NULL_MESSAGE = "Un identifiant de groupe est requis" -private const val RECIPE_STEPS_DTO_MESSAGES_NULL_MESSAGE = "Des messages sont requis" - -private const val NOTE_GROUP_ID_NULL_MESSAGE = "Un identifiant de groupe est requis" +private const val VALIDATION_COLOR_PATTERN = "^#([0-9a-f]{6})$" const val RECIPE_IMAGES_DIRECTORY = "images/recipes" @@ -91,30 +77,28 @@ data class Recipe( } open class RecipeSaveDto( - @field:NotBlank(message = RECIPE_NAME_NULL_MESSAGE) + @field:NotBlank val name: String, - @field:NotBlank(message = RECIPE_DESCRIPTION_NULL_MESSAGE) + @field:NotBlank val description: String, - @field:NotBlank(message = RECIPE_COLOR_NULL_MESSAGE) - @field:Pattern(regexp = "^#([0-9a-f]{6})$") + @field:NotBlank + @field:Pattern(regexp = VALIDATION_COLOR_PATTERN) val color: String, - @field:NotNull(message = RECIPE_GLOSS_NULL_MESSAGE) - @field:Min(value = 0, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) - @field:Max(value = 100, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) + @field:Min(0, message = VALIDATION_RANGE_PERCENTS) + @field:Max(100, message = VALIDATION_RANGE_PERCENTS) val gloss: Byte, - @field:Min(value = 0, message = RECIPE_SAMPLE_TOO_SMALL_MESSAGE) + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) val sample: Int?, val approbationDate: LocalDate?, val remark: String?, - @field:Min(value = 0, message = RECIPE_COMPANY_NULL_MESSAGE) - val companyId: Long = -1L, + val companyId: Long ) : EntityDto { override fun toEntity(): Recipe = recipe( name = name, @@ -127,24 +111,23 @@ open class RecipeSaveDto( } open class RecipeUpdateDto( - @field:NotNull(message = RECIPE_ID_NULL_MESSAGE) val id: Long, - @field:NullOrNotBlank(message = RECIPE_NAME_NULL_MESSAGE) + @field:NotBlank val name: String?, - @field:NullOrNotBlank(message = RECIPE_DESCRIPTION_NULL_MESSAGE) + @field:NotBlank val description: String?, - @field:NullOrNotBlank(message = RECIPE_COLOR_NULL_MESSAGE) - @field:Pattern(regexp = "^#([0-9a-f]{6})$") + @field:NotBlank + @field:Pattern(regexp = VALIDATION_COLOR_PATTERN) val color: String?, - @field:Min(value = 0, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) - @field:Max(value = 100, message = RECIPE_GLOSS_OUTSIDE_RANGE_MESSAGE) + @field:Min(0, message = VALIDATION_RANGE_PERCENTS) + @field:Max(100, message = VALIDATION_RANGE_PERCENTS) val gloss: Byte?, - @field:NullOrSize(min = 0, message = RECIPE_SAMPLE_TOO_SMALL_MESSAGE) + @field:Min(0, message = VALIDATION_SIZE_GE_ZERO) val sample: Int?, val approbationDate: LocalDate?, @@ -188,15 +171,12 @@ data class RecipeGroupInformation( ) data class RecipeStepsDto( - @field:NotNull(message = RECIPE_STEPS_DTO_GROUP_ID_NULL_MESSAGE) val groupId: Long, - @field:NotNull(message = RECIPE_STEPS_DTO_MESSAGES_NULL_MESSAGE) val steps: Set ) data class RecipePublicDataDto( - @field:NotNull(message = RECIPE_ID_NULL_MESSAGE) val recipeId: Long, val notes: Set?, @@ -205,7 +185,6 @@ data class RecipePublicDataDto( ) data class NoteDto( - @field:NotNull(message = NOTE_GROUP_ID_NULL_MESSAGE) val groupId: Long, val content: String? @@ -290,8 +269,6 @@ 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, diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Group.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Group.kt index 4bc229d..1169c5e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Group.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Group.kt @@ -3,21 +3,16 @@ package dev.fyloz.colorrecipesexplorer.model.account import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException import dev.fyloz.colorrecipesexplorer.exception.NotFoundException import dev.fyloz.colorrecipesexplorer.exception.RestException -import dev.fyloz.colorrecipesexplorer.model.EntityDto -import dev.fyloz.colorrecipesexplorer.model.Model -import dev.fyloz.colorrecipesexplorer.model.NamedModel +import dev.fyloz.colorrecipesexplorer.model.* 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.NotEmpty 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 = "user_group") data class Group( @@ -43,11 +38,10 @@ data class Group( } open class GroupSaveDto( - @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) - @field:Size(min = 3) + @field:NotBlank val name: String, - @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) + @field:NotEmpty val permissions: MutableSet ) : EntityDto { override fun toEntity(): Group = @@ -55,14 +49,12 @@ open class GroupSaveDto( } open class GroupUpdateDto( - @field:NotNull(message = GROUP_ID_NULL_MESSAGE) val id: Long, - @field:NotBlank(message = GROUP_NAME_NULL_MESSAGE) - @field:Size(min = 3) + @field:NotBlank val name: String, - @field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE) + @field:NotEmpty val permissions: MutableSet ) : EntityDto { override fun toEntity(): Group = diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Permission.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Permission.kt index 333f8fb..abd2fb0 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Permission.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/Permission.kt @@ -9,14 +9,11 @@ enum class Permission( ) { READ_FILE, WRITE_FILE(listOf(READ_FILE)), - REMOVE_FILE(listOf(WRITE_FILE)), VIEW_RECIPES(listOf(READ_FILE)), VIEW_CATALOG(listOf(READ_FILE)), VIEW_USERS, - PRINT_MIXES(listOf(VIEW_RECIPES)), - EDIT_RECIPES_PUBLIC_DATA(listOf(VIEW_RECIPES)), EDIT_RECIPES(listOf(EDIT_RECIPES_PUBLIC_DATA, WRITE_FILE)), EDIT_MATERIALS(listOf(VIEW_CATALOG, WRITE_FILE)), @@ -25,29 +22,24 @@ enum class Permission( EDIT_USERS(listOf(VIEW_USERS)), EDIT_CATALOG(listOf(EDIT_MATERIALS, EDIT_MATERIAL_TYPES, EDIT_COMPANIES)), - REMOVE_RECIPES(listOf(EDIT_RECIPES, REMOVE_FILE)), - REMOVE_MATERIALS(listOf(EDIT_MATERIALS, REMOVE_FILE)), - 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)), + VIEW_TOUCH_UP_KITS, + EDIT_TOUCH_UP_KITS(listOf(VIEW_TOUCH_UP_KITS)), + PRINT_MIXES(listOf(VIEW_RECIPES)), ADD_TO_INVENTORY(listOf(VIEW_CATALOG)), DEDUCT_FROM_INVENTORY(listOf(VIEW_RECIPES)), - GENERATE_TOUCH_UP_KIT, ADMIN( listOf( + EDIT_RECIPES, EDIT_CATALOG, + EDIT_USERS, - REMOVE_RECIPES, - REMOVE_USERS, - REMOVE_CATALOG, + EDIT_TOUCH_UP_KITS, PRINT_MIXES, ADD_TO_INVENTORY, DEDUCT_FROM_INVENTORY, - GENERATE_TOUCH_UP_KIT ) ), @@ -69,6 +61,16 @@ enum class Permission( EDIT_EMPLOYEE_PASSWORD(listOf(EDIT_USERS), true), EDIT_EMPLOYEE_GROUP(listOf(EDIT_USERS), true), + REMOVE_FILE(listOf(WRITE_FILE), true), + GENERATE_TOUCH_UP_KIT(listOf(VIEW_TOUCH_UP_KITS), true), + + REMOVE_RECIPES(listOf(EDIT_RECIPES, REMOVE_FILE), true), + REMOVE_MATERIALS(listOf(EDIT_MATERIALS, REMOVE_FILE), true), + REMOVE_MATERIAL_TYPES(listOf(EDIT_MATERIAL_TYPES), true), + REMOVE_COMPANIES(listOf(EDIT_COMPANIES), true), + REMOVE_USERS(listOf(EDIT_USERS), true), + REMOVE_CATALOG(listOf(REMOVE_MATERIALS, REMOVE_MATERIAL_TYPES, REMOVE_COMPANIES), true), + REMOVE_RECIPE(listOf(REMOVE_RECIPES), true), REMOVE_MATERIAL(listOf(REMOVE_MATERIALS), true), REMOVE_MATERIAL_TYPE(listOf(REMOVE_MATERIAL_TYPES), true), diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt index c6e2b58..3f4a64a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt @@ -4,7 +4,6 @@ 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.validation.NullOrNotBlank import org.hibernate.annotations.Fetch import org.hibernate.annotations.FetchMode import org.springframework.security.core.GrantedAuthority @@ -13,14 +12,9 @@ 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 USER_ID_NULL_MESSAGE = "Un numéro d'utilisateur est requis" -private const val USER_LAST_NAME_EMPTY_MESSAGE = "Un nom est requis" -private const val USER_FIRST_NAME_EMPTY_MESSAGE = "Un prénom est requis" -private const val USER_PASSWORD_EMPTY_MESSAGE = "Un mot de passe est requis" -private const val USER_PASSWORD_TOO_SHORT_MESSAGE = "Le mot de passe doit contenir au moins 8 caractères" +private const val VALIDATION_PASSWORD_LENGTH = "Must contains at least 8 characters" @Entity @Table(name = "user") @@ -70,19 +64,17 @@ data class User( get() = flatPermissions.map { it.toAuthority() }.toMutableSet() } -/** DTO for creating users. Allows a [password] a [groupId]. */ open class UserSaveDto( - @field:NotNull(message = USER_ID_NULL_MESSAGE) val id: Long, - @field:NotBlank(message = USER_FIRST_NAME_EMPTY_MESSAGE) + @field:NotBlank val firstName: String, - @field:NotBlank(message = USER_LAST_NAME_EMPTY_MESSAGE) + @field:NotBlank val lastName: String, - @field:NotBlank(message = USER_PASSWORD_EMPTY_MESSAGE) - @field:Size(min = 8, message = USER_PASSWORD_TOO_SHORT_MESSAGE) + @field:NotBlank + @field:Size(min = 8, message = VALIDATION_PASSWORD_LENGTH) val password: String, val groupId: Long?, @@ -92,13 +84,12 @@ open class UserSaveDto( ) : EntityDto open class UserUpdateDto( - @field:NotNull(message = USER_ID_NULL_MESSAGE) val id: Long, - @field:NullOrNotBlank(message = USER_FIRST_NAME_EMPTY_MESSAGE) + @field:NotBlank val firstName: String?, - @field:NullOrNotBlank(message = USER_LAST_NAME_EMPTY_MESSAGE) + @field:NotBlank val lastName: String?, val groupId: Long?, diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/touchupkit/TouchUpKit.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/touchupkit/TouchUpKit.kt index 7300624..e6a01ed 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/touchupkit/TouchUpKit.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/touchupkit/TouchUpKit.kt @@ -1,12 +1,211 @@ package dev.fyloz.colorrecipesexplorer.model.touchupkit +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.VALIDATION_SIZE_GE_ONE +import java.time.LocalDate +import javax.persistence.* +import javax.validation.constraints.Min +import javax.validation.constraints.NotBlank +import javax.validation.constraints.NotEmpty + +const val TOUCH_UP_KIT_DELIMITER = ';' + +@Entity +@Table(name = "touch_up_kit") data class TouchUpKit( - val id: Long, + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, + val project: String, - val buggy: String + + val buggy: String, + + val company: String, + + val quantity: Int, + + @Column(name = "shipping_date") + val shippingDate: LocalDate, + + @Column(name = "finish") + private val finishConcatenated: String, + + @Column(name = "material") + private val materialConcatenated: String, + + @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true) + @JoinColumn(name = "touch_up_kit_id") + val content: Set +) : Model { + val finish + get() = finishConcatenated.split(TOUCH_UP_KIT_DELIMITER) + + val material + get() = materialConcatenated.split(TOUCH_UP_KIT_DELIMITER) +} + +@Entity +@Table(name = "touch_up_kit_product") +data class TouchUpKitProduct( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + override val id: Long?, + + val name: String, + + val description: String?, + + val quantity: Float +) : Model + +data class TouchUpKitSaveDto( + @field:NotBlank + val project: String, + + @field:NotBlank + val buggy: String, + + @field:NotBlank + val company: String, + + @field:Min(1, message = VALIDATION_SIZE_GE_ONE) + val quantity: Int, + + val shippingDate: LocalDate, + + @field:NotEmpty + val finish: List, + + @field:NotEmpty + val material: List, + + @field:NotEmpty + val content: Set +) : EntityDto { + override fun toEntity() = touchUpKit(this) +} + +data class TouchUpKitUpdateDto( + val id: Long, + + @field:NotBlank + val project: String?, + + @field:NotBlank + val buggy: String?, + + @field:NotBlank + val company: String?, + + @field:Min(1, message = VALIDATION_SIZE_GE_ONE) + val quantity: Int?, + + val shippingDate: LocalDate?, + + @field:NotEmpty + val finish: List?, + + @field:NotEmpty + val material: List?, + + @field:NotEmpty + val content: Set? +) : EntityDto + +data class TouchUpKitOutputDto( + override val id: Long, + val project: String, + val buggy: String, + val company: String, + val quantity: Int, + val shippingDate: LocalDate, + val finish: List, + val material: List, + val content: Set, + val pdfUrl: String +) : Model + +data class TouchUpKitProductDto( + val name: String, + val description: String?, + val quantity: Float ) -sealed class TouchUpKitCompany { - inline class CompanyName(val name: String) - class Company(val company: Company) -} +// ==== DSL ==== +fun touchUpKit( + id: Long? = null, + project: String = "project", + buggy: String = "buggy", + company: String = "company", + quantity: Int = 1, + shippingDate: LocalDate = LocalDate.now(), + finish: List, + material: List, + content: Set, + op: TouchUpKit.() -> Unit = {} +) = TouchUpKit( + id, + project, + buggy, + company, + quantity, + shippingDate, + finish.reduce { acc, f -> "$acc$TOUCH_UP_KIT_DELIMITER$f" }, + material.reduce { acc, f -> "$acc$TOUCH_UP_KIT_DELIMITER$f" }, + content +).apply(op) + +fun touchUpKit(touchUpKitSaveDto: TouchUpKitSaveDto) = + with(touchUpKitSaveDto) { + touchUpKit( + project = project, + buggy = buggy, + company = company, + quantity = quantity, + shippingDate = shippingDate, + finish = finish, + material = material, + content = content.map { touchUpKitProduct(it) }.toSet() + ) + } + +fun touchUpKitProduct( + id: Long? = null, + name: String = "product", + description: String? = "description", + quantity: Float = 1f, + op: TouchUpKitProduct.() -> Unit = {} +) = TouchUpKitProduct(id, name, description, quantity) + .apply(op) + +fun touchUpKitProduct(touchUpKitProductDto: TouchUpKitProductDto) = + touchUpKitProduct( + name = touchUpKitProductDto.name, + description = touchUpKitProductDto.description, + quantity = touchUpKitProductDto.quantity + ) + +// ==== Exceptions ==== +private const val TOUCH_UP_KIT_NOT_FOUND_EXCEPTION_TITLE = "Touch up kit not found" +private const val TOUCH_UP_KIT_ALREADY_EXISTS_EXCEPTION_TITLE = "Touch up kit already exists" +private const val TOUCH_UP_KIT_EXCEPTION_ERROR_CODE = "touchupkit" + +fun touchUpKitIdNotFoundException(id: Long) = + NotFoundException( + TOUCH_UP_KIT_EXCEPTION_ERROR_CODE, + TOUCH_UP_KIT_NOT_FOUND_EXCEPTION_TITLE, + "A touch up kit with the id $id could not be found", + id + ) + +fun touchUpKitIdAlreadyExistsException(id: Long) = + AlreadyExistsException( + TOUCH_UP_KIT_EXCEPTION_ERROR_CODE, + TOUCH_UP_KIT_ALREADY_EXISTS_EXCEPTION_TITLE, + "A touch up kit with the id $id already exists", + id + ) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/TouchUpKitRepository.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/TouchUpKitRepository.kt new file mode 100644 index 0000000..0819613 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/repository/TouchUpKitRepository.kt @@ -0,0 +1,6 @@ +package dev.fyloz.colorrecipesexplorer.repository + +import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKit +import org.springframework.data.jpa.repository.JpaRepository + +interface TouchUpKitRepository : JpaRepository diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt index 62f6d03..69165f4 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt @@ -1,7 +1,6 @@ package dev.fyloz.colorrecipesexplorer.rest import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeEditUsers -import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeRemoveUsers import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewUsers import dev.fyloz.colorrecipesexplorer.model.account.* import dev.fyloz.colorrecipesexplorer.service.UserService @@ -87,7 +86,7 @@ class UserController(private val userService: UserService) { } @DeleteMapping("{id}") - @PreAuthorizeRemoveUsers + @PreAuthorizeEditUsers fun deleteById(@PathVariable id: Long) = userService.deleteById(id) } @@ -147,7 +146,7 @@ class GroupsController( } @DeleteMapping("{id}") - @PreAuthorizeRemoveUsers + @PreAuthorizeEditUsers fun deleteById(@PathVariable id: Long) = noContent { groupService.deleteById(id) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt index e1acc00..f16f253 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/CompanyController.kt @@ -38,7 +38,7 @@ class CompanyController(private val companyService: CompanyService) { } @DeleteMapping("{id}") - @PreAuthorize("hasAuthority('REMOVE_COMPANIES')") + @PreAuthorize("hasAuthority('EDIT_COMPANIES')") fun deleteById(@PathVariable id: Long) = noContent { companyService.deleteById(id) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt index 8e99b03..0bec46a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialController.kt @@ -64,7 +64,7 @@ class MaterialController( } @DeleteMapping("{id}") - @PreAuthorize("hasAuthority('REMOVE_MATERIALS')") + @PreAuthorize("hasAuthority('EDIT_MATERIALS')") fun deleteById(@PathVariable id: Long) = noContent { materialService.deleteById(id) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt index 50e26f0..877f8d1 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/MaterialTypeController.kt @@ -38,7 +38,7 @@ class MaterialTypeController(private val materialTypeService: MaterialTypeServic } @DeleteMapping("{id}") - @PreAuthorize("hasAuthority('REMOVE_MATERIAL_TYPES')") + @PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')") fun deleteById(@PathVariable id: Long) = noContent { materialTypeService.deleteById(id) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt index 8bf447e..efdd414 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/RecipeController.kt @@ -1,11 +1,8 @@ package dev.fyloz.colorrecipesexplorer.rest import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeEditRecipes -import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeRemoveRecipes import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewRecipes -import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties import dev.fyloz.colorrecipesexplorer.model.* -import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH import dev.fyloz.colorrecipesexplorer.service.MixService import dev.fyloz.colorrecipesexplorer.service.RecipeImageService import dev.fyloz.colorrecipesexplorer.service.RecipeService @@ -14,8 +11,6 @@ import org.springframework.http.ResponseEntity import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.* import org.springframework.web.multipart.MultipartFile -import java.net.URLEncoder -import java.nio.charset.StandardCharsets import javax.validation.Valid @@ -61,7 +56,7 @@ class RecipeController( } @DeleteMapping("{id}") - @PreAuthorizeRemoveRecipes + @PreAuthorizeEditRecipes fun deleteById(@PathVariable id: Long) = noContent { recipeService.deleteById(id) @@ -105,7 +100,7 @@ class MixController(private val mixService: MixService) { } @DeleteMapping("{id}") - @PreAuthorizeRemoveRecipes + @PreAuthorizeEditRecipes fun deleteById(@PathVariable id: Long) = noContent { mixService.deleteById(id) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/TouchUpKitController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/TouchUpKitController.kt new file mode 100644 index 0000000..c39684d --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/TouchUpKitController.kt @@ -0,0 +1,63 @@ +package dev.fyloz.colorrecipesexplorer.rest + +import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitOutputDto +import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitSaveDto +import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitUpdateDto +import dev.fyloz.colorrecipesexplorer.service.touchupkit.TouchUpKitService +import org.springframework.core.io.ByteArrayResource +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import javax.validation.Valid + +const val TOUCH_UP_KIT_CONTROLLER_PATH = "/api/touchupkit" + +@RestController +@RequestMapping(TOUCH_UP_KIT_CONTROLLER_PATH) +@PreAuthorize("hasAuthority('VIEW_TOUCH_UP_KITS')") +class TouchUpKitController( + private val touchUpKitService: TouchUpKitService +) { + @GetMapping + fun getAll() = + ok(touchUpKitService.getAllForOutput()) + + @GetMapping("{id}") + fun getById(@PathVariable id: Long) = + ok(touchUpKitService.getByIdForOutput(id)) + + @PostMapping + @PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')") + fun save(@Valid @RequestBody touchUpKit: TouchUpKitSaveDto) = + created(TOUCH_UP_KIT_CONTROLLER_PATH) { + with(touchUpKitService) { + save(touchUpKit).toOutput() + } + } + + @PutMapping + @PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')") + fun update(@Valid @RequestBody touchUpKit: TouchUpKitUpdateDto) = + noContent { + touchUpKitService.update(touchUpKit) + } + + @DeleteMapping("{id}") + @PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')") + fun deleteById(@PathVariable id: Long) = + noContent { + touchUpKitService.deleteById(id) + } + + @GetMapping("pdf") + fun getJobPdf(@RequestParam project: String): ResponseEntity { + with(touchUpKitService.generateJobPdfResource(project)) { + return ResponseEntity.ok() + .header("Content-Disposition", "filename=TouchUpKit_$project.pdf") + .contentLength(this.contentLength()) + .contentType(MediaType.APPLICATION_PDF) + .body(this) + } + } +} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/FileController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/FileController.kt index 9e5125b..cad230a 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/FileController.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/FileController.kt @@ -46,7 +46,7 @@ class FileController( } @DeleteMapping - @PreAuthorize("hasAnyAuthority('REMOVE_FILE')") + @PreAuthorize("hasAnyAuthority('WRITE_FILE')") fun delete(@RequestParam path: String): ResponseEntity { return noContent { fileService.delete(path) diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/TouchUpKitController.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/TouchUpKitController.kt deleted file mode 100644 index 6993025..0000000 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/files/TouchUpKitController.kt +++ /dev/null @@ -1,26 +0,0 @@ -package dev.fyloz.colorrecipesexplorer.rest.files - -import dev.fyloz.colorrecipesexplorer.service.files.TouchUpKitService -import org.springframework.core.io.ByteArrayResource -import org.springframework.http.MediaType -import org.springframework.http.ResponseEntity -import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.web.bind.annotation.* - -@RestController -@RequestMapping("/api/touchup") -@PreAuthorize("hasAuthority('GENERATE_TOUCH_UP_KIT')") -class TouchUpKitController( - private val touchUpKitService: TouchUpKitService -) { - @GetMapping - fun getJobPdf(@RequestParam job: String): ResponseEntity { - with(touchUpKitService.generateJobPdfResource(job)) { - return ResponseEntity.ok() - .header("Content-Disposition", "filename=TouchUpKit_$job.pdf") - .contentLength(this.contentLength()) - .contentType(MediaType.APPLICATION_PDF) - .body(this) - } - } -} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt index 02b843d..cb5c3ab 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/MaterialService.kt @@ -136,7 +136,7 @@ class MaterialServiceImpl( override fun delete(entity: Material) { if (!repository.canBeDeleted(entity.id!!)) throw cannotDeleteMaterialException(entity) - fileService.delete(entity.simdutFilePath) + if (fileService.exists(entity.simdutFilePath)) fileService.delete(entity.simdutFilePath) super.delete(entity) } } diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/touchupkit/TouchUpKitService.kt similarity index 52% rename from src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.kt rename to src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/touchupkit/TouchUpKitService.kt index 68b043e..79c9440 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/touchupkit/TouchUpKitService.kt @@ -1,6 +1,12 @@ -package dev.fyloz.colorrecipesexplorer.service.files +package dev.fyloz.colorrecipesexplorer.service.touchupkit import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties +import dev.fyloz.colorrecipesexplorer.model.touchupkit.* +import dev.fyloz.colorrecipesexplorer.repository.TouchUpKitRepository +import dev.fyloz.colorrecipesexplorer.rest.TOUCH_UP_KIT_CONTROLLER_PATH +import dev.fyloz.colorrecipesexplorer.service.AbstractExternalModelService +import dev.fyloz.colorrecipesexplorer.service.ExternalModelService +import dev.fyloz.colorrecipesexplorer.service.files.FileService import dev.fyloz.colorrecipesexplorer.utils.* import org.springframework.core.io.ByteArrayResource import org.springframework.stereotype.Service @@ -10,7 +16,8 @@ private const val TOUCH_UP_KIT_FILES_PATH = "pdf/touchupkits" const val TOUCH_UP_TEXT_FR = "KIT DE RETOUCHE" const val TOUCH_UP_TEXT_EN = "TOUCH UP KIT" -interface TouchUpKitService { +interface TouchUpKitService : + ExternalModelService { /** Generates and returns a [PdfDocument] for the given [job]. */ fun generateJobPdf(job: String): PdfDocument @@ -29,8 +36,45 @@ interface TouchUpKitService { @Service class TouchUpKitServiceImpl( private val fileService: FileService, - private val creProperties: CreProperties -) : TouchUpKitService { + touchUpKitRepository: TouchUpKitRepository, + private val creProperties: CreProperties, +) : AbstractExternalModelService( + touchUpKitRepository +), TouchUpKitService { + override fun idNotFoundException(id: Long) = touchUpKitIdNotFoundException(id) + override fun idAlreadyExistsException(id: Long) = touchUpKitIdAlreadyExistsException(id) + + override fun TouchUpKit.toOutput() = TouchUpKitOutputDto( + this.id!!, + this.project, + this.buggy, + this.company, + this.quantity, + this.shippingDate, + this.finish, + this.material, + this.content, + this.pdfUrl() + ) + + override fun update(entity: TouchUpKitUpdateDto): TouchUpKit { + val persistedKit by lazy { getById(entity.id) } + + return super.update(with(entity) { + touchUpKit( + id = id, + project = project ?: persistedKit.project, + buggy = buggy ?: persistedKit.buggy, + company = company ?: persistedKit.company, + quantity = quantity ?: persistedKit.quantity, + shippingDate = shippingDate ?: persistedKit.shippingDate, + finish = finish ?: persistedKit.finish, + material = material ?: persistedKit.material, + content = content?.map { touchUpKitProduct(it) }?.toSet() ?: persistedKit.content + ) + }) + } + override fun generateJobPdf(job: String) = pdf { container { centeredVertically = true @@ -75,4 +119,7 @@ class TouchUpKitServiceImpl( private fun String.pdfDocumentPath() = "$TOUCH_UP_KIT_FILES_PATH/$this.pdf" + + private fun TouchUpKit.pdfUrl() = + "${creProperties.deploymentUrl}$TOUCH_UP_KIT_CONTROLLER_PATH/pdf?job=$project" } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 74dd17a..d6fad50 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -21,7 +21,7 @@ entities.material-types.baseName=Base databaseupdater.username=root databaseupdater.password=pass # DEBUG -spring.jpa.show-sql=true +spring.jpa.show-sql=false # Do not modify spring.messages.fallback-to-system-locale=true spring.servlet.multipart.max-file-size=10MB @@ -30,4 +30,6 @@ spring.jpa.open-in-view=true server.http2.enabled=true server.error.whitelabel.enabled=false spring.h2.console.enabled=false +spring.jackson.deserialization.fail-on-null-for-primitives=true +spring.jackson.default-property-inclusion=non_null spring.profiles.active=@spring.profiles.active@ diff --git a/src/main/resources/junit-platform.properties b/src/main/resources/junit-platform.properties deleted file mode 100644 index 2af5bf8..0000000 --- a/src/main/resources/junit-platform.properties +++ /dev/null @@ -1 +0,0 @@ -junit.jupiter.testinstance.lifecycle.default=per_class diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt index 4affc53..3813fd5 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/TouchUpKitServiceTest.kt @@ -1,6 +1,10 @@ package dev.fyloz.colorrecipesexplorer.service.files import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties +import dev.fyloz.colorrecipesexplorer.repository.TouchUpKitRepository +import dev.fyloz.colorrecipesexplorer.service.touchupkit.TOUCH_UP_TEXT_EN +import dev.fyloz.colorrecipesexplorer.service.touchupkit.TOUCH_UP_TEXT_FR +import dev.fyloz.colorrecipesexplorer.service.touchupkit.TouchUpKitServiceImpl import dev.fyloz.colorrecipesexplorer.utils.PdfDocument import dev.fyloz.colorrecipesexplorer.utils.toByteArrayResource import io.mockk.* @@ -10,13 +14,14 @@ import org.springframework.core.io.ByteArrayResource import kotlin.test.assertEquals private class TouchUpKitServiceTestContext { + val touchUpKitRepository = mockk() val fileService = mockk { every { write(any(), any(), any()) } just Runs } val creProperties = mockk { every { cacheGeneratedFiles } returns false } - val touchUpKitService = spyk(TouchUpKitServiceImpl(fileService, creProperties)) + val touchUpKitService = spyk(TouchUpKitServiceImpl(fileService, touchUpKitRepository, creProperties)) val pdfDocumentData = mockk() val pdfDocument = mockk { mockkStatic(PdfDocument::toByteArrayResource)