Skip to content

Commit b3031c4

Browse files
iii123iiiclaude
andcommitted
Add pagination to admin user list and validation annotations to all DTOs
- Implement server-side pagination in AdminService.getAllUsers() with search and filter support - Update AdminController to accept page, pageSize, search, and filter query parameters - Return Spring Data Page<> with total pages and paginated content - Add validation annotations to 20+ request DTOs covering: - String length and format validation (@notblank, @SiZe, @pattern) - Numeric range validation (@min, @max) - Collection validation (@notempty) - Nested validation (@Valid) - Change default admin credentials to email: admin, password: admin (configurable via env vars) 96 of 97 backend tests pass (1 contextLoads test requires DB setup) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 32725f4 commit b3031c4

24 files changed

Lines changed: 341 additions & 45 deletions

backend/src/main/java/com/crystalpdf/backend/controller/AdminController.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1111
import org.springframework.web.bind.annotation.*;
1212

13-
import java.util.List;
13+
import org.springframework.data.domain.Page;
1414
import java.util.Map;
1515

1616
@RestController
@@ -28,9 +28,14 @@ private void requireAdmin(User user) {
2828
}
2929

3030
@GetMapping("/users")
31-
public ResponseEntity<List<AdminUserResponse>> listUsers(@AuthenticationPrincipal User user) {
31+
public ResponseEntity<Page<AdminUserResponse>> listUsers(
32+
@RequestParam(defaultValue = "0") int page,
33+
@RequestParam(defaultValue = "20") int pageSize,
34+
@RequestParam(required = false) String search,
35+
@RequestParam(required = false) String filter,
36+
@AuthenticationPrincipal User user) {
3237
requireAdmin(user);
33-
return ResponseEntity.ok(adminService.getAllUsers());
38+
return ResponseEntity.ok(adminService.getAllUsers(page, pageSize, search, filter));
3439
}
3540

3641
@PatchMapping("/users/{id}/storage-limit")
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
package com.crystalpdf.backend.dto;
22

3-
public record CompressRequest(String level, String sourcePassword) {}
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Pattern;
5+
6+
public record CompressRequest(
7+
@NotBlank(message = "Compression level is required")
8+
@Pattern(regexp = "low|medium|high", message = "Level must be 'low', 'medium', or 'high'")
9+
String level,
10+
11+
String sourcePassword
12+
) {}
Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,46 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.Valid;
4+
import jakarta.validation.constraints.Min;
5+
import jakarta.validation.constraints.NotNull;
6+
37
import java.util.List;
48

59
public record CropRequest(
610
String sourcePassword,
711
// Legacy: uniform margins applied to all pages (or pages list)
12+
@Min(value = 0, message = "Top margin must be non-negative")
813
Float marginTop,
14+
15+
@Min(value = 0, message = "Right margin must be non-negative")
916
Float marginRight,
17+
18+
@Min(value = 0, message = "Bottom margin must be non-negative")
1019
Float marginBottom,
20+
21+
@Min(value = 0, message = "Left margin must be non-negative")
1122
Float marginLeft,
23+
1224
List<Integer> pages,
25+
1326
// Per-page: each entry specifies margins for a specific page
27+
@Valid
1428
List<PageCropEntry> pageCrops
1529
) {
16-
public record PageCropEntry(int page, float marginTop, float marginRight, float marginBottom, float marginLeft) {}
30+
public record PageCropEntry(
31+
@NotNull(message = "Page number is required")
32+
int page,
33+
34+
@Min(value = 0, message = "Top margin must be non-negative")
35+
float marginTop,
36+
37+
@Min(value = 0, message = "Right margin must be non-negative")
38+
float marginRight,
39+
40+
@Min(value = 0, message = "Bottom margin must be non-negative")
41+
float marginBottom,
42+
43+
@Min(value = 0, message = "Left margin must be non-negative")
44+
float marginLeft
45+
) {}
1746
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
package com.crystalpdf.backend.dto;
22

3-
public record DeleteAccountRequest(String password) {}
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
public record DeleteAccountRequest(
6+
@NotBlank(message = "Password is required")
7+
String password
8+
) {}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.constraints.NotEmpty;
4+
35
import java.util.List;
46

5-
public record DeletePagesRequest(List<Integer> pages, String sourcePassword) {}
7+
public record DeletePagesRequest(
8+
@NotEmpty(message = "Pages list is required")
9+
List<Integer> pages,
10+
11+
String sourcePassword
12+
) {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Pattern;
5+
36
public record ExtractImagesRequest(
47
String sourcePassword,
8+
9+
@NotBlank(message = "Image format is required")
10+
@Pattern(regexp = "png|jpg|jpeg", message = "Format must be 'png', 'jpg', or 'jpeg'")
511
String format
612
) {}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.constraints.NotEmpty;
4+
35
import java.util.List;
46

5-
public record ExtractSelectionRequest(List<Integer> pages, String sourcePassword) {}
7+
public record ExtractSelectionRequest(
8+
@NotEmpty(message = "Pages list is required")
9+
List<Integer> pages,
10+
11+
String sourcePassword
12+
) {}

backend/src/main/java/com/crystalpdf/backend/dto/FlattenAnnotationsRequest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.Valid;
4+
import jakarta.validation.constraints.Min;
5+
import jakarta.validation.constraints.NotBlank;
6+
import jakarta.validation.constraints.NotEmpty;
7+
import jakarta.validation.constraints.NotNull;
8+
39
import java.util.List;
410
import java.util.Map;
511

612
public record FlattenAnnotationsRequest(
13+
@NotEmpty(message = "Pages map is required")
14+
@Valid
715
Map<Integer, FlattenAnnotationsRequest.PageData> pages,
16+
17+
@Min(value = 0, message = "Scale must be positive")
818
double scale,
19+
920
String sourcePassword
1021
) {
1122
public record PageData(
@@ -14,20 +25,42 @@ public record PageData(
1425
) {}
1526

1627
public record StrokeData(
28+
@NotBlank(message = "Stroke type is required")
1729
String type,
30+
31+
@NotBlank(message = "Stroke color is required")
1832
String color,
33+
34+
@Min(value = 0, message = "Width must be non-negative")
1935
double width,
36+
37+
@Min(value = 0, message = "Opacity must be non-negative")
2038
double opacity,
39+
40+
@NotEmpty(message = "Stroke points are required")
2141
List<List<Double>> points
2242
) {}
2343

2444
public record TextData(
45+
@NotBlank(message = "Text ID is required")
2546
String id,
47+
48+
@Min(value = 0, message = "X must be non-negative")
2649
double x,
50+
51+
@Min(value = 0, message = "Y must be non-negative")
2752
double y,
53+
54+
@Min(value = 0, message = "Width must be non-negative")
2855
double width,
56+
57+
@Min(value = 0, message = "Font size must be positive")
2958
double fontSize,
59+
60+
@NotBlank(message = "Text color is required")
3061
String color,
62+
63+
@NotBlank(message = "Text content is required")
3164
String text
3265
) {}
3366
}

backend/src/main/java/com/crystalpdf/backend/dto/HeaderFooterRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.constraints.Min;
4+
import jakarta.validation.constraints.NotNull;
5+
36
public record HeaderFooterRequest(
47
String sourcePassword,
58
String headerLeft,
@@ -8,5 +11,8 @@ public record HeaderFooterRequest(
811
String footerLeft,
912
String footerCenter,
1013
String footerRight,
14+
15+
@NotNull(message = "Font size is required")
16+
@Min(value = 6, message = "Font size must be at least 6")
1117
Float fontSize
1218
) {}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package com.crystalpdf.backend.dto;
22

3+
import jakarta.validation.constraints.NotEmpty;
4+
35
import java.util.List;
46

5-
public record MergeRequest(List<Long> otherDocumentIds, String sourcePassword) {}
7+
public record MergeRequest(
8+
@NotEmpty(message = "At least one document to merge is required")
9+
List<Long> otherDocumentIds,
10+
11+
String sourcePassword
12+
) {}

0 commit comments

Comments
 (0)