Fix and polish category CRUD.
This commit is contained in:
@@ -7,10 +7,11 @@ import java.util.Optional;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.codiki.domain.category.model.builder.CategoryBuilder.aCategory;
|
import static org.codiki.domain.category.model.builder.CategoryBuilder.aCategory;
|
||||||
import static org.springframework.util.CollectionUtils.isEmpty;
|
import org.codiki.domain.category.exception.CategoryDeletionException;
|
||||||
import org.codiki.domain.category.exception.CategoryEditionException;
|
import org.codiki.domain.category.exception.CategoryEditionException;
|
||||||
import org.codiki.domain.category.exception.CategoryNotFoundException;
|
import org.codiki.domain.category.exception.CategoryNotFoundException;
|
||||||
import org.codiki.domain.category.model.Category;
|
import org.codiki.domain.category.model.Category;
|
||||||
|
import org.codiki.domain.category.model.builder.CategoryBuilder;
|
||||||
import org.codiki.domain.category.port.CategoryPort;
|
import org.codiki.domain.category.port.CategoryPort;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -33,7 +34,11 @@ public class CategoryUseCases {
|
|||||||
|
|
||||||
List<Category> subCategories = emptyList();
|
List<Category> subCategories = emptyList();
|
||||||
if (!isNull(subCategoryIds)) {
|
if (!isNull(subCategoryIds)) {
|
||||||
|
try {
|
||||||
subCategories = categoryPort.findAllByIds(subCategoryIds);
|
subCategories = categoryPort.findAllByIds(subCategoryIds);
|
||||||
|
} catch (CategoryNotFoundException exception) {
|
||||||
|
throw new CategoryEditionException(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Category newCategory = aCategory()
|
Category newCategory = aCategory()
|
||||||
@@ -48,19 +53,44 @@ public class CategoryUseCases {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Category updateCategory(UUID categoryId, String name, List<UUID> subCategoryIds) {
|
public Category updateCategory(UUID categoryId, String name, List<UUID> subCategoryIds) {
|
||||||
if (isNull(name) && isEmpty(subCategoryIds)) {
|
if (isNull(name) && isNull(subCategoryIds)) {
|
||||||
throw new CategoryEditionException("no any field is filled");
|
throw new CategoryEditionException("no any field is filled");
|
||||||
}
|
}
|
||||||
|
|
||||||
Category categoryToUpdate = categoryPort.findById(categoryId)
|
Category categoryToUpdate = categoryPort.findById(categoryId)
|
||||||
.orElseThrow(() -> new CategoryNotFoundException(categoryId));
|
.orElseThrow(() -> new CategoryNotFoundException(categoryId));
|
||||||
|
|
||||||
Category updatedCategory = aCategory()
|
CategoryBuilder categoryBuilder = aCategory()
|
||||||
.basedOn(categoryToUpdate)
|
.basedOn(categoryToUpdate);
|
||||||
.build();
|
|
||||||
|
|
||||||
|
if (!isNull(name)) {
|
||||||
|
categoryBuilder.withName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNull(subCategoryIds)) {
|
||||||
|
List<Category> subCategories;
|
||||||
|
try {
|
||||||
|
subCategories = categoryPort.findAllByIds(subCategoryIds);
|
||||||
|
} catch (CategoryNotFoundException exception) {
|
||||||
|
throw new CategoryEditionException(exception);
|
||||||
|
}
|
||||||
|
categoryBuilder.withSubCategories(subCategories);
|
||||||
|
}
|
||||||
|
|
||||||
|
Category updatedCategory = categoryBuilder.build();
|
||||||
categoryPort.save(updatedCategory);
|
categoryPort.save(updatedCategory);
|
||||||
|
|
||||||
return updatedCategory;
|
return updatedCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteCategory(UUID categoryId) {
|
||||||
|
if (!categoryPort.existsById(categoryId)) {
|
||||||
|
throw new CategoryNotFoundException(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (categoryPort.existsAnyAssociatedPublication(categoryId)) {
|
||||||
|
throw new CategoryDeletionException(categoryId, "some publications are associated to the category");
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryPort.deleteById(categoryId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.codiki.domain.category.exception;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.codiki.domain.exception.FunctionnalException;
|
||||||
|
|
||||||
|
public class CategoryDeletionException extends FunctionnalException {
|
||||||
|
public CategoryDeletionException(UUID categoryId, String cause) {
|
||||||
|
super(String.format("Impossible to delete category with id %s. Cause: %s.", categoryId, cause));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,4 +12,10 @@ public interface CategoryPort {
|
|||||||
void save(Category category);
|
void save(Category category);
|
||||||
|
|
||||||
List<Category> findAllByIds(List<UUID> subCategoryIds);
|
List<Category> findAllByIds(List<UUID> subCategoryIds);
|
||||||
|
|
||||||
|
boolean existsAnyAssociatedPublication(UUID categoryId);
|
||||||
|
|
||||||
|
void deleteById(UUID categoryId);
|
||||||
|
|
||||||
|
boolean existsById(UUID categoryId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package org.codiki.exposition.category;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.springframework.http.HttpStatus.CREATED;
|
import static org.springframework.http.HttpStatus.CREATED;
|
||||||
|
import static org.springframework.http.HttpStatus.NO_CONTENT;
|
||||||
import org.codiki.application.category.CategoryUseCases;
|
import org.codiki.application.category.CategoryUseCases;
|
||||||
import org.codiki.domain.category.model.Category;
|
import org.codiki.domain.category.model.Category;
|
||||||
import org.codiki.exposition.category.model.CategoryDto;
|
import org.codiki.exposition.category.model.CategoryDto;
|
||||||
import org.codiki.exposition.category.model.CategoryEditionRequest;
|
import org.codiki.exposition.category.model.CategoryEditionRequest;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
@@ -43,4 +45,10 @@ public class CategoryController {
|
|||||||
);
|
);
|
||||||
return new CategoryDto(createdCategory);
|
return new CategoryDto(createdCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{categoryId}")
|
||||||
|
@ResponseStatus(NO_CONTENT)
|
||||||
|
public void deleteCategory(@PathVariable("categoryId") UUID categoryId) {
|
||||||
|
categoryUseCases.deleteCategory(categoryId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
|||||||
import static org.springframework.http.HttpStatus.FORBIDDEN;
|
import static org.springframework.http.HttpStatus.FORBIDDEN;
|
||||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||||
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
|
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
|
||||||
|
import org.codiki.domain.category.exception.CategoryDeletionException;
|
||||||
import org.codiki.domain.category.exception.CategoryEditionException;
|
import org.codiki.domain.category.exception.CategoryEditionException;
|
||||||
import org.codiki.domain.category.exception.CategoryNotFoundException;
|
import org.codiki.domain.category.exception.CategoryNotFoundException;
|
||||||
import org.codiki.domain.exception.LoginFailureException;
|
import org.codiki.domain.exception.LoginFailureException;
|
||||||
@@ -73,4 +74,10 @@ public class GlobalControllerExceptionHandler {
|
|||||||
public void handleCategoryEditionException() {
|
public void handleCategoryEditionException() {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResponseStatus(BAD_REQUEST)
|
||||||
|
@ExceptionHandler(CategoryDeletionException.class)
|
||||||
|
public void handleCategoryDeletionException() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package org.codiki.exposition.configuration.security;
|
package org.codiki.exposition.configuration.security;
|
||||||
|
|
||||||
|
import static org.springframework.http.HttpMethod.DELETE;
|
||||||
import static org.springframework.http.HttpMethod.GET;
|
import static org.springframework.http.HttpMethod.GET;
|
||||||
import static org.springframework.http.HttpMethod.OPTIONS;
|
import static org.springframework.http.HttpMethod.OPTIONS;
|
||||||
import static org.springframework.http.HttpMethod.POST;
|
import static org.springframework.http.HttpMethod.POST;
|
||||||
|
import static org.springframework.http.HttpMethod.PUT;
|
||||||
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
|
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -52,6 +54,14 @@ public class SecurityConfiguration {
|
|||||||
POST,
|
POST,
|
||||||
"/api/categories"
|
"/api/categories"
|
||||||
).hasRole("ADMIN")
|
).hasRole("ADMIN")
|
||||||
|
.requestMatchers(
|
||||||
|
PUT,
|
||||||
|
"/api/categories/{categoryId}"
|
||||||
|
).hasRole("ADMIN")
|
||||||
|
.requestMatchers(
|
||||||
|
DELETE,
|
||||||
|
"/api/categories/{categoryId}"
|
||||||
|
).hasRole("ADMIN")
|
||||||
.requestMatchers(OPTIONS).permitAll()
|
.requestMatchers(OPTIONS).permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.codiki.domain.category.exception.CategoryNotFoundException;
|
||||||
import org.codiki.domain.category.model.Category;
|
import org.codiki.domain.category.model.Category;
|
||||||
import org.codiki.domain.category.port.CategoryPort;
|
import org.codiki.domain.category.port.CategoryPort;
|
||||||
import org.codiki.infrastructure.category.model.CategoryEntity;
|
import org.codiki.infrastructure.category.model.CategoryEntity;
|
||||||
@@ -31,10 +32,34 @@ public class CategoryJpaAdapter implements CategoryPort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Category> findAllByIds(final List<UUID> subCategoryIds) {
|
public List<Category> findAllByIds(List<UUID> categoryIds) {
|
||||||
return categoryRepository.findAllById(subCategoryIds)
|
final List<Category> categories = categoryRepository.findAllById(categoryIds)
|
||||||
.stream()
|
.stream()
|
||||||
.map(CategoryEntity::toDomain)
|
.map(CategoryEntity::toDomain)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
Optional<UUID> notFoundCategoryId = categories.stream()
|
||||||
|
.filter(category -> !categoryIds.contains(category.id()))
|
||||||
|
.findFirst()
|
||||||
|
.map(Category::id);
|
||||||
|
if (notFoundCategoryId.isPresent()) {
|
||||||
|
throw new CategoryNotFoundException(notFoundCategoryId.get());
|
||||||
|
}
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsAnyAssociatedPublication(UUID categoryId) {
|
||||||
|
return categoryRepository.existsAnyAssociatedPublication(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteById(UUID categoryId) {
|
||||||
|
categoryRepository.deleteById(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsById(UUID categoryId) {
|
||||||
|
return categoryRepository.existsById(categoryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import org.codiki.infrastructure.category.model.CategoryEntity;
|
import org.codiki.infrastructure.category.model.CategoryEntity;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
public interface CategoryRepository extends JpaRepository<CategoryEntity, UUID> {
|
public interface CategoryRepository extends JpaRepository<CategoryEntity, UUID> {
|
||||||
|
@Query(value = """
|
||||||
|
SELECT (
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM publication p
|
||||||
|
WHERE p.category_id = :categoryId
|
||||||
|
) > 0
|
||||||
|
""", nativeQuery = true)
|
||||||
|
boolean existsAnyAssociatedPublication(@Param("categoryId") UUID categoryId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
vars {
|
vars {
|
||||||
url: http://localhost:8080
|
url: http://localhost:8080
|
||||||
publicationId: fce1de27-11c6-4deb-a248-b63288c00037
|
publicationId: fce1de27-11c6-4deb-a248-b63288c00037
|
||||||
bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxNWExM2RjNy0wMjlkLTRlYWItYTYzZC1jMWU5NmY5MDI0MWQiLCJleHAiOjE3MTAyNTE0MzV9.0-KmVfwoyJ1JDZs-f2paEZVAljCPVkcEi33bYra4hoVSvECFsdc0CFlJKpWEeEswIv4jSsnEzs7yFW_XM9WWAA
|
bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxNWExM2RjNy0wMjlkLTRlYWItYTYzZC1jMWU5NmY5MDI0MWQiLCJleHAiOjE3MTAyNjMyOTh9.j-PRoIYVjPcpaE92u8sEUsZib1mGTrZmfd96ZgnGgskUqhnoUtCHwQbmbvHTdkr2XdypSU3Hq9dndwxU4ElmWA
|
||||||
categoryId: 25742844-f0c2-454a-a4d1-71ad3bda28df
|
categoryId: 872abbb4-a287-4519-8eeb-c43d567d89c8
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user