feat(shopping): finalize GET /v1/shopping-list endpoint and regenerate OpenAPI types
Renamed endpoint to /v1/shopping-list to avoid Springdoc path conflict.
Added @RequiresHouseholdRole("planner") on generate. Regenerated
frontend OpenAPI schema with all new shopping list endpoints.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ public class ShoppingListController {
|
|||||||
this.householdResolver = householdResolver;
|
this.householdResolver = householdResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/v1/shopping-lists")
|
@GetMapping("/v1/shopping-list")
|
||||||
public ShoppingListResponse getByWeekStart(
|
public ShoppingListResponse getByWeekStart(
|
||||||
@RequestParam(required = false) LocalDate weekStart,
|
@RequestParam(required = false) LocalDate weekStart,
|
||||||
Principal principal) {
|
Principal principal) {
|
||||||
|
|||||||
4
backend/src/main/resources/application-docker.yml
Normal file
4
backend/src/main/resources/application-docker.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
spring:
|
||||||
|
flyway:
|
||||||
|
locations: classpath:db/migration,classpath:db/seed
|
||||||
|
out-of-order: true
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
ALTER TABLE shopping_list
|
ALTER TABLE shopping_list
|
||||||
ADD COLUMN generated_at timestamptz NOT NULL DEFAULT now();
|
ADD COLUMN IF NOT EXISTS generated_at timestamptz NOT NULL DEFAULT now();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ShoppingListControllerTest {
|
|||||||
when(householdResolver.resolve("sarah@example.com")).thenReturn(HOUSEHOLD_ID);
|
when(householdResolver.resolve("sarah@example.com")).thenReturn(HOUSEHOLD_ID);
|
||||||
when(shoppingService.getByWeekStart(eq(HOUSEHOLD_ID), any())).thenReturn(response);
|
when(shoppingService.getByWeekStart(eq(HOUSEHOLD_ID), any())).thenReturn(response);
|
||||||
|
|
||||||
mockMvc.perform(get("/v1/shopping-lists")
|
mockMvc.perform(get("/v1/shopping-list")
|
||||||
.param("weekStart", "2026-04-06")
|
.param("weekStart", "2026-04-06")
|
||||||
.principal(() -> "sarah@example.com"))
|
.principal(() -> "sarah@example.com"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -69,7 +69,7 @@ class ShoppingListControllerTest {
|
|||||||
when(householdResolver.resolve("sarah@example.com")).thenReturn(HOUSEHOLD_ID);
|
when(householdResolver.resolve("sarah@example.com")).thenReturn(HOUSEHOLD_ID);
|
||||||
when(shoppingService.getByWeekStart(eq(HOUSEHOLD_ID), any())).thenReturn(null);
|
when(shoppingService.getByWeekStart(eq(HOUSEHOLD_ID), any())).thenReturn(null);
|
||||||
|
|
||||||
mockMvc.perform(get("/v1/shopping-lists")
|
mockMvc.perform(get("/v1/shopping-list")
|
||||||
.param("weekStart", "2026-04-06")
|
.param("weekStart", "2026-04-06")
|
||||||
.principal(() -> "sarah@example.com"))
|
.principal(() -> "sarah@example.com"))
|
||||||
.andExpect(status().isNotFound());
|
.andExpect(status().isNotFound());
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
49
frontend/src/lib/api/schema.d.ts
vendored
49
frontend/src/lib/api/schema.d.ts
vendored
@@ -452,6 +452,22 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/v1/shopping-list": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get: operations["getByWeekStart"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/v1/ingredients": {
|
"/v1/ingredients": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -624,6 +640,11 @@ export interface components {
|
|||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
recipeId: string;
|
recipeId: string;
|
||||||
};
|
};
|
||||||
|
RecipeRef: {
|
||||||
|
/** Format: uuid */
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
ShoppingListItemResponse: {
|
ShoppingListItemResponse: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id?: string;
|
||||||
@@ -636,13 +657,17 @@ export interface components {
|
|||||||
isChecked?: boolean;
|
isChecked?: boolean;
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
checkedBy?: string;
|
checkedBy?: string;
|
||||||
sourceRecipes?: string[];
|
sourceRecipes?: components["schemas"]["RecipeRef"][];
|
||||||
};
|
};
|
||||||
ShoppingListResponse: {
|
ShoppingListResponse: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id?: string;
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
weekPlanId?: string;
|
weekPlanId?: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
generatedAt?: string;
|
||||||
|
/** Format: int32 */
|
||||||
|
filteredStaplesCount?: number;
|
||||||
items?: components["schemas"]["ShoppingListItemResponse"][];
|
items?: components["schemas"]["ShoppingListItemResponse"][];
|
||||||
};
|
};
|
||||||
TagCreateRequest: {
|
TagCreateRequest: {
|
||||||
@@ -1902,6 +1927,28 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
getByWeekStart: {
|
||||||
|
parameters: {
|
||||||
|
query?: {
|
||||||
|
weekStart?: string;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description OK */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"*/*": components["schemas"]["ShoppingListResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
searchIngredients: {
|
searchIngredients: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: {
|
query?: {
|
||||||
|
|||||||
Reference in New Issue
Block a user