From b0e682e82e75b4d868877d3ff29342fb65222e65 Mon Sep 17 00:00:00 2001 From: Florian THIERRY Date: Tue, 12 Mar 2024 14:24:17 +0100 Subject: [PATCH] Implementation of category creation. --- .../category/CategoryCreationValidator.java | 7 +++ .../category/CategoryUseCases.java | 26 +++++++++++ .../exception/CategoryEditionException.java | 13 ++++++ .../model/builder/CategoryBuilder.java | 46 +++++++++++++++++++ .../domain/category/port/CategoryPort.java | 7 ++- .../category/CategoryController.java | 30 ++++++++++++ .../model/CategoryEditionRequest.java | 9 ++++ .../GlobalControllerExceptionHandler.java | 7 +++ .../security/SecurityConfiguration.java | 4 ++ .../category/CategoryJpaAdapter.java | 15 ++++++ rest-client-collection/Codiki/Users/Login.bru | 18 -------- .../Codiki/environments/localhost.bru | 2 +- 12 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 codiki-application/src/main/java/org/codiki/application/category/CategoryCreationValidator.java create mode 100644 codiki-domain/src/main/java/org/codiki/domain/category/exception/CategoryEditionException.java create mode 100644 codiki-domain/src/main/java/org/codiki/domain/category/model/builder/CategoryBuilder.java create mode 100644 codiki-exposition/src/main/java/org/codiki/exposition/category/CategoryController.java create mode 100644 codiki-exposition/src/main/java/org/codiki/exposition/category/model/CategoryEditionRequest.java delete mode 100644 rest-client-collection/Codiki/Users/Login.bru diff --git a/codiki-application/src/main/java/org/codiki/application/category/CategoryCreationValidator.java b/codiki-application/src/main/java/org/codiki/application/category/CategoryCreationValidator.java new file mode 100644 index 0000000..9e9d5d8 --- /dev/null +++ b/codiki-application/src/main/java/org/codiki/application/category/CategoryCreationValidator.java @@ -0,0 +1,7 @@ +package org.codiki.application.category; + +import org.springframework.stereotype.Component; + +@Component +public class CategoryCreationValidator { +} diff --git a/codiki-application/src/main/java/org/codiki/application/category/CategoryUseCases.java b/codiki-application/src/main/java/org/codiki/application/category/CategoryUseCases.java index fad504e..d5755fe 100644 --- a/codiki-application/src/main/java/org/codiki/application/category/CategoryUseCases.java +++ b/codiki-application/src/main/java/org/codiki/application/category/CategoryUseCases.java @@ -1,8 +1,13 @@ package org.codiki.application.category; +import static java.util.Collections.emptyList; +import static java.util.Objects.isNull; +import java.util.List; import java.util.Optional; import java.util.UUID; +import static org.codiki.domain.category.model.builder.CategoryBuilder.aCategory; +import org.codiki.domain.category.exception.CategoryEditionException; import org.codiki.domain.category.model.Category; import org.codiki.domain.category.port.CategoryPort; import org.springframework.stereotype.Service; @@ -18,4 +23,25 @@ public class CategoryUseCases { public Optional findById(UUID categoryId) { return categoryPort.findById(categoryId); } + + public Category createCategory(String name, List subCategoryIds) { + if (isNull(name)) { + throw new CategoryEditionException("name can not be empty"); + } + + List subCategories = emptyList(); + if (!isNull(subCategoryIds)) { + subCategories = categoryPort.findAllByIds(subCategoryIds); + } + + Category newCategory = aCategory() + .withId(UUID.randomUUID()) + .withName(name) + .withSubCategories(subCategories) + .build(); + + categoryPort.save(newCategory); + + return newCategory; + } } diff --git a/codiki-domain/src/main/java/org/codiki/domain/category/exception/CategoryEditionException.java b/codiki-domain/src/main/java/org/codiki/domain/category/exception/CategoryEditionException.java new file mode 100644 index 0000000..5da0655 --- /dev/null +++ b/codiki-domain/src/main/java/org/codiki/domain/category/exception/CategoryEditionException.java @@ -0,0 +1,13 @@ +package org.codiki.domain.category.exception; + +import org.codiki.domain.exception.FunctionnalException; + +public class CategoryEditionException extends FunctionnalException { + public CategoryEditionException(String reason) { + super(String.format("Impossible to edit a category because : %s.", reason)); + } + + public CategoryEditionException(FunctionnalException cause) { + super("Impossible to edit a category due to a root cause.", cause); + } +} diff --git a/codiki-domain/src/main/java/org/codiki/domain/category/model/builder/CategoryBuilder.java b/codiki-domain/src/main/java/org/codiki/domain/category/model/builder/CategoryBuilder.java new file mode 100644 index 0000000..87836ef --- /dev/null +++ b/codiki-domain/src/main/java/org/codiki/domain/category/model/builder/CategoryBuilder.java @@ -0,0 +1,46 @@ +package org.codiki.domain.category.model.builder; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.codiki.domain.category.model.Category; + +public class CategoryBuilder { + private UUID id; + private String name; + private List subCategories; + + public static CategoryBuilder aCategory() { + return new CategoryBuilder(); + } + + private CategoryBuilder() {} + + public CategoryBuilder withId(UUID id) { + this.id = id; + return this; + } + + public CategoryBuilder withName(String name) { + this.name = name; + return this; + } + + public CategoryBuilder withSubCategories(List subCategories) { + this.subCategories = subCategories; + return this; + } + + public CategoryBuilder withSubCategory(Category subCategory) { + if (subCategories == null) { + subCategories = new ArrayList<>(); + } + subCategories.add(subCategory); + return this; + } + + public Category build() { + return new Category(id, name, subCategories); + } +} diff --git a/codiki-domain/src/main/java/org/codiki/domain/category/port/CategoryPort.java b/codiki-domain/src/main/java/org/codiki/domain/category/port/CategoryPort.java index e22649e..f8e3e2f 100644 --- a/codiki-domain/src/main/java/org/codiki/domain/category/port/CategoryPort.java +++ b/codiki-domain/src/main/java/org/codiki/domain/category/port/CategoryPort.java @@ -1,10 +1,15 @@ package org.codiki.domain.category.port; +import java.util.List; import java.util.Optional; import java.util.UUID; import org.codiki.domain.category.model.Category; public interface CategoryPort { - Optional findById(final UUID uuid); + Optional findById(UUID uuid); + + void save(Category category); + + List findAllByIds(List subCategoryIds); } diff --git a/codiki-exposition/src/main/java/org/codiki/exposition/category/CategoryController.java b/codiki-exposition/src/main/java/org/codiki/exposition/category/CategoryController.java new file mode 100644 index 0000000..2eac23d --- /dev/null +++ b/codiki-exposition/src/main/java/org/codiki/exposition/category/CategoryController.java @@ -0,0 +1,30 @@ +package org.codiki.exposition.category; + +import static org.springframework.http.HttpStatus.CREATED; +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.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/categories") +public class CategoryController { + private final CategoryUseCases categoryUseCases; + + public CategoryController(CategoryUseCases categoryUseCases) { + this.categoryUseCases = categoryUseCases; + } + + @PostMapping + @ResponseStatus(CREATED) + public CategoryDto createCategory(@RequestBody CategoryEditionRequest request) { + Category createdCategory = categoryUseCases.createCategory(request.name(), request.subCategoryIds()); + return new CategoryDto(createdCategory); + } +} diff --git a/codiki-exposition/src/main/java/org/codiki/exposition/category/model/CategoryEditionRequest.java b/codiki-exposition/src/main/java/org/codiki/exposition/category/model/CategoryEditionRequest.java new file mode 100644 index 0000000..aca4e48 --- /dev/null +++ b/codiki-exposition/src/main/java/org/codiki/exposition/category/model/CategoryEditionRequest.java @@ -0,0 +1,9 @@ +package org.codiki.exposition.category.model; + +import java.util.List; +import java.util.UUID; + +public record CategoryEditionRequest( + String name, + List subCategoryIds +) {} diff --git a/codiki-exposition/src/main/java/org/codiki/exposition/configuration/GlobalControllerExceptionHandler.java b/codiki-exposition/src/main/java/org/codiki/exposition/configuration/GlobalControllerExceptionHandler.java index 73271dc..aaa5113 100644 --- a/codiki-exposition/src/main/java/org/codiki/exposition/configuration/GlobalControllerExceptionHandler.java +++ b/codiki-exposition/src/main/java/org/codiki/exposition/configuration/GlobalControllerExceptionHandler.java @@ -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.CategoryEditionException; import org.codiki.domain.category.exception.CategoryNotFoundException; import org.codiki.domain.exception.LoginFailureException; import org.codiki.domain.exception.RefreshTokenDoesNotExistException; @@ -66,4 +67,10 @@ public class GlobalControllerExceptionHandler { public void handlePublicationUpdateForbiddenException() { // Do nothing. } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(CategoryEditionException.class) + public void handleCategoryEditionException() { + // Do nothing. + } } diff --git a/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/SecurityConfiguration.java b/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/SecurityConfiguration.java index ab5fbf8..a8a1bc6 100644 --- a/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/SecurityConfiguration.java +++ b/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/SecurityConfiguration.java @@ -48,6 +48,10 @@ public class SecurityConfiguration { "/api/users/login", "/api/users/refresh-token" ).permitAll() + .requestMatchers( + POST, + "/api/categories" + ).hasRole("ADMIN") .requestMatchers(OPTIONS).permitAll() .anyRequest().authenticated() ); diff --git a/codiki-infrastructure/src/main/java/org/codiki/infrastructure/category/CategoryJpaAdapter.java b/codiki-infrastructure/src/main/java/org/codiki/infrastructure/category/CategoryJpaAdapter.java index b262cdb..b5aeb13 100644 --- a/codiki-infrastructure/src/main/java/org/codiki/infrastructure/category/CategoryJpaAdapter.java +++ b/codiki-infrastructure/src/main/java/org/codiki/infrastructure/category/CategoryJpaAdapter.java @@ -1,5 +1,6 @@ package org.codiki.infrastructure.category; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -22,4 +23,18 @@ public class CategoryJpaAdapter implements CategoryPort { return categoryRepository.findById(categoryId) .map(CategoryEntity::toDomain); } + + @Override + public void save(Category category) { + CategoryEntity categoryEntity = new CategoryEntity(category); + categoryRepository.save(categoryEntity); + } + + @Override + public List findAllByIds(final List subCategoryIds) { + return categoryRepository.findAllById(subCategoryIds) + .stream() + .map(CategoryEntity::toDomain) + .toList(); + } } diff --git a/rest-client-collection/Codiki/Users/Login.bru b/rest-client-collection/Codiki/Users/Login.bru deleted file mode 100644 index 784638d..0000000 --- a/rest-client-collection/Codiki/Users/Login.bru +++ /dev/null @@ -1,18 +0,0 @@ -meta { - name: Login - type: http - seq: 1 -} - -post { - url: {{url}}/api/users/login - body: json - auth: none -} - -body:json { - { - "id": "5ad462b8-8f9e-4a26-bb86-c74fef5d11b6", - "password": "password" - } -} diff --git a/rest-client-collection/Codiki/environments/localhost.bru b/rest-client-collection/Codiki/environments/localhost.bru index 44db5b5..66729af 100644 --- a/rest-client-collection/Codiki/environments/localhost.bru +++ b/rest-client-collection/Codiki/environments/localhost.bru @@ -1,5 +1,5 @@ vars { url: http://localhost:8080 publicationId: fce1de27-11c6-4deb-a248-b63288c00037 - bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1YWQ0NjJiOC04ZjllLTRhMjYtYmI4Ni1jNzRmZWY1ZDExYjYiLCJleHAiOjE3MTAyNDk1MjJ9.gKS4h4sWXlFn4DImsXk6NDa2wEz8ZpG0qoX-IaGPHHaMJObds4qVqK91WPgrVQ6Ci0_W6wCoDImLrrPEDgtJag + bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxNWExM2RjNy0wMjlkLTRlYWItYTYzZC1jMWU5NmY5MDI0MWQiLCJleHAiOjE3MTAyNTE0MzV9.0-KmVfwoyJ1JDZs-f2paEZVAljCPVkcEi33bYra4hoVSvECFsdc0CFlJKpWEeEswIv4jSsnEzs7yFW_XM9WWAA }