|
12 | 12 | import org.springframework.web.bind.annotation.RequestParam; |
13 | 13 | import org.springframework.web.bind.annotation.RestController; |
14 | 14 |
|
15 | | - |
16 | 15 | /** |
17 | | - * REST controller for image analysis operations. |
18 | | - * Endpoints: |
19 | | - * - POST /api/analyze/{imageId} -> start an analysis (202 Accepted) |
20 | | - * - GET /api/analyze/{analysisId} -> status/score (polling) |
21 | | - * - GET /api/analyze/{analysisId}/manifest -> manifest JSON |
22 | | - * - GET /api/analyze/compare -> stubbed compare (left & right image IDs) |
23 | | - * Notes: |
24 | | - * - Ownership and RLS checks are enforced in AnalyzeService/ImageService. |
25 | | - * - Exceptions (Forbidden/NotFound/etc.) |
26 | | - * are expected to be mapped by global @RestControllerAdvice. |
| 16 | + * HTTP API for starting and querying image analyses. |
| 17 | + * Responsibilities |
| 18 | + * - Accepts requests to start analysis on an already-uploaded image. |
| 19 | + * - Exposes polling endpoints to retrieve analysis status, confidence scores, and stored manifests. |
| 20 | + * - Delegates ownership/RLS checks and business logic to {@link AnalyzeService}. |
| 21 | + * Contract |
| 22 | + * - POST /api/analyze/{imageId} returns 202 Accepted with an analysis identifier. |
| 23 | + * - GET /api/analyze/{analysisId} returns current status and (optionally) a confidence score. |
| 24 | + * - GET /api/analyze/{analysisId}/manifest returns a captured C2PA manifest (if available). |
| 25 | + * - GET /api/analyze/compare?left=...&right=... |
| 26 | + * returns a lightweight comparison (Iteration 1 stub). |
| 27 | + * Error Handling |
| 28 | + * - Authorization, ownership, and not-found conditions are surfaced as exceptions from the service |
| 29 | + * layer and mapped by a global {@code @RestControllerAdvice}. |
| 30 | + * Thread-safety |
| 31 | + * - Controller is stateless; relies on Spring-managed, thread-safe collaborators. |
27 | 32 | */ |
28 | 33 | @RestController |
29 | 34 | @RequestMapping("/api/analyze") |
30 | 35 | public class AnalyzeController { |
31 | 36 |
|
32 | 37 | private final AnalyzeService analyzeService; |
33 | 38 |
|
| 39 | + /** |
| 40 | + * Constructs the controller with required collaborators. |
| 41 | + * |
| 42 | + * @param analyzeService domain service coordinating analysis lifecycle and access checks |
| 43 | + */ |
34 | 44 | public AnalyzeController(AnalyzeService analyzeService) { |
35 | 45 | this.analyzeService = analyzeService; |
36 | 46 | } |
37 | 47 |
|
38 | 48 | /** |
39 | | - * Starts analysis for an existing image that is already uploaded to Supabase Storage. |
40 | | - * Returns 202 with a body containing the new analysisId. |
| 49 | + * Starts analysis for an existing image that is already persisted |
| 50 | + * and stored (e.g., in Supabase Storage). |
| 51 | + * The service enqueues or triggers downstream processing and |
| 52 | + * returns an identifier used for polling. |
| 53 | + * Response semantics |
| 54 | + * - Returns HTTP 202 Accepted to indicate asynchronous processing has begun. |
| 55 | + * |
| 56 | + * @param imageId unique identifier of the previously uploaded image |
| 57 | + * @return 202 Accepted with a body containing |
| 58 | + * {@link Dtos.AnalyzeStartResponse} and a new analysisId |
| 59 | + * @throws org.springframework.web.server.ResponseStatusException if the image does not exist or |
| 60 | + * the caller is not authorized to analyze it (propagated from the service layer) |
41 | 61 | */ |
42 | 62 | @PostMapping("/{imageId}") |
43 | 63 | public ResponseEntity<Dtos.AnalyzeStartResponse> submit(@PathVariable UUID imageId) { |
| 64 | + // Delegate to service: performs ownership checks, persists analysis row, and schedules work. |
44 | 65 | Dtos.AnalyzeStartResponse resp = analyzeService.submitAnalysis(imageId); |
45 | | - // As per ticket: 202 Accepted with { analysisId } |
| 66 | + |
| 67 | + // Per API contract, asynchronous start returns 202 Accepted rather than 200 OK. |
46 | 68 | return ResponseEntity.status(HttpStatus.ACCEPTED).body(resp); |
47 | 69 | } |
48 | 70 |
|
49 | 71 | /** |
50 | | - * Returns current status (PENDING/COMPLETED/FAILED) and an optional score (stubbed). |
51 | | - * Suitable for client-side polling. |
| 72 | + * Retrieves the current analysis status and (optionally) a confidence score suitable for polling. |
| 73 | + * Typical states include PENDING, COMPLETED, and FAILED. |
| 74 | + * |
| 75 | + * @param analysisId unique identifier returned by {@link #submit(UUID)} |
| 76 | + * @return 200 OK with {@link Dtos.AnalyzeConfidenceResponse} |
| 77 | + * @throws org.springframework.web.server.ResponseStatusException |
| 78 | + * if the analysis does not exist or the |
| 79 | + * caller lacks access (propagated from the service layer) |
52 | 80 | */ |
53 | 81 | @GetMapping("/{analysisId}") |
54 | 82 | public ResponseEntity<Dtos.AnalyzeConfidenceResponse> getStatus(@PathVariable UUID analysisId) { |
| 83 | + // Service encapsulates lookup and authorization; controller simply returns the DTO. |
55 | 84 | Dtos.AnalyzeConfidenceResponse resp = analyzeService.getConfidence(analysisId); |
56 | 85 | return ResponseEntity.ok(resp); |
57 | 86 | } |
58 | 87 |
|
59 | 88 | /** |
60 | | - * Returns the stored C2PA manifest JSON for a completed analysis. |
| 89 | + * Returns the stored C2PA manifest for a completed analysis, when available. |
| 90 | + * Clients should check analysis status before calling this endpoint |
| 91 | + * to avoid unnecessary requests. |
| 92 | + * |
| 93 | + * @param analysisId unique identifier of the analysis |
| 94 | + * @return 200 OK with {@link Dtos.AnalysisManifestResponse}; |
| 95 | + * may be 404/403 via advice if not accessible |
61 | 96 | */ |
62 | 97 | @GetMapping("/{analysisId}/manifest") |
63 | 98 | public ResponseEntity<Dtos.AnalysisManifestResponse> getManifest(@PathVariable UUID analysisId) { |
| 99 | + // The service is responsible for ensuring the analysis is complete and manifest exists or |
| 100 | + // raising a mapped exception if it does not. |
64 | 101 | Dtos.AnalysisManifestResponse resp = analyzeService.getMetadata(analysisId); |
65 | 102 | return ResponseEntity.ok(resp); |
66 | 103 | } |
67 | 104 |
|
68 | 105 | /** |
69 | | - * Stubbed comparison endpoint (Iteration 1). |
70 | | - * Ownership of both images is validated by the service layer. |
71 | | - * Example: /api/analyze/compare?left={imageId}&right={imageId} |
| 106 | + * Lightweight comparison endpoint (Iteration 1 stub) that |
| 107 | + * compares two images owned by the caller. |
| 108 | + * The service validates ownership of both resources and returns a basic comparison DTO. |
| 109 | + * Example |
| 110 | + * GET /api/analyze/compare?left={imageId}&right={imageId} |
| 111 | + * |
| 112 | + * @param leftImageId identifier of the left image to compare |
| 113 | + * @param rightImageId identifier of the right image to compare |
| 114 | + * @return 200 OK with {@link Dtos.AnalyzeCompareResponse} |
| 115 | + * @throws org.springframework.web.server.ResponseStatusException |
| 116 | + * if either image is missing or unauthorized |
72 | 117 | */ |
73 | 118 | @GetMapping("/compare") |
74 | 119 | public ResponseEntity<Dtos.AnalyzeCompareResponse> compare( |
75 | 120 | @RequestParam("left") UUID leftImageId, |
76 | 121 | @RequestParam("right") UUID rightImageId) { |
77 | 122 |
|
| 123 | + // Delegate comparison to the domain service; controller remains a thin transport layer. |
78 | 124 | Dtos.AnalyzeCompareResponse resp = analyzeService.compare(leftImageId, rightImageId); |
79 | 125 | return ResponseEntity.ok(resp); |
80 | 126 | } |
|
0 commit comments