Skip to content

Commit bc93b6d

Browse files
authored
Merge pull request #25 from Jalen-Stephens/6-api-implement-imagecontroller-endpoints
6 api implement imagecontroller endpoints
2 parents d3d7725 + aec6f6a commit bc93b6d

File tree

6 files changed

+418
-61
lines changed

6 files changed

+418
-61
lines changed

citations.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,3 +935,119 @@ Assistance was used to design and implement comprehensive branch-coverage unit t
935935
> Portions of this commit or configuration were generated with assistance from OpenAI ChatGPT (GPT-5) on October 22, 2025. All AI-generated content was reviewed, verified, and finalized by the development team.
936936
937937
---
938+
939+
### **Commit / Ticket Reference**
940+
941+
* **Commit:** `api(#6): wire ImageController to ImageService and update metadata handling`
942+
* **Ticket:** `#6 — Implement ImageController endpoints`
943+
* **Date:** October 22, 2025
944+
* **Team Member:** Jalen Stephens
945+
946+
---
947+
948+
### **AI Tool Information**
949+
950+
* **Tool Used:** OpenAI ChatGPT (GPT-5)
951+
* **Access Method:** ChatGPT Web (.edu academic access)
952+
* **Configuration:** Default model settings
953+
* **Cost:** $0 (no paid API calls)
954+
955+
---
956+
957+
### **Purpose of AI Assistance**
958+
959+
The AI assisted in implementing the HTTP-facing controller layer by wiring `ImageController` to the existing `ImageService`, ensuring correct DTO mappings, handling ownership enforcement, and aligning update endpoints with the final DTO definitions. It also confirmed correct HTTP response shapes and status codes.
960+
961+
---
962+
963+
### **Prompts / Interaction Summary**
964+
965+
* “let’s implement the ticket”
966+
* “here is image controller”
967+
* “here is my user service”
968+
* “we can redo the dtos”
969+
* Compile errors surfaced → AI realigned controller logic with actual DTO structure
970+
971+
---
972+
973+
### **Resulting Artifacts**
974+
975+
* `src/main/java/dev/coms4156/project/metadetect/controller/ImageController.java` (updated)
976+
* Metadata update endpoint corrected to match `UpdateImageRequest` structure (note + labels only)
977+
* Exception → HTTP mapping added (404 / 403)
978+
979+
---
980+
981+
### **Verification**
982+
983+
* Successful Maven compilation after DTO alignment
984+
* Manual inspection of controller flow against schema and service logic
985+
* Local run verified routing and method resolution
986+
987+
---
988+
989+
### **Attribution Statement**
990+
991+
> Portions of this commit or configuration were generated with assistance from OpenAI ChatGPT (GPT-5) on October 22, 2025. All AI-generated content was reviewed, verified, and finalized by the development team.
992+
993+
---
994+
995+
### **Commit / Ticket Reference**
996+
997+
* **Commit:** `api(#6): finalize ImageController + DTOs, add controller tests and move C2PA check to unit test`
998+
* **Ticket:** `#6 — Implement ImageController endpoints`
999+
* **Date:** October 22, 2025
1000+
* **Team Member:** Jalen Stephens
1001+
1002+
---
1003+
1004+
### **AI Tool Information**
1005+
1006+
* **Tool Used:** OpenAI ChatGPT (GPT-5)
1007+
* **Access Method:** ChatGPT Web (.edu academic access)
1008+
* **Configuration:** Default model settings
1009+
* **Cost:** $0 (no paid API calls)
1010+
1011+
---
1012+
1013+
### **Purpose of AI Assistance**
1014+
1015+
Helped implement the controller logic integrating with the `ImageService`, updated DTO structures to match Supabase schema, and wrote comprehensive MockMvc-based unit tests to achieve branch and error-path coverage.
1016+
1017+
---
1018+
1019+
### **Prompts / Interaction Summary**
1020+
1021+
* “Redo this ticket because we are using Supabase”
1022+
* “Let’s implement the ticket”
1023+
* “Generate test cases”
1024+
* “Fix test failures and remove integration test dependency on c2patool”
1025+
* “One line commit description”
1026+
1027+
---
1028+
1029+
### **Resulting Artifacts**
1030+
1031+
* Updated: `ImageController.java`
1032+
* Updated: `Dtos.java`
1033+
* Added: `ImageControllerTest.java` (MockMvc tests)
1034+
* Added: `AnalyzeServiceTest.java` (unit test replacement for former IT)
1035+
* Removed: `AnalyzeServiceIntegrationTests.java`
1036+
* Updated documentation: `citations.md`
1037+
1038+
---
1039+
1040+
### **Verification**
1041+
1042+
* Successfully built via `mvn clean test`
1043+
* All controller endpoints verified with MockMvc tests
1044+
* Branch/error-path coverage for forbidden and not-found scenarios
1045+
* Ensured no external binary dependency required for CI
1046+
1047+
---
1048+
1049+
### **Attribution Statement**
1050+
1051+
> Portions of this commit or configuration were generated with assistance from OpenAI ChatGPT (GPT-5) on October 22, 2025. All AI-generated content was reviewed, verified, and finalized by the development team.
1052+
1053+
---
Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package dev.coms4156.project.metadetect.controller;
22

33
import dev.coms4156.project.metadetect.dto.Dtos;
4+
import dev.coms4156.project.metadetect.model.Image;
45
import dev.coms4156.project.metadetect.service.ImageService;
6+
import dev.coms4156.project.metadetect.service.UserService;
7+
import dev.coms4156.project.metadetect.service.errors.ForbiddenException;
8+
import dev.coms4156.project.metadetect.service.errors.NotFoundException;
59
import java.util.List;
10+
import java.util.UUID;
11+
import java.util.stream.Collectors;
12+
import org.springframework.http.HttpStatus;
613
import org.springframework.http.ResponseEntity;
714
import org.springframework.web.bind.annotation.DeleteMapping;
15+
import org.springframework.web.bind.annotation.ExceptionHandler;
816
import org.springframework.web.bind.annotation.GetMapping;
917
import org.springframework.web.bind.annotation.PathVariable;
1018
import org.springframework.web.bind.annotation.PutMapping;
@@ -14,41 +22,112 @@
1422
import org.springframework.web.bind.annotation.RestController;
1523

1624
/**
17-
* CRUD around user images (records, not binary).
25+
* CRUD for image metadata (not binary files). Enforces ownership via ImageService.
1826
*/
1927
@RestController
2028
@RequestMapping("/api/images")
2129
public class ImageController {
2230

2331
private final ImageService imageService;
32+
private final UserService userService;
2433

25-
public ImageController(ImageService imageService) {
34+
public ImageController(ImageService imageService, UserService userService) {
2635
this.imageService = imageService;
36+
this.userService = userService;
2737
}
2838

39+
/** GET /api/images?page=0&size=20 — list current user's images (simple paging). */
2940
@GetMapping
30-
public ResponseEntity<List<Dtos.ImageDto>> list(@RequestParam(defaultValue = "0") int page,
31-
@RequestParam(defaultValue = "20") int size) {
32-
// TODO: paginate by owner user
33-
return ResponseEntity.ok(List.of());
41+
public ResponseEntity<List<Dtos.ImageDto>> list(
42+
@RequestParam(defaultValue = "0") int page,
43+
@RequestParam(defaultValue = "20") int size) {
44+
45+
if (page < 0 || size <= 0) {
46+
return ResponseEntity.badRequest().build();
47+
}
48+
49+
UUID userId = userService.getCurrentUserIdOrThrow();
50+
List<Image> all = imageService.listByOwner(userId);
51+
52+
int from = Math.min(page * size, all.size());
53+
int to = Math.min(from + size, all.size());
54+
55+
List<Dtos.ImageDto> items = all.subList(from, to)
56+
.stream()
57+
.map(this::toDto)
58+
.collect(Collectors.toList());
59+
60+
return ResponseEntity.ok(items);
3461
}
3562

63+
/** GET /api/images/{id} — fetch a single image (ownership enforced in service). */
3664
@GetMapping("/{id}")
3765
public ResponseEntity<Dtos.ImageDto> get(@PathVariable String id) {
38-
// TODO: fetch by id + ownership
39-
return ResponseEntity.ok(new Dtos.ImageDto(id, "original.jpg", "owner-uid", null));
66+
UUID userId = userService.getCurrentUserIdOrThrow();
67+
UUID imageId = UUID.fromString(id);
68+
69+
Image img = imageService.getById(userId, imageId);
70+
return ResponseEntity.ok(toDto(img));
4071
}
4172

73+
/** PUT /api/images/{id} — update mutable fields. */
4274
@PutMapping("/{id}")
43-
public ResponseEntity<Dtos.ImageDto> update(@PathVariable String id,
44-
@RequestBody Dtos.UpdateImageRequest req) {
45-
// TODO: update mutable fields (labels, note)
46-
return ResponseEntity.ok(new Dtos.ImageDto(id, "original.jpg", "owner-uid", null));
75+
public ResponseEntity<Dtos.ImageDto> update(
76+
@PathVariable String id,
77+
@RequestBody Dtos.UpdateImageRequest req) {
78+
79+
UUID userId = userService.getCurrentUserIdOrThrow();
80+
UUID imageId = UUID.fromString(id);
81+
82+
// Convert List<String> -> String[] for the service layer
83+
String[] labels = (req.labels() == null) ? null : req.labels().toArray(new String[0]);
84+
85+
Image updated = imageService.update(
86+
userId,
87+
imageId,
88+
null, // filename (not changed via this endpoint)
89+
null, // storagePath (not changed via this endpoint)
90+
labels, // labels
91+
req.note() // note
92+
);
93+
94+
return ResponseEntity.ok(toDto(updated));
4795
}
4896

97+
98+
/** DELETE /api/images/{id} — hard delete metadata record. */
4999
@DeleteMapping("/{id}")
50100
public ResponseEntity<Void> delete(@PathVariable String id) {
51-
// TODO: soft/hard delete image + reports
101+
UUID userId = userService.getCurrentUserIdOrThrow();
102+
UUID imageId = UUID.fromString(id);
103+
104+
imageService.delete(userId, imageId);
52105
return ResponseEntity.noContent().build();
53106
}
107+
108+
// ---- Exception → HTTP mapping (controller-scoped) ----
109+
110+
@ExceptionHandler(NotFoundException.class)
111+
public ResponseEntity<String> handleNotFound(NotFoundException ex) {
112+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
113+
}
114+
115+
@ExceptionHandler(ForbiddenException.class)
116+
public ResponseEntity<String> handleForbidden(ForbiddenException ex) {
117+
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ex.getMessage());
118+
}
119+
120+
// ---- Mapping helper ----
121+
// Matches Dtos.ImageDto(id, filename, ownerId, uploadedAt)
122+
private Dtos.ImageDto toDto(Image img) {
123+
return new Dtos.ImageDto(
124+
img.getId().toString(),
125+
img.getFilename(),
126+
img.getUserId().toString(),
127+
img.getUploadedAt(),
128+
(img.getLabels() == null ? List.of() : List.of(img.getLabels())),
129+
img.getNote()
130+
);
131+
}
132+
54133
}

src/main/java/dev/coms4156/project/metadetect/dto/Dtos.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,14 @@ public record CompareResponse(String imageIdA, String imageIdB, Double similarit
7171
/**
7272
* Simplified view of an image record.
7373
*/
74-
public record ImageDto(String id, String filename, String ownerUserId, Instant uploadedAt) {
75-
}
74+
public record ImageDto(
75+
String id,
76+
String filename,
77+
String ownerUserId,
78+
Instant uploadedAt,
79+
List<String> labels,
80+
String note
81+
) {}
7682

7783
/**
7884
* Request body for updating image metadata (labels, note).

src/test/java/dev/coms4156/project/metadetect/AnalyzeServiceIntegrationTests.java

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)