- Add @RequiresHouseholdRole("member") to GET /{planId}/variety-preview endpoint
to require household membership (was accessible to any authenticated user)
- Extract scoreFromSimulatedSlots() private method eliminating duplicate logic
between simulateVarietyScore() and the old computeCurrentScore()
- Fix loose variety preview test assertions (isBetween → exact assertEquals)
- Add test verifying negative scoreDelta when candidate is a duplicate recipe
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
4.3 KiB
Java
111 lines
4.3 KiB
Java
package com.recipeapp.planning;
|
|
|
|
import com.recipeapp.common.RequiresHouseholdRole;
|
|
import com.recipeapp.planning.dto.*;
|
|
import com.recipeapp.recipe.HouseholdResolver;
|
|
import jakarta.validation.Valid;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.security.Principal;
|
|
import java.time.LocalDate;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
@RestController
|
|
@RequestMapping("/v1/week-plans")
|
|
public class WeekPlanController {
|
|
|
|
private final PlanningService planningService;
|
|
private final HouseholdResolver householdResolver;
|
|
|
|
public WeekPlanController(PlanningService planningService, HouseholdResolver householdResolver) {
|
|
this.planningService = planningService;
|
|
this.householdResolver = householdResolver;
|
|
}
|
|
|
|
@GetMapping
|
|
public WeekPlanResponse getWeekPlan(Principal principal, @RequestParam LocalDate weekStart) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
return planningService.getWeekPlan(householdId, weekStart);
|
|
}
|
|
|
|
@PostMapping
|
|
public ResponseEntity<WeekPlanResponse> createWeekPlan(
|
|
Principal principal,
|
|
@Valid @RequestBody CreateWeekPlanRequest request) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
WeekPlanResponse response = planningService.createWeekPlan(householdId, request.weekStart());
|
|
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
|
}
|
|
|
|
@PostMapping("/{id}/slots")
|
|
@RequiresHouseholdRole("planner")
|
|
public ResponseEntity<SlotResponse> addSlot(
|
|
Principal principal,
|
|
@PathVariable UUID id,
|
|
@Valid @RequestBody CreateSlotRequest request) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
SlotResponse response = planningService.addSlot(householdId, id, request);
|
|
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
|
}
|
|
|
|
@PatchMapping("/{planId}/slots/{slotId}")
|
|
@RequiresHouseholdRole("planner")
|
|
public SlotResponse updateSlot(
|
|
Principal principal,
|
|
@PathVariable UUID planId,
|
|
@PathVariable UUID slotId,
|
|
@Valid @RequestBody UpdateSlotRequest request) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
return planningService.updateSlot(householdId, planId, slotId, request);
|
|
}
|
|
|
|
@DeleteMapping("/{planId}/slots/{slotId}")
|
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
|
@RequiresHouseholdRole("planner")
|
|
public void deleteSlot(
|
|
Principal principal,
|
|
@PathVariable UUID planId,
|
|
@PathVariable UUID slotId) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
planningService.deleteSlot(householdId, planId, slotId);
|
|
}
|
|
|
|
@PostMapping("/{id}/confirm")
|
|
public WeekPlanResponse confirmPlan(Principal principal, @PathVariable UUID id) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
return planningService.confirmPlan(householdId, id);
|
|
}
|
|
|
|
@GetMapping("/{id}/suggestions")
|
|
public SuggestionResponse getSuggestions(
|
|
Principal principal,
|
|
@PathVariable UUID id,
|
|
@RequestParam LocalDate slotDate,
|
|
@RequestParam(required = false) List<String> tags,
|
|
@RequestParam(required = false) Integer topN) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
return planningService.getSuggestions(householdId, id, slotDate,
|
|
tags != null ? tags : List.of(), topN);
|
|
}
|
|
|
|
@GetMapping("/{id}/variety-score")
|
|
public VarietyScoreResponse getVarietyScore(Principal principal, @PathVariable UUID id) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
return planningService.getVarietyScore(householdId, id);
|
|
}
|
|
|
|
@GetMapping("/{planId}/variety-preview")
|
|
@RequiresHouseholdRole("member")
|
|
public VarietyPreviewResponse getVarietyPreview(
|
|
Principal principal,
|
|
@PathVariable UUID planId,
|
|
@RequestParam UUID recipeId,
|
|
@RequestParam LocalDate date) {
|
|
UUID householdId = householdResolver.resolve(principal.getName());
|
|
return planningService.getVarietyPreview(householdId, planId, recipeId, date);
|
|
}
|
|
}
|