Skip to content

Commit 0895ea7

Browse files
committed
Merge branch 'main' of https://github.com/AET-DevOps26/team-bytebite into feature/shopping-list-ui
2 parents fd946f2 + a647735 commit 0895ea7

16 files changed

Lines changed: 402 additions & 6 deletions

File tree

databases/grocery-db/init.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ CREATE TABLE IF NOT EXISTS grocery_lists (
2424
grocery_list_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
2525
name VARCHAR(255) NOT NULL,
2626
outdated BOOLEAN NOT NULL DEFAULT FALSE,
27-
user_id UUID NOT NULL
27+
user_id UUID NOT NULL,
28+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
2829
);
2930

3031
CREATE TABLE IF NOT EXISTS grocery_list_recipes (

databases/user-db/init.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ CREATE TABLE IF NOT EXISTS users (
88
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
99
);
1010

11+
-- Dev admin account: login with admin@bytebite.dev / password
12+
INSERT INTO users (name, email, password_hash) VALUES (
13+
'Admin',
14+
'admin@bytebite.dev',
15+
'$2b$12$F0rLm1sYDRHEUjQkni8z3e7dpeFNW4irxo4jkMgP4YMGffswtD.OS'
16+
) ON CONFLICT (email) DO NOTHING;

server/api-gateway/src/main/resources/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ spring:
3232
- id: grocery-service
3333
uri: ${GROCERY_SERVICE_BASE_URL:http://localhost:8082}
3434
predicates:
35-
- Path=/api/recipes/**
35+
- Path=/api/recipes/**,/api/grocery-list/**
3636
- id: user-service
3737
uri: ${USER_SERVICE_BASE_URL:http://localhost:8083}
3838
predicates:

server/grocery-service/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@
4848
<scope>runtime</scope>
4949
</dependency>
5050

51+
<dependency>
52+
<groupId>org.springframework.boot</groupId>
53+
<artifactId>spring-boot-starter-data-jpa</artifactId>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>org.postgresql</groupId>
58+
<artifactId>postgresql</artifactId>
59+
<scope>runtime</scope>
60+
</dependency>
61+
5162
<dependency>
5263
<groupId>org.springframework.boot</groupId>
5364
<artifactId>spring-boot-starter-test</artifactId>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.bytebite.server;
2+
3+
import com.bytebite.server.dto.GroceryListCreateRequest;
4+
import com.bytebite.server.dto.GroceryListDetailDTO;
5+
import com.bytebite.server.dto.GroceryListSummaryDTO;
6+
import com.bytebite.server.service.GroceryListService;
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import io.swagger.v3.oas.annotations.Parameter;
9+
import io.swagger.v3.oas.annotations.media.Content;
10+
import io.swagger.v3.oas.annotations.media.Schema;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
12+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
13+
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import org.springframework.http.MediaType;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.web.bind.annotation.DeleteMapping;
17+
import org.springframework.web.bind.annotation.GetMapping;
18+
import org.springframework.web.bind.annotation.PathVariable;
19+
import org.springframework.web.bind.annotation.PostMapping;
20+
import org.springframework.web.bind.annotation.RequestBody;
21+
import org.springframework.web.bind.annotation.RequestMapping;
22+
import org.springframework.web.bind.annotation.RestController;
23+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
24+
25+
import java.net.URI;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.UUID;
29+
30+
@RestController
31+
@RequestMapping("/api/grocery-list")
32+
@Tag(name = "Grocery Lists", description = "Stored grocery-list history and details")
33+
public class GroceryListController {
34+
35+
private final GroceryListService service;
36+
37+
public GroceryListController(GroceryListService service) {
38+
this.service = service;
39+
}
40+
41+
@GetMapping(value = "/history", produces = MediaType.APPLICATION_JSON_VALUE)
42+
@Operation(
43+
summary = "List recent grocery lists",
44+
description = "Returns a summary of the 20 most recently created grocery lists, newest first.",
45+
security = @SecurityRequirement(name = "bearerAuth"),
46+
responses = {
47+
@ApiResponse(responseCode = "200", description = "Grocery-list summaries"),
48+
@ApiResponse(responseCode = "401", description = "Missing, expired, or invalid JWT", content = @Content)
49+
}
50+
)
51+
public List<GroceryListSummaryDTO> getHistory() {
52+
return service.getHistory();
53+
}
54+
55+
@GetMapping(value = "/history/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
56+
@Operation(
57+
summary = "Get a grocery list by id",
58+
description = "Returns a single grocery list including its items.",
59+
security = @SecurityRequirement(name = "bearerAuth"),
60+
responses = {
61+
@ApiResponse(responseCode = "200", description = "Grocery list with items"),
62+
@ApiResponse(responseCode = "401", description = "Missing, expired, or invalid JWT", content = @Content),
63+
@ApiResponse(responseCode = "404", description = "Grocery list not found", content = @Content(schema = @Schema(implementation = Map.class)))
64+
}
65+
)
66+
public GroceryListDetailDTO getById(
67+
@Parameter(description = "Identifier of the grocery list", required = true)
68+
@PathVariable UUID id) {
69+
return service.getById(id);
70+
}
71+
72+
@PostMapping(value = "/history",
73+
consumes = MediaType.APPLICATION_JSON_VALUE,
74+
produces = MediaType.APPLICATION_JSON_VALUE)
75+
@Operation(
76+
summary = "Create a grocery list",
77+
description = "Persists a new grocery list with its items and returns the created resource.",
78+
security = @SecurityRequirement(name = "bearerAuth"),
79+
responses = {
80+
@ApiResponse(responseCode = "201", description = "Grocery list created"),
81+
@ApiResponse(responseCode = "400", description = "Invalid request body", content = @Content(schema = @Schema(implementation = Map.class))),
82+
@ApiResponse(responseCode = "401", description = "Missing, expired, or invalid JWT", content = @Content)
83+
}
84+
)
85+
public ResponseEntity<GroceryListDetailDTO> create(@RequestBody GroceryListCreateRequest request) {
86+
GroceryListDetailDTO created = service.create(request);
87+
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
88+
.path("/{id}")
89+
.buildAndExpand(created.id())
90+
.toUri();
91+
return ResponseEntity.created(location).body(created);
92+
}
93+
94+
@DeleteMapping(value = "/history/{id}")
95+
@Operation(
96+
summary = "Delete a grocery list",
97+
description = "Deletes the grocery list identified by the given id along with its items.",
98+
security = @SecurityRequirement(name = "bearerAuth"),
99+
responses = {
100+
@ApiResponse(responseCode = "204", description = "Grocery list deleted"),
101+
@ApiResponse(responseCode = "401", description = "Missing, expired, or invalid JWT", content = @Content),
102+
@ApiResponse(responseCode = "404", description = "Grocery list not found", content = @Content(schema = @Schema(implementation = Map.class)))
103+
}
104+
)
105+
public ResponseEntity<Void> delete(
106+
@Parameter(description = "Identifier of the grocery list", required = true)
107+
@PathVariable UUID id) {
108+
service.delete(id);
109+
return ResponseEntity.noContent().build();
110+
}
111+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.bytebite.server.dto;
2+
3+
public record GroceryItemRequestDTO(String name, double quantity, String unit, String category, boolean purchased) {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.bytebite.server.dto;
2+
3+
import java.util.UUID;
4+
5+
public record GroceryItemResponseDTO(UUID id, String name, double quantity, String unit, String category, boolean purchased) {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.bytebite.server.dto;
2+
3+
import java.util.List;
4+
import java.util.UUID;
5+
6+
public record GroceryListCreateRequest(String name, UUID userId, List<GroceryItemRequestDTO> items) {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.bytebite.server.dto;
2+
3+
import java.time.Instant;
4+
import java.util.List;
5+
import java.util.UUID;
6+
7+
public record GroceryListDetailDTO(UUID id, String name, Instant createdAt, List<GroceryItemResponseDTO> items) {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.bytebite.server.dto;
2+
3+
import java.time.Instant;
4+
import java.util.UUID;
5+
6+
public record GroceryListSummaryDTO(UUID id, String name, Instant createdAt) {}

0 commit comments

Comments
 (0)