@rashidee/co2 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,722 @@
1
+ # REST API Patterns — Controllers, DTOs, and Conventions
2
+
3
+ This reference describes the REST API-specific patterns for the Spring Boot application.
4
+ Include this content in Sections 7-9 of the generated specification.
5
+
6
+ ---
7
+
8
+ ## Architecture Overview
9
+
10
+ The REST API uses a layered composition model:
11
+
12
+ ```
13
+ Request → SecurityFilter → CorrelationIdFilter → @RestController
14
+ → Service (queries DB via Repository)
15
+ → Response DTO (mapped via MapStruct)
16
+ → JSON Response (serialized by Jackson)
17
+ ```
18
+
19
+ ---
20
+
21
+ ## REST Controller Pattern
22
+
23
+ Every REST controller follows this structure:
24
+
25
+ ```java
26
+ package {{BASE_PACKAGE}}.{{module}}.internal;
27
+
28
+ import {{BASE_PACKAGE}}.{{module}}.{{ModuleName}}Service;
29
+ import {{BASE_PACKAGE}}.{{module}}.{{ModuleName}}DTO;
30
+ import io.swagger.v3.oas.annotations.Operation;
31
+ import io.swagger.v3.oas.annotations.Parameter;
32
+ import io.swagger.v3.oas.annotations.responses.ApiResponse;
33
+ import io.swagger.v3.oas.annotations.tags.Tag;
34
+ import jakarta.validation.Valid;
35
+ import lombok.RequiredArgsConstructor;
36
+ import org.springframework.data.domain.Page;
37
+ import org.springframework.data.domain.Pageable;
38
+ import org.springframework.data.web.PageableDefault;
39
+ import org.springframework.http.ResponseEntity;
40
+ import org.springframework.security.access.prepost.PreAuthorize;
41
+ import org.springframework.web.bind.annotation.*;
42
+ import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
43
+ import java.net.URI;
44
+
45
+ @RestController
46
+ @RequestMapping("/api/v1/{{resource-plural}}")
47
+ @RequiredArgsConstructor
48
+ @Tag(name = "{{ModuleName}}", description = "{{ModuleName}} management operations")
49
+ class {{ModuleName}}Controller {
50
+
51
+ private final {{ModuleName}}Service service;
52
+
53
+ @GetMapping
54
+ @Operation(summary = "List all {{resource-plural}} with pagination")
55
+ public ResponseEntity<Page<{{ModuleName}}DTO>> list(
56
+ @PageableDefault(size = 20, sort = "createdAt") Pageable pageable,
57
+ @RequestParam(required = false) String search) {
58
+ Page<{{ModuleName}}DTO> results = service.findAll(search, pageable);
59
+ return ResponseEntity.ok(results);
60
+ }
61
+
62
+ @GetMapping("/{id}")
63
+ @Operation(summary = "Get {{resource}} by ID")
64
+ @ApiResponse(responseCode = "200", description = "{{ModuleName}} found")
65
+ @ApiResponse(responseCode = "404", description = "{{ModuleName}} not found")
66
+ public ResponseEntity<{{ModuleName}}DTO> getById(@PathVariable String id) {
67
+ {{ModuleName}}DTO result = service.findById(id);
68
+ return ResponseEntity.ok(result);
69
+ }
70
+
71
+ @PostMapping
72
+ @Operation(summary = "Create a new {{resource}}")
73
+ @ApiResponse(responseCode = "201", description = "{{ModuleName}} created")
74
+ @ApiResponse(responseCode = "400", description = "Validation failed")
75
+ public ResponseEntity<{{ModuleName}}DTO> create(
76
+ @Valid @RequestBody Create{{ModuleName}}Request request) {
77
+ {{ModuleName}}DTO created = service.create(request);
78
+ URI location = ServletUriComponentsBuilder.fromCurrentRequest()
79
+ .path("/{id}")
80
+ .buildAndExpand(created.id())
81
+ .toUri();
82
+ return ResponseEntity.created(location).body(created);
83
+ }
84
+
85
+ @PutMapping("/{id}")
86
+ @Operation(summary = "Update an existing {{resource}}")
87
+ @ApiResponse(responseCode = "200", description = "{{ModuleName}} updated")
88
+ @ApiResponse(responseCode = "404", description = "{{ModuleName}} not found")
89
+ public ResponseEntity<{{ModuleName}}DTO> update(
90
+ @PathVariable String id,
91
+ @Valid @RequestBody Update{{ModuleName}}Request request) {
92
+ {{ModuleName}}DTO updated = service.update(id, request);
93
+ return ResponseEntity.ok(updated);
94
+ }
95
+
96
+ @DeleteMapping("/{id}")
97
+ @Operation(summary = "Delete a {{resource}}")
98
+ @ApiResponse(responseCode = "204", description = "{{ModuleName}} deleted")
99
+ @ApiResponse(responseCode = "404", description = "{{ModuleName}} not found")
100
+ public ResponseEntity<Void> delete(@PathVariable String id) {
101
+ service.delete(id);
102
+ return ResponseEntity.noContent().build();
103
+ }
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Request/Response DTO Pattern
110
+
111
+ ### Response DTO (Java Record)
112
+
113
+ Response DTOs are immutable Java records that represent the API contract for responses:
114
+
115
+ ```java
116
+ package {{BASE_PACKAGE}}.{{module}};
117
+
118
+ import java.time.Instant;
119
+
120
+ public record {{ModuleName}}DTO(
121
+ String id,
122
+ String name,
123
+ String code,
124
+ String description,
125
+ String status,
126
+ Instant createdAt,
127
+ Instant updatedAt,
128
+ String createdBy,
129
+ String updatedBy
130
+ ) {}
131
+ ```
132
+
133
+ ### Request DTO with Bean Validation
134
+
135
+ Request DTOs use Java records with Bean Validation annotations:
136
+
137
+ ```java
138
+ package {{BASE_PACKAGE}}.{{module}};
139
+
140
+ import jakarta.validation.constraints.NotBlank;
141
+ import jakarta.validation.constraints.NotNull;
142
+ import jakarta.validation.constraints.Positive;
143
+ import jakarta.validation.constraints.Size;
144
+
145
+ public record Create{{ModuleName}}Request(
146
+ @NotBlank(message = "Name is required")
147
+ @Size(max = 255, message = "Name must not exceed 255 characters")
148
+ String name,
149
+
150
+ @NotBlank(message = "Code is required")
151
+ @Size(max = 50, message = "Code must not exceed 50 characters")
152
+ String code,
153
+
154
+ @Size(max = 1000, message = "Description must not exceed 1000 characters")
155
+ String description
156
+ ) {}
157
+ ```
158
+
159
+ ```java
160
+ package {{BASE_PACKAGE}}.{{module}};
161
+
162
+ import jakarta.validation.constraints.Size;
163
+
164
+ public record Update{{ModuleName}}Request(
165
+ @Size(max = 255, message = "Name must not exceed 255 characters")
166
+ String name,
167
+
168
+ @Size(max = 1000, message = "Description must not exceed 1000 characters")
169
+ String description
170
+ ) {}
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Standardized Error Response
176
+
177
+ ### Error Envelope
178
+
179
+ All error responses use a consistent JSON structure:
180
+
181
+ ```java
182
+ package {{BASE_PACKAGE}}.shared.exception;
183
+
184
+ import java.time.Instant;
185
+ import java.util.List;
186
+
187
+ public record ApiError(
188
+ Instant timestamp,
189
+ int status,
190
+ String error,
191
+ String message,
192
+ String path,
193
+ List<FieldError> fieldErrors
194
+ ) {
195
+ public record FieldError(String field, String message) {}
196
+
197
+ public static ApiError of(int status, String error, String message, String path) {
198
+ return new ApiError(Instant.now(), status, error, message, path, null);
199
+ }
200
+
201
+ public static ApiError withFieldErrors(int status, String error, String message,
202
+ String path, List<FieldError> fieldErrors) {
203
+ return new ApiError(Instant.now(), status, error, message, path, fieldErrors);
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### Example Error Responses
209
+
210
+ **404 Not Found:**
211
+ ```json
212
+ {
213
+ "timestamp": "2026-03-13T10:15:30Z",
214
+ "status": 404,
215
+ "error": "Not Found",
216
+ "message": "Job Demand not found with id: 12345",
217
+ "path": "/api/v1/job-demands/12345",
218
+ "fieldErrors": null
219
+ }
220
+ ```
221
+
222
+ **400 Validation Error:**
223
+ ```json
224
+ {
225
+ "timestamp": "2026-03-13T10:15:30Z",
226
+ "status": 400,
227
+ "error": "Validation Failed",
228
+ "message": "Request body contains invalid fields",
229
+ "path": "/api/v1/job-demands",
230
+ "fieldErrors": [
231
+ { "field": "name", "message": "Name is required" },
232
+ { "field": "code", "message": "Code must not exceed 50 characters" }
233
+ ]
234
+ }
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Global Exception Handler
240
+
241
+ ```java
242
+ package {{BASE_PACKAGE}}.shared.exception;
243
+
244
+ import jakarta.servlet.http.HttpServletRequest;
245
+ import lombok.extern.slf4j.Slf4j;
246
+ import org.springframework.http.ResponseEntity;
247
+ import org.springframework.web.bind.MethodArgumentNotValidException;
248
+ import org.springframework.web.bind.annotation.ExceptionHandler;
249
+ import org.springframework.web.bind.annotation.RestControllerAdvice;
250
+
251
+ import java.util.List;
252
+
253
+ @RestControllerAdvice
254
+ @Slf4j
255
+ public class GlobalExceptionHandler {
256
+
257
+ @ExceptionHandler(ResourceNotFoundException.class)
258
+ public ResponseEntity<ApiError> handleNotFound(
259
+ ResourceNotFoundException ex, HttpServletRequest request) {
260
+ ApiError error = ApiError.of(404, "Not Found",
261
+ ex.getMessage(), request.getRequestURI());
262
+ return ResponseEntity.status(404).body(error);
263
+ }
264
+
265
+ @ExceptionHandler(MethodArgumentNotValidException.class)
266
+ public ResponseEntity<ApiError> handleValidation(
267
+ MethodArgumentNotValidException ex, HttpServletRequest request) {
268
+ List<ApiError.FieldError> fieldErrors = ex.getBindingResult()
269
+ .getFieldErrors().stream()
270
+ .map(fe -> new ApiError.FieldError(fe.getField(), fe.getDefaultMessage()))
271
+ .toList();
272
+
273
+ ApiError error = ApiError.withFieldErrors(400, "Validation Failed",
274
+ "Request body contains invalid fields",
275
+ request.getRequestURI(), fieldErrors);
276
+ return ResponseEntity.badRequest().body(error);
277
+ }
278
+
279
+ @ExceptionHandler(ConflictException.class)
280
+ public ResponseEntity<ApiError> handleConflict(
281
+ ConflictException ex, HttpServletRequest request) {
282
+ ApiError error = ApiError.of(409, "Conflict",
283
+ ex.getMessage(), request.getRequestURI());
284
+ return ResponseEntity.status(409).body(error);
285
+ }
286
+
287
+ @ExceptionHandler(Exception.class)
288
+ public ResponseEntity<ApiError> handleGeneral(
289
+ Exception ex, HttpServletRequest request) {
290
+ log.error("Unhandled exception at {}", request.getRequestURI(), ex);
291
+ ApiError error = ApiError.of(500, "Internal Server Error",
292
+ "An unexpected error occurred", request.getRequestURI());
293
+ return ResponseEntity.status(500).body(error);
294
+ }
295
+ }
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Base Exception Classes
301
+
302
+ ```java
303
+ package {{BASE_PACKAGE}}.shared.exception;
304
+
305
+ public class ResourceNotFoundException extends RuntimeException {
306
+ public ResourceNotFoundException(String message) {
307
+ super(message);
308
+ }
309
+
310
+ public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) {
311
+ super(String.format("%s not found with %s: %s", resourceName, fieldName, fieldValue));
312
+ }
313
+ }
314
+ ```
315
+
316
+ ```java
317
+ package {{BASE_PACKAGE}}.shared.exception;
318
+
319
+ public class ConflictException extends RuntimeException {
320
+ public ConflictException(String message) {
321
+ super(message);
322
+ }
323
+ }
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Pagination Response Structure
329
+
330
+ Spring Data `Page` is serialized by Jackson automatically. The response structure:
331
+
332
+ ```json
333
+ {
334
+ "content": [
335
+ { "id": "1", "name": "Widget A", "code": "WA01" },
336
+ { "id": "2", "name": "Widget B", "code": "WB01" }
337
+ ],
338
+ "pageable": {
339
+ "pageNumber": 0,
340
+ "pageSize": 20,
341
+ "sort": { "sorted": true, "unsorted": false, "empty": false },
342
+ "offset": 0,
343
+ "paged": true,
344
+ "unpaged": false
345
+ },
346
+ "totalElements": 142,
347
+ "totalPages": 8,
348
+ "last": false,
349
+ "first": true,
350
+ "size": 20,
351
+ "number": 0,
352
+ "sort": { "sorted": true, "unsorted": false, "empty": false },
353
+ "numberOfElements": 20,
354
+ "empty": false
355
+ }
356
+ ```
357
+
358
+ ### Controller Pagination Pattern
359
+
360
+ ```java
361
+ @GetMapping
362
+ public ResponseEntity<Page<{{ModuleName}}DTO>> list(
363
+ @PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC)
364
+ Pageable pageable,
365
+ @RequestParam(required = false) String search) {
366
+ Page<{{ModuleName}}DTO> results = service.findAll(search, pageable);
367
+ return ResponseEntity.ok(results);
368
+ }
369
+ ```
370
+
371
+ ### Custom Page Serialization (Optional)
372
+
373
+ If you need a simplified page response:
374
+
375
+ ```java
376
+ package {{BASE_PACKAGE}}.shared.dto;
377
+
378
+ import org.springframework.data.domain.Page;
379
+ import java.util.List;
380
+
381
+ public record PageResponse<T>(
382
+ List<T> content,
383
+ int pageNumber,
384
+ int pageSize,
385
+ long totalElements,
386
+ int totalPages,
387
+ boolean first,
388
+ boolean last
389
+ ) {
390
+ public static <T> PageResponse<T> from(Page<T> page) {
391
+ return new PageResponse<>(
392
+ page.getContent(),
393
+ page.getNumber(),
394
+ page.getSize(),
395
+ page.getTotalElements(),
396
+ page.getTotalPages(),
397
+ page.isFirst(),
398
+ page.isLast()
399
+ );
400
+ }
401
+ }
402
+ ```
403
+
404
+ ---
405
+
406
+ ## Correlation ID Filter
407
+
408
+ ```java
409
+ package {{BASE_PACKAGE}}.shared.filter;
410
+
411
+ import jakarta.servlet.FilterChain;
412
+ import jakarta.servlet.ServletException;
413
+ import jakarta.servlet.http.HttpServletRequest;
414
+ import jakarta.servlet.http.HttpServletResponse;
415
+ import org.slf4j.MDC;
416
+ import org.springframework.stereotype.Component;
417
+ import org.springframework.web.filter.OncePerRequestFilter;
418
+ import java.io.IOException;
419
+ import java.util.Optional;
420
+ import java.util.UUID;
421
+
422
+ @Component
423
+ public class CorrelationIdFilter extends OncePerRequestFilter {
424
+
425
+ private static final String CORRELATION_HEADER = "X-Correlation-Id";
426
+ private static final String MDC_KEY = "correlationId";
427
+
428
+ @Override
429
+ protected void doFilterInternal(HttpServletRequest request,
430
+ HttpServletResponse response, FilterChain chain)
431
+ throws ServletException, IOException {
432
+
433
+ String correlationId = Optional
434
+ .ofNullable(request.getHeader(CORRELATION_HEADER))
435
+ .orElse(UUID.randomUUID().toString());
436
+
437
+ MDC.put(MDC_KEY, correlationId);
438
+ response.setHeader(CORRELATION_HEADER, correlationId);
439
+
440
+ try {
441
+ chain.doFilter(request, response);
442
+ } finally {
443
+ MDC.remove(MDC_KEY);
444
+ }
445
+ }
446
+ }
447
+ ```
448
+
449
+ ---
450
+
451
+ ## Jackson Configuration
452
+
453
+ ```java
454
+ package {{BASE_PACKAGE}}.config;
455
+
456
+ import com.fasterxml.jackson.databind.ObjectMapper;
457
+ import com.fasterxml.jackson.databind.SerializationFeature;
458
+ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
459
+ import org.springframework.context.annotation.Bean;
460
+ import org.springframework.context.annotation.Configuration;
461
+
462
+ @Configuration
463
+ public class JacksonConfig {
464
+
465
+ @Bean
466
+ public ObjectMapper objectMapper() {
467
+ ObjectMapper mapper = new ObjectMapper();
468
+ mapper.registerModule(new JavaTimeModule());
469
+ mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
470
+ mapper.setDefaultPropertyInclusion(
471
+ com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
472
+ return mapper;
473
+ }
474
+ }
475
+ ```
476
+
477
+ ---
478
+
479
+ ## CORS Configuration
480
+
481
+ ```java
482
+ package {{BASE_PACKAGE}}.config;
483
+
484
+ import org.springframework.beans.factory.annotation.Value;
485
+ import org.springframework.context.annotation.Bean;
486
+ import org.springframework.context.annotation.Configuration;
487
+ import org.springframework.web.cors.CorsConfiguration;
488
+ import org.springframework.web.cors.CorsConfigurationSource;
489
+ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
490
+ import java.util.List;
491
+
492
+ @Configuration
493
+ public class CorsConfig {
494
+
495
+ @Value("${app.cors.allowed-origins:http://localhost:3000}")
496
+ private List<String> allowedOrigins;
497
+
498
+ @Bean
499
+ public CorsConfigurationSource corsConfigurationSource() {
500
+ CorsConfiguration config = new CorsConfiguration();
501
+ config.setAllowedOrigins(allowedOrigins);
502
+ config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
503
+ config.setAllowedHeaders(List.of("*"));
504
+ config.setAllowCredentials(true);
505
+ config.setMaxAge(3600L);
506
+
507
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
508
+ source.registerCorsConfiguration("/api/**", config);
509
+ return source;
510
+ }
511
+ }
512
+ ```
513
+
514
+ ---
515
+
516
+ ## OpenAPI / Springdoc Configuration
517
+
518
+ ### Maven Dependency
519
+
520
+ ```xml
521
+ <dependency>
522
+ <groupId>org.springdoc</groupId>
523
+ <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
524
+ <version>2.8.0</version>
525
+ </dependency>
526
+ ```
527
+
528
+ ### Application Configuration
529
+
530
+ ```yaml
531
+ springdoc:
532
+ api-docs:
533
+ path: /api-docs
534
+ swagger-ui:
535
+ path: /swagger-ui.html
536
+ operations-sorter: method
537
+ tags-sorter: alpha
538
+ default-produces-media-type: application/json
539
+ default-consumes-media-type: application/json
540
+ ```
541
+
542
+ ### OpenAPI Global Config
543
+
544
+ ```java
545
+ package {{BASE_PACKAGE}}.config;
546
+
547
+ import io.swagger.v3.oas.models.OpenAPI;
548
+ import io.swagger.v3.oas.models.info.Info;
549
+ import io.swagger.v3.oas.models.info.Contact;
550
+ import io.swagger.v3.oas.models.servers.Server;
551
+ import org.springframework.beans.factory.annotation.Value;
552
+ import org.springframework.context.annotation.Bean;
553
+ import org.springframework.context.annotation.Configuration;
554
+ import java.util.List;
555
+
556
+ @Configuration
557
+ public class OpenApiConfig {
558
+
559
+ @Value("${spring.application.name}")
560
+ private String applicationName;
561
+
562
+ @Bean
563
+ public OpenAPI customOpenAPI() {
564
+ return new OpenAPI()
565
+ .info(new Info()
566
+ .title(applicationName + " API")
567
+ .version("v1")
568
+ .description("REST API for " + applicationName)
569
+ .contact(new Contact()
570
+ .name("Bestinet")
571
+ .email("support@bestinet.com")))
572
+ .servers(List.of(
573
+ new Server().url("http://localhost:8080").description("Local development")
574
+ ));
575
+ }
576
+ }
577
+ ```
578
+
579
+ ### Controller Annotations
580
+
581
+ ```java
582
+ @RestController
583
+ @RequestMapping("/api/v1/job-demands")
584
+ @Tag(name = "Job Demands", description = "Job demand management operations")
585
+ class JobDemandController {
586
+
587
+ @Operation(
588
+ summary = "Search job demands",
589
+ description = "Search job demands with pagination and optional filters",
590
+ responses = {
591
+ @ApiResponse(responseCode = "200", description = "Job demands retrieved"),
592
+ @ApiResponse(responseCode = "400", description = "Invalid search parameters")
593
+ }
594
+ )
595
+ @GetMapping
596
+ public ResponseEntity<Page<JobDemandDTO>> search(
597
+ @Parameter(description = "Country ISO3 code") @RequestParam(required = false) String country,
598
+ @Parameter(description = "Status filter") @RequestParam(required = false) String status,
599
+ @PageableDefault(size = 20) Pageable pageable) {
600
+ // ...
601
+ }
602
+ }
603
+ ```
604
+
605
+ ---
606
+
607
+ ## Service Interface Pattern
608
+
609
+ ```java
610
+ package {{BASE_PACKAGE}}.{{module}};
611
+
612
+ import org.springframework.data.domain.Page;
613
+ import org.springframework.data.domain.Pageable;
614
+
615
+ public interface {{ModuleName}}Service {
616
+
617
+ Page<{{ModuleName}}DTO> findAll(String search, Pageable pageable);
618
+
619
+ {{ModuleName}}DTO findById(String id);
620
+
621
+ {{ModuleName}}DTO create(Create{{ModuleName}}Request request);
622
+
623
+ {{ModuleName}}DTO update(String id, Update{{ModuleName}}Request request);
624
+
625
+ void delete(String id);
626
+ }
627
+ ```
628
+
629
+ ---
630
+
631
+ ## Service Implementation Pattern
632
+
633
+ ```java
634
+ package {{BASE_PACKAGE}}.{{module}}.internal;
635
+
636
+ import {{BASE_PACKAGE}}.{{module}}.*;
637
+ import {{BASE_PACKAGE}}.shared.exception.ResourceNotFoundException;
638
+ import lombok.RequiredArgsConstructor;
639
+ import lombok.extern.slf4j.Slf4j;
640
+ import org.springframework.data.domain.Page;
641
+ import org.springframework.data.domain.Pageable;
642
+ import org.springframework.stereotype.Service;
643
+ import org.springframework.transaction.annotation.Transactional;
644
+
645
+ @Service
646
+ @RequiredArgsConstructor
647
+ @Slf4j
648
+ @Transactional(readOnly = true)
649
+ class {{ModuleName}}ServiceImpl implements {{ModuleName}}Service {
650
+
651
+ private final {{ModuleName}}Repository repository;
652
+ private final {{ModuleName}}Mapper mapper;
653
+
654
+ @Override
655
+ public Page<{{ModuleName}}DTO> findAll(String search, Pageable pageable) {
656
+ if (search != null && !search.isBlank()) {
657
+ return repository.findByNameContainingIgnoreCase(search, pageable)
658
+ .map(mapper::toDTO);
659
+ }
660
+ return repository.findAll(pageable).map(mapper::toDTO);
661
+ }
662
+
663
+ @Override
664
+ public {{ModuleName}}DTO findById(String id) {
665
+ return repository.findById(id)
666
+ .map(mapper::toDTO)
667
+ .orElseThrow(() -> new ResourceNotFoundException("{{ModuleName}}", "id", id));
668
+ }
669
+
670
+ @Override
671
+ @Transactional
672
+ public {{ModuleName}}DTO create(Create{{ModuleName}}Request request) {
673
+ var entity = mapper.toEntity(request);
674
+ var saved = repository.save(entity);
675
+ log.info("Created {{ModuleName}}: {}", saved.getId());
676
+ return mapper.toDTO(saved);
677
+ }
678
+
679
+ @Override
680
+ @Transactional
681
+ public {{ModuleName}}DTO update(String id, Update{{ModuleName}}Request request) {
682
+ var entity = repository.findById(id)
683
+ .orElseThrow(() -> new ResourceNotFoundException("{{ModuleName}}", "id", id));
684
+ mapper.updateEntity(request, entity);
685
+ var saved = repository.save(entity);
686
+ log.info("Updated {{ModuleName}}: {}", saved.getId());
687
+ return mapper.toDTO(saved);
688
+ }
689
+
690
+ @Override
691
+ @Transactional
692
+ public void delete(String id) {
693
+ if (!repository.existsById(id)) {
694
+ throw new ResourceNotFoundException("{{ModuleName}}", "id", id);
695
+ }
696
+ repository.deleteById(id);
697
+ log.info("Deleted {{ModuleName}}: {}", id);
698
+ }
699
+ }
700
+ ```
701
+
702
+ ---
703
+
704
+ ## MapStruct Mapper Pattern
705
+
706
+ ```java
707
+ package {{BASE_PACKAGE}}.{{module}}.internal;
708
+
709
+ import {{BASE_PACKAGE}}.{{module}}.*;
710
+ import org.mapstruct.Mapper;
711
+ import org.mapstruct.MappingTarget;
712
+
713
+ @Mapper(componentModel = "spring")
714
+ interface {{ModuleName}}Mapper {
715
+
716
+ {{ModuleName}}DTO toDTO({{ModuleName}}Entity entity);
717
+
718
+ {{ModuleName}}Entity toEntity(Create{{ModuleName}}Request request);
719
+
720
+ void updateEntity(Update{{ModuleName}}Request request, @MappingTarget {{ModuleName}}Entity entity);
721
+ }
722
+ ```