mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 19:30:16 +01:00
feat(production): Rezept archivieren (#30)
This commit is contained in:
parent
7b1c114693
commit
408813a5b5
5 changed files with 99 additions and 1 deletions
|
|
@ -0,0 +1,53 @@
|
|||
package de.effigenix.application.production;
|
||||
|
||||
import de.effigenix.application.production.command.ArchiveRecipeCommand;
|
||||
import de.effigenix.domain.production.*;
|
||||
import de.effigenix.shared.common.Result;
|
||||
import de.effigenix.shared.security.ActorId;
|
||||
import de.effigenix.shared.security.AuthorizationPort;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Transactional
|
||||
public class ArchiveRecipe {
|
||||
|
||||
private final RecipeRepository recipeRepository;
|
||||
private final AuthorizationPort authorizationPort;
|
||||
|
||||
public ArchiveRecipe(RecipeRepository recipeRepository, AuthorizationPort authorizationPort) {
|
||||
this.recipeRepository = recipeRepository;
|
||||
this.authorizationPort = authorizationPort;
|
||||
}
|
||||
|
||||
public Result<RecipeError, Recipe> execute(ArchiveRecipeCommand cmd, ActorId performedBy) {
|
||||
if (!authorizationPort.can(performedBy, ProductionAction.RECIPE_WRITE)) {
|
||||
return Result.failure(new RecipeError.Unauthorized("Not authorized to modify recipes"));
|
||||
}
|
||||
|
||||
var recipeId = RecipeId.of(cmd.recipeId());
|
||||
|
||||
Recipe recipe;
|
||||
switch (recipeRepository.findById(recipeId)) {
|
||||
case Result.Failure(var err) ->
|
||||
{ return Result.failure(new RecipeError.RepositoryFailure(err.message())); }
|
||||
case Result.Success(var opt) -> {
|
||||
if (opt.isEmpty()) {
|
||||
return Result.failure(new RecipeError.RecipeNotFound(recipeId));
|
||||
}
|
||||
recipe = opt.get();
|
||||
}
|
||||
}
|
||||
|
||||
switch (recipe.archive()) {
|
||||
case Result.Failure(var err) -> { return Result.failure(err); }
|
||||
case Result.Success(var ignored) -> { }
|
||||
}
|
||||
|
||||
switch (recipeRepository.save(recipe)) {
|
||||
case Result.Failure(var err) ->
|
||||
{ return Result.failure(new RecipeError.RepositoryFailure(err.message())); }
|
||||
case Result.Success(var ignored) -> { }
|
||||
}
|
||||
|
||||
return Result.success(recipe);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package de.effigenix.application.production.command;
|
||||
|
||||
public record ArchiveRecipeCommand(String recipeId) {
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ import static de.effigenix.shared.common.Result.*;
|
|||
* 10. Step numbers must be unique within a recipe
|
||||
* 11. Recipe can only be activated when it has at least one ingredient
|
||||
* 12. Recipe can only be activated from DRAFT status
|
||||
* 13. Recipe can only be archived from ACTIVE status
|
||||
*/
|
||||
public class Recipe {
|
||||
|
||||
|
|
@ -232,6 +233,15 @@ public class Recipe {
|
|||
return Result.success(null);
|
||||
}
|
||||
|
||||
public Result<RecipeError, Void> archive() {
|
||||
if (status != RecipeStatus.ACTIVE) {
|
||||
return Result.failure(new RecipeError.InvalidStatusTransition(status, RecipeStatus.ARCHIVED));
|
||||
}
|
||||
this.status = RecipeStatus.ARCHIVED;
|
||||
touch();
|
||||
return Result.success(null);
|
||||
}
|
||||
|
||||
// ==================== Getters ====================
|
||||
|
||||
public RecipeId id() { return id; }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package de.effigenix.infrastructure.config;
|
||||
|
||||
import de.effigenix.application.production.ActivateRecipe;
|
||||
import de.effigenix.application.production.ArchiveRecipe;
|
||||
import de.effigenix.application.production.AddProductionStep;
|
||||
import de.effigenix.application.production.AddRecipeIngredient;
|
||||
import de.effigenix.application.production.CreateRecipe;
|
||||
|
|
@ -43,4 +44,9 @@ public class ProductionUseCaseConfiguration {
|
|||
public ActivateRecipe activateRecipe(RecipeRepository recipeRepository, AuthorizationPort authorizationPort) {
|
||||
return new ActivateRecipe(recipeRepository, authorizationPort);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ArchiveRecipe archiveRecipe(RecipeRepository recipeRepository, AuthorizationPort authorizationPort) {
|
||||
return new ArchiveRecipe(recipeRepository, authorizationPort);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package de.effigenix.infrastructure.production.web.controller;
|
||||
|
||||
import de.effigenix.application.production.ActivateRecipe;
|
||||
import de.effigenix.application.production.ArchiveRecipe;
|
||||
import de.effigenix.application.production.AddProductionStep;
|
||||
import de.effigenix.application.production.AddRecipeIngredient;
|
||||
import de.effigenix.application.production.CreateRecipe;
|
||||
import de.effigenix.application.production.RemoveProductionStep;
|
||||
import de.effigenix.application.production.RemoveRecipeIngredient;
|
||||
import de.effigenix.application.production.command.ActivateRecipeCommand;
|
||||
import de.effigenix.application.production.command.ArchiveRecipeCommand;
|
||||
import de.effigenix.application.production.command.AddProductionStepCommand;
|
||||
import de.effigenix.application.production.command.AddRecipeIngredientCommand;
|
||||
import de.effigenix.application.production.command.CreateRecipeCommand;
|
||||
|
|
@ -43,19 +45,22 @@ public class RecipeController {
|
|||
private final AddProductionStep addProductionStep;
|
||||
private final RemoveProductionStep removeProductionStep;
|
||||
private final ActivateRecipe activateRecipe;
|
||||
private final ArchiveRecipe archiveRecipe;
|
||||
|
||||
public RecipeController(CreateRecipe createRecipe,
|
||||
AddRecipeIngredient addRecipeIngredient,
|
||||
RemoveRecipeIngredient removeRecipeIngredient,
|
||||
AddProductionStep addProductionStep,
|
||||
RemoveProductionStep removeProductionStep,
|
||||
ActivateRecipe activateRecipe) {
|
||||
ActivateRecipe activateRecipe,
|
||||
ArchiveRecipe archiveRecipe) {
|
||||
this.createRecipe = createRecipe;
|
||||
this.addRecipeIngredient = addRecipeIngredient;
|
||||
this.removeRecipeIngredient = removeRecipeIngredient;
|
||||
this.addProductionStep = addProductionStep;
|
||||
this.removeProductionStep = removeProductionStep;
|
||||
this.activateRecipe = activateRecipe;
|
||||
this.archiveRecipe = archiveRecipe;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
|
|
@ -192,6 +197,26 @@ public class RecipeController {
|
|||
return ResponseEntity.ok(RecipeResponse.from(result.unsafeGetValue()));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/archive")
|
||||
@PreAuthorize("hasAuthority('RECIPE_WRITE')")
|
||||
public ResponseEntity<RecipeResponse> archiveRecipe(
|
||||
@PathVariable("id") String recipeId,
|
||||
Authentication authentication
|
||||
) {
|
||||
var actorId = extractActorId(authentication);
|
||||
logger.info("Archiving recipe: {} by actor: {}", recipeId, actorId.value());
|
||||
|
||||
var cmd = new ArchiveRecipeCommand(recipeId);
|
||||
var result = archiveRecipe.execute(cmd, actorId);
|
||||
|
||||
if (result.isFailure()) {
|
||||
throw new RecipeDomainErrorException(result.unsafeGetError());
|
||||
}
|
||||
|
||||
logger.info("Recipe archived: {}", recipeId);
|
||||
return ResponseEntity.ok(RecipeResponse.from(result.unsafeGetValue()));
|
||||
}
|
||||
|
||||
private ActorId extractActorId(Authentication authentication) {
|
||||
if (authentication == null || authentication.getName() == null) {
|
||||
throw new IllegalStateException("No authentication found in SecurityContext");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue