Fix and polish category CRUD.
This commit is contained in:
@@ -7,10 +7,11 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
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.CategoryNotFoundException;
|
||||
import org.codiki.domain.category.model.Category;
|
||||
import org.codiki.domain.category.model.builder.CategoryBuilder;
|
||||
import org.codiki.domain.category.port.CategoryPort;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -33,7 +34,11 @@ public class CategoryUseCases {
|
||||
|
||||
List<Category> subCategories = emptyList();
|
||||
if (!isNull(subCategoryIds)) {
|
||||
subCategories = categoryPort.findAllByIds(subCategoryIds);
|
||||
try {
|
||||
subCategories = categoryPort.findAllByIds(subCategoryIds);
|
||||
} catch (CategoryNotFoundException exception) {
|
||||
throw new CategoryEditionException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
Category newCategory = aCategory()
|
||||
@@ -48,19 +53,44 @@ public class CategoryUseCases {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
Category categoryToUpdate = categoryPort.findById(categoryId)
|
||||
.orElseThrow(() -> new CategoryNotFoundException(categoryId));
|
||||
|
||||
Category updatedCategory = aCategory()
|
||||
.basedOn(categoryToUpdate)
|
||||
.build();
|
||||
CategoryBuilder categoryBuilder = aCategory()
|
||||
.basedOn(categoryToUpdate);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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 static org.springframework.http.HttpStatus.CREATED;
|
||||
import static org.springframework.http.HttpStatus.NO_CONTENT;
|
||||
import org.codiki.application.category.CategoryUseCases;
|
||||
import org.codiki.domain.category.model.Category;
|
||||
import org.codiki.exposition.category.model.CategoryDto;
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
@@ -43,4 +45,10 @@ public class CategoryController {
|
||||
);
|
||||
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.NOT_FOUND;
|
||||
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.CategoryNotFoundException;
|
||||
import org.codiki.domain.exception.LoginFailureException;
|
||||
@@ -73,4 +74,10 @@ public class GlobalControllerExceptionHandler {
|
||||
public void handleCategoryEditionException() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@ResponseStatus(BAD_REQUEST)
|
||||
@ExceptionHandler(CategoryDeletionException.class)
|
||||
public void handleCategoryDeletionException() {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
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.OPTIONS;
|
||||
import static org.springframework.http.HttpMethod.POST;
|
||||
import static org.springframework.http.HttpMethod.PUT;
|
||||
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -52,6 +54,14 @@ public class SecurityConfiguration {
|
||||
POST,
|
||||
"/api/categories"
|
||||
).hasRole("ADMIN")
|
||||
.requestMatchers(
|
||||
PUT,
|
||||
"/api/categories/{categoryId}"
|
||||
).hasRole("ADMIN")
|
||||
.requestMatchers(
|
||||
DELETE,
|
||||
"/api/categories/{categoryId}"
|
||||
).hasRole("ADMIN")
|
||||
.requestMatchers(OPTIONS).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.codiki.domain.category.exception.CategoryNotFoundException;
|
||||
import org.codiki.domain.category.model.Category;
|
||||
import org.codiki.domain.category.port.CategoryPort;
|
||||
import org.codiki.infrastructure.category.model.CategoryEntity;
|
||||
@@ -31,10 +32,34 @@ public class CategoryJpaAdapter implements CategoryPort {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Category> findAllByIds(final List<UUID> subCategoryIds) {
|
||||
return categoryRepository.findAllById(subCategoryIds)
|
||||
public List<Category> findAllByIds(List<UUID> categoryIds) {
|
||||
final List<Category> categories = categoryRepository.findAllById(categoryIds)
|
||||
.stream()
|
||||
.map(CategoryEntity::toDomain)
|
||||
.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.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> {
|
||||
@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 {
|
||||
url: http://localhost:8080
|
||||
publicationId: fce1de27-11c6-4deb-a248-b63288c00037
|
||||
bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxNWExM2RjNy0wMjlkLTRlYWItYTYzZC1jMWU5NmY5MDI0MWQiLCJleHAiOjE3MTAyNTE0MzV9.0-KmVfwoyJ1JDZs-f2paEZVAljCPVkcEi33bYra4hoVSvECFsdc0CFlJKpWEeEswIv4jSsnEzs7yFW_XM9WWAA
|
||||
categoryId: 25742844-f0c2-454a-a4d1-71ad3bda28df
|
||||
bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxNWExM2RjNy0wMjlkLTRlYWItYTYzZC1jMWU5NmY5MDI0MWQiLCJleHAiOjE3MTAyNjMyOTh9.j-PRoIYVjPcpaE92u8sEUsZib1mGTrZmfd96ZgnGgskUqhnoUtCHwQbmbvHTdkr2XdypSU3Hq9dndwxU4ElmWA
|
||||
categoryId: 872abbb4-a287-4519-8eeb-c43d567d89c8
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user