javi-forge 1.0.0 → 1.2.0
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.
- package/ai-config/skills/docs/api-documentation/SKILL.md +293 -0
- package/ai-config/skills/docs/docs-spring/SKILL.md +377 -0
- package/ai-config/skills/docs/mustache-templates/SKILL.md +190 -0
- package/ai-config/skills/docs/technical-docs/SKILL.md +447 -0
- package/ci-local/ci-local.sh +37 -3
- package/ci-local/docker/node.Dockerfile +7 -0
- package/ci-local/hooks/commit-msg +0 -0
- package/ci-local/hooks/pre-commit +10 -155
- package/ci-local/hooks/pre-push +12 -29
- package/ci-local/install.sh +0 -0
- package/dist/commands/ci.d.ts +33 -0
- package/dist/commands/ci.js +341 -0
- package/dist/commands/init.js +5 -0
- package/dist/index.js +39 -5
- package/dist/lib/docker.d.ts +43 -0
- package/dist/lib/docker.js +223 -0
- package/dist/ui/CI.d.ts +9 -0
- package/dist/ui/CI.js +91 -0
- package/lib/common.sh +183 -0
- package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +25 -0
- package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +29 -0
- package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +18 -0
- package/package.json +20 -12
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-documentation
|
|
3
|
+
description: >
|
|
4
|
+
API documentation concepts. OpenAPI/Swagger, interactive docs, versioning, examples.
|
|
5
|
+
Trigger: API docs, OpenAPI, Swagger, Redoc, documentation, API reference
|
|
6
|
+
tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Edit
|
|
10
|
+
- Grep
|
|
11
|
+
metadata:
|
|
12
|
+
author: apigen-team
|
|
13
|
+
version: "1.0"
|
|
14
|
+
tags: [documentation, openapi, swagger, api]
|
|
15
|
+
scope: ["**/docs/**"]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# API Documentation Concepts
|
|
19
|
+
|
|
20
|
+
## OpenAPI Specification (OAS)
|
|
21
|
+
|
|
22
|
+
### Structure
|
|
23
|
+
```yaml
|
|
24
|
+
openapi: 3.1.0
|
|
25
|
+
info:
|
|
26
|
+
title: My API
|
|
27
|
+
version: 1.0.0
|
|
28
|
+
description: API description with **markdown** support
|
|
29
|
+
|
|
30
|
+
servers:
|
|
31
|
+
- url: https://api.example.com/v1
|
|
32
|
+
description: Production
|
|
33
|
+
- url: https://staging-api.example.com/v1
|
|
34
|
+
description: Staging
|
|
35
|
+
|
|
36
|
+
paths:
|
|
37
|
+
/users:
|
|
38
|
+
get:
|
|
39
|
+
summary: List users
|
|
40
|
+
operationId: listUsers
|
|
41
|
+
tags: [Users]
|
|
42
|
+
parameters:
|
|
43
|
+
- name: page
|
|
44
|
+
in: query
|
|
45
|
+
schema:
|
|
46
|
+
type: integer
|
|
47
|
+
default: 1
|
|
48
|
+
responses:
|
|
49
|
+
'200':
|
|
50
|
+
description: Successful response
|
|
51
|
+
content:
|
|
52
|
+
application/json:
|
|
53
|
+
schema:
|
|
54
|
+
$ref: '#/components/schemas/UserList'
|
|
55
|
+
|
|
56
|
+
components:
|
|
57
|
+
schemas:
|
|
58
|
+
User:
|
|
59
|
+
type: object
|
|
60
|
+
required: [id, email]
|
|
61
|
+
properties:
|
|
62
|
+
id:
|
|
63
|
+
type: string
|
|
64
|
+
format: uuid
|
|
65
|
+
email:
|
|
66
|
+
type: string
|
|
67
|
+
format: email
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Key Elements
|
|
71
|
+
```
|
|
72
|
+
Info: API metadata, contact, license
|
|
73
|
+
Servers: Base URLs for environments
|
|
74
|
+
Paths: Endpoints and operations
|
|
75
|
+
Components: Reusable schemas, parameters, responses
|
|
76
|
+
Tags: Logical grouping of operations
|
|
77
|
+
Security: Authentication schemes
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Documentation Tools
|
|
81
|
+
|
|
82
|
+
### Swagger UI
|
|
83
|
+
```
|
|
84
|
+
Features:
|
|
85
|
+
- Interactive API explorer
|
|
86
|
+
- Try it out functionality
|
|
87
|
+
- Authorization support
|
|
88
|
+
- Request/response examples
|
|
89
|
+
|
|
90
|
+
Best for:
|
|
91
|
+
- Developer testing
|
|
92
|
+
- Internal documentation
|
|
93
|
+
- Quick prototyping
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Redoc
|
|
97
|
+
```
|
|
98
|
+
Features:
|
|
99
|
+
- Clean, professional look
|
|
100
|
+
- Three-panel layout
|
|
101
|
+
- Search functionality
|
|
102
|
+
- No interactivity (reference only)
|
|
103
|
+
|
|
104
|
+
Best for:
|
|
105
|
+
- Public API documentation
|
|
106
|
+
- Customer-facing docs
|
|
107
|
+
- Embedded in websites
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Stoplight
|
|
111
|
+
```
|
|
112
|
+
Features:
|
|
113
|
+
- Design-first approach
|
|
114
|
+
- Mock servers
|
|
115
|
+
- Style guides
|
|
116
|
+
- Git integration
|
|
117
|
+
|
|
118
|
+
Best for:
|
|
119
|
+
- API design
|
|
120
|
+
- Enterprise documentation
|
|
121
|
+
- Team collaboration
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Documentation Best Practices
|
|
125
|
+
|
|
126
|
+
### Descriptions
|
|
127
|
+
```yaml
|
|
128
|
+
# Good: Explains purpose and context
|
|
129
|
+
description: |
|
|
130
|
+
Retrieves a paginated list of users in the organization.
|
|
131
|
+
Results are sorted by creation date, newest first.
|
|
132
|
+
|
|
133
|
+
**Note:** Requires `users:read` permission.
|
|
134
|
+
|
|
135
|
+
# Bad: States the obvious
|
|
136
|
+
description: Gets users
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Examples
|
|
140
|
+
```yaml
|
|
141
|
+
# Provide realistic examples
|
|
142
|
+
schema:
|
|
143
|
+
type: object
|
|
144
|
+
properties:
|
|
145
|
+
email:
|
|
146
|
+
type: string
|
|
147
|
+
format: email
|
|
148
|
+
example: john.doe@example.com
|
|
149
|
+
createdAt:
|
|
150
|
+
type: string
|
|
151
|
+
format: date-time
|
|
152
|
+
example: "2024-01-15T10:30:00Z"
|
|
153
|
+
|
|
154
|
+
# Multiple examples for different scenarios
|
|
155
|
+
examples:
|
|
156
|
+
admin:
|
|
157
|
+
summary: Admin user
|
|
158
|
+
value:
|
|
159
|
+
id: "123"
|
|
160
|
+
email: "admin@example.com"
|
|
161
|
+
role: "admin"
|
|
162
|
+
regular:
|
|
163
|
+
summary: Regular user
|
|
164
|
+
value:
|
|
165
|
+
id: "456"
|
|
166
|
+
email: "user@example.com"
|
|
167
|
+
role: "user"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Error Responses
|
|
171
|
+
```yaml
|
|
172
|
+
responses:
|
|
173
|
+
'400':
|
|
174
|
+
description: Validation error
|
|
175
|
+
content:
|
|
176
|
+
application/problem+json:
|
|
177
|
+
schema:
|
|
178
|
+
$ref: '#/components/schemas/ProblemDetail'
|
|
179
|
+
examples:
|
|
180
|
+
invalidEmail:
|
|
181
|
+
summary: Invalid email format
|
|
182
|
+
value:
|
|
183
|
+
type: "https://api.example.com/errors/validation"
|
|
184
|
+
title: "Validation Error"
|
|
185
|
+
status: 400
|
|
186
|
+
detail: "Invalid email format"
|
|
187
|
+
errors:
|
|
188
|
+
- field: "email"
|
|
189
|
+
message: "Must be a valid email address"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## API Versioning Documentation
|
|
193
|
+
|
|
194
|
+
### URL Versioning
|
|
195
|
+
```yaml
|
|
196
|
+
servers:
|
|
197
|
+
- url: https://api.example.com/v1
|
|
198
|
+
- url: https://api.example.com/v2
|
|
199
|
+
|
|
200
|
+
paths:
|
|
201
|
+
/v1/users:
|
|
202
|
+
deprecated: true
|
|
203
|
+
x-deprecation-date: 2024-06-01
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Header Versioning
|
|
207
|
+
```yaml
|
|
208
|
+
parameters:
|
|
209
|
+
- name: API-Version
|
|
210
|
+
in: header
|
|
211
|
+
required: false
|
|
212
|
+
schema:
|
|
213
|
+
type: string
|
|
214
|
+
default: "2024-01-01"
|
|
215
|
+
enum:
|
|
216
|
+
- "2024-01-01"
|
|
217
|
+
- "2023-06-01"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Authentication Documentation
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
components:
|
|
224
|
+
securitySchemes:
|
|
225
|
+
bearerAuth:
|
|
226
|
+
type: http
|
|
227
|
+
scheme: bearer
|
|
228
|
+
bearerFormat: JWT
|
|
229
|
+
description: |
|
|
230
|
+
JWT token obtained from `/auth/login`.
|
|
231
|
+
Token expires after 1 hour.
|
|
232
|
+
|
|
233
|
+
apiKey:
|
|
234
|
+
type: apiKey
|
|
235
|
+
in: header
|
|
236
|
+
name: X-API-Key
|
|
237
|
+
description: |
|
|
238
|
+
API key for server-to-server communication.
|
|
239
|
+
Contact support to obtain a key.
|
|
240
|
+
|
|
241
|
+
oauth2:
|
|
242
|
+
type: oauth2
|
|
243
|
+
flows:
|
|
244
|
+
authorizationCode:
|
|
245
|
+
authorizationUrl: https://auth.example.com/authorize
|
|
246
|
+
tokenUrl: https://auth.example.com/token
|
|
247
|
+
scopes:
|
|
248
|
+
users:read: Read user information
|
|
249
|
+
users:write: Create and update users
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Changelog & Migration
|
|
253
|
+
|
|
254
|
+
```markdown
|
|
255
|
+
## API Changelog
|
|
256
|
+
|
|
257
|
+
### v2.0.0 (2024-01-15)
|
|
258
|
+
#### Breaking Changes
|
|
259
|
+
- `GET /users` now returns paginated response
|
|
260
|
+
- `email` field renamed to `emailAddress`
|
|
261
|
+
|
|
262
|
+
#### New Features
|
|
263
|
+
- Added `GET /users/{id}/preferences`
|
|
264
|
+
- Added filtering by `status` parameter
|
|
265
|
+
|
|
266
|
+
### v1.5.0 (2023-12-01)
|
|
267
|
+
#### Deprecations
|
|
268
|
+
- `GET /users/all` deprecated, use `GET /users` with pagination
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Documentation-as-Code
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Benefits:
|
|
275
|
+
- Version controlled
|
|
276
|
+
- Review process
|
|
277
|
+
- CI/CD integration
|
|
278
|
+
- Single source of truth
|
|
279
|
+
|
|
280
|
+
Workflow:
|
|
281
|
+
1. Write OpenAPI spec (YAML/JSON)
|
|
282
|
+
2. Review changes via PR
|
|
283
|
+
3. Generate docs on merge
|
|
284
|
+
4. Deploy to documentation site
|
|
285
|
+
5. Generate SDKs from spec
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Related Skills
|
|
289
|
+
|
|
290
|
+
- `docs-spring`: Spring Boot API documentation
|
|
291
|
+
- `apigen-architecture`: Overall system architecture
|
|
292
|
+
|
|
293
|
+
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs-spring
|
|
3
|
+
description: >
|
|
4
|
+
Spring Boot API documentation. SpringDoc, Swagger UI, Redoc, custom themes.
|
|
5
|
+
Trigger: apigen-docs, SpringDoc, Swagger, OpenAPI, @Operation, @Schema
|
|
6
|
+
tools:
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Edit
|
|
10
|
+
- Bash
|
|
11
|
+
- Grep
|
|
12
|
+
metadata:
|
|
13
|
+
author: apigen-team
|
|
14
|
+
version: "1.0"
|
|
15
|
+
tags: [documentation, spring-boot, springdoc, java]
|
|
16
|
+
scope: ["apigen-docs/**"]
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# API Documentation Spring Boot (apigen-docs)
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
apigen:
|
|
25
|
+
docs:
|
|
26
|
+
enabled: true
|
|
27
|
+
|
|
28
|
+
openapi:
|
|
29
|
+
title: ${spring.application.name}
|
|
30
|
+
version: ${app.version:1.0.0}
|
|
31
|
+
description: API Documentation
|
|
32
|
+
contact:
|
|
33
|
+
name: API Support
|
|
34
|
+
email: support@example.com
|
|
35
|
+
license:
|
|
36
|
+
name: Apache 2.0
|
|
37
|
+
url: https://www.apache.org/licenses/LICENSE-2.0
|
|
38
|
+
|
|
39
|
+
swagger-ui:
|
|
40
|
+
enabled: true
|
|
41
|
+
path: /swagger-ui
|
|
42
|
+
theme: dark # default, dark, feeling-blue
|
|
43
|
+
try-it-out: true
|
|
44
|
+
|
|
45
|
+
redoc:
|
|
46
|
+
enabled: true
|
|
47
|
+
path: /redoc
|
|
48
|
+
|
|
49
|
+
graphql-playground:
|
|
50
|
+
enabled: true
|
|
51
|
+
path: /graphql-playground
|
|
52
|
+
|
|
53
|
+
springdoc:
|
|
54
|
+
api-docs:
|
|
55
|
+
path: /api-docs
|
|
56
|
+
show-actuator: false
|
|
57
|
+
packages-to-scan: com.example.api
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## OpenAPI Configuration
|
|
61
|
+
|
|
62
|
+
```java
|
|
63
|
+
@Configuration
|
|
64
|
+
@EnableConfigurationProperties(DocsProperties.class)
|
|
65
|
+
public class OpenApiConfiguration {
|
|
66
|
+
|
|
67
|
+
@Bean
|
|
68
|
+
public OpenAPI customOpenAPI(DocsProperties props) {
|
|
69
|
+
return new OpenAPI()
|
|
70
|
+
.info(new Info()
|
|
71
|
+
.title(props.getOpenapi().getTitle())
|
|
72
|
+
.version(props.getOpenapi().getVersion())
|
|
73
|
+
.description(props.getOpenapi().getDescription())
|
|
74
|
+
.contact(new Contact()
|
|
75
|
+
.name(props.getOpenapi().getContact().getName())
|
|
76
|
+
.email(props.getOpenapi().getContact().getEmail()))
|
|
77
|
+
.license(new License()
|
|
78
|
+
.name(props.getOpenapi().getLicense().getName())
|
|
79
|
+
.url(props.getOpenapi().getLicense().getUrl())))
|
|
80
|
+
.externalDocs(new ExternalDocumentation()
|
|
81
|
+
.description("API Guide")
|
|
82
|
+
.url("https://docs.example.com"))
|
|
83
|
+
.components(new Components()
|
|
84
|
+
.addSecuritySchemes("bearerAuth", securityScheme()))
|
|
85
|
+
.security(List.of(new SecurityRequirement().addList("bearerAuth")));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private SecurityScheme securityScheme() {
|
|
89
|
+
return new SecurityScheme()
|
|
90
|
+
.type(SecurityScheme.Type.HTTP)
|
|
91
|
+
.scheme("bearer")
|
|
92
|
+
.bearerFormat("JWT")
|
|
93
|
+
.description("JWT token from /auth/login endpoint");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Bean
|
|
97
|
+
public GroupedOpenApi publicApi() {
|
|
98
|
+
return GroupedOpenApi.builder()
|
|
99
|
+
.group("public")
|
|
100
|
+
.pathsToMatch("/api/**")
|
|
101
|
+
.pathsToExclude("/api/admin/**")
|
|
102
|
+
.build();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@Bean
|
|
106
|
+
public GroupedOpenApi adminApi() {
|
|
107
|
+
return GroupedOpenApi.builder()
|
|
108
|
+
.group("admin")
|
|
109
|
+
.pathsToMatch("/api/admin/**")
|
|
110
|
+
.build();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Controller Annotations
|
|
116
|
+
|
|
117
|
+
```java
|
|
118
|
+
@RestController
|
|
119
|
+
@RequestMapping("/api/users")
|
|
120
|
+
@Tag(name = "Users", description = "User management operations")
|
|
121
|
+
@SecurityRequirement(name = "bearerAuth")
|
|
122
|
+
public class UserController {
|
|
123
|
+
|
|
124
|
+
@Operation(
|
|
125
|
+
summary = "Get user by ID",
|
|
126
|
+
description = "Retrieves a user by their unique identifier",
|
|
127
|
+
responses = {
|
|
128
|
+
@ApiResponse(
|
|
129
|
+
responseCode = "200",
|
|
130
|
+
description = "User found",
|
|
131
|
+
content = @Content(schema = @Schema(implementation = UserDTO.class))),
|
|
132
|
+
@ApiResponse(
|
|
133
|
+
responseCode = "404",
|
|
134
|
+
description = "User not found",
|
|
135
|
+
content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
@GetMapping("/{id}")
|
|
139
|
+
public UserDTO getUser(
|
|
140
|
+
@Parameter(description = "User ID", example = "123e4567-e89b-12d3-a456-426614174000")
|
|
141
|
+
@PathVariable UUID id) {
|
|
142
|
+
return userService.findById(id);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Operation(
|
|
146
|
+
summary = "Create user",
|
|
147
|
+
description = "Creates a new user account"
|
|
148
|
+
)
|
|
149
|
+
@ApiResponse(responseCode = "201", description = "User created")
|
|
150
|
+
@ApiResponse(responseCode = "400", description = "Invalid input")
|
|
151
|
+
@ApiResponse(responseCode = "409", description = "Email already exists")
|
|
152
|
+
@PostMapping
|
|
153
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
154
|
+
public UserDTO createUser(
|
|
155
|
+
@RequestBody @Valid
|
|
156
|
+
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
|
157
|
+
description = "User to create",
|
|
158
|
+
required = true,
|
|
159
|
+
content = @Content(
|
|
160
|
+
examples = @ExampleObject(
|
|
161
|
+
name = "Example user",
|
|
162
|
+
value = """
|
|
163
|
+
{
|
|
164
|
+
"email": "john@example.com",
|
|
165
|
+
"name": "John Doe",
|
|
166
|
+
"password": "SecurePass123!"
|
|
167
|
+
}
|
|
168
|
+
"""
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
CreateUserRequest request) {
|
|
173
|
+
return userService.create(request);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@Operation(
|
|
177
|
+
summary = "List users",
|
|
178
|
+
description = "Returns a paginated list of users"
|
|
179
|
+
)
|
|
180
|
+
@GetMapping
|
|
181
|
+
public Page<UserDTO> listUsers(
|
|
182
|
+
@Parameter(description = "Page number (0-indexed)")
|
|
183
|
+
@RequestParam(defaultValue = "0") int page,
|
|
184
|
+
|
|
185
|
+
@Parameter(description = "Page size")
|
|
186
|
+
@RequestParam(defaultValue = "20") int size,
|
|
187
|
+
|
|
188
|
+
@Parameter(description = "Filter by status")
|
|
189
|
+
@RequestParam(required = false) UserStatus status) {
|
|
190
|
+
return userService.findAll(PageRequest.of(page, size), status);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Schema Annotations
|
|
196
|
+
|
|
197
|
+
```java
|
|
198
|
+
@Schema(description = "User data transfer object")
|
|
199
|
+
public record UserDTO(
|
|
200
|
+
@Schema(description = "Unique user identifier",
|
|
201
|
+
example = "123e4567-e89b-12d3-a456-426614174000",
|
|
202
|
+
accessMode = Schema.AccessMode.READ_ONLY)
|
|
203
|
+
UUID id,
|
|
204
|
+
|
|
205
|
+
@Schema(description = "User email address",
|
|
206
|
+
example = "john@example.com",
|
|
207
|
+
requiredMode = Schema.RequiredMode.REQUIRED)
|
|
208
|
+
String email,
|
|
209
|
+
|
|
210
|
+
@Schema(description = "User display name",
|
|
211
|
+
example = "John Doe",
|
|
212
|
+
maxLength = 100)
|
|
213
|
+
String name,
|
|
214
|
+
|
|
215
|
+
@Schema(description = "User account status",
|
|
216
|
+
defaultValue = "ACTIVE")
|
|
217
|
+
UserStatus status,
|
|
218
|
+
|
|
219
|
+
@Schema(description = "Account creation timestamp",
|
|
220
|
+
accessMode = Schema.AccessMode.READ_ONLY)
|
|
221
|
+
Instant createdAt
|
|
222
|
+
) {}
|
|
223
|
+
|
|
224
|
+
@Schema(description = "Request to create a new user")
|
|
225
|
+
public record CreateUserRequest(
|
|
226
|
+
@Schema(description = "Email address", example = "john@example.com")
|
|
227
|
+
@Email @NotBlank
|
|
228
|
+
String email,
|
|
229
|
+
|
|
230
|
+
@Schema(description = "Display name", example = "John Doe")
|
|
231
|
+
@NotBlank @Size(max = 100)
|
|
232
|
+
String name,
|
|
233
|
+
|
|
234
|
+
@Schema(description = "Password (min 8 chars, must contain uppercase, lowercase, digit)",
|
|
235
|
+
example = "SecurePass123!",
|
|
236
|
+
minLength = 8)
|
|
237
|
+
@NotBlank @Size(min = 8)
|
|
238
|
+
String password
|
|
239
|
+
) {}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Custom Swagger UI Theme
|
|
243
|
+
|
|
244
|
+
```java
|
|
245
|
+
@Bean
|
|
246
|
+
public SwaggerUiConfigProperties swaggerUiConfig() {
|
|
247
|
+
SwaggerUiConfigProperties props = new SwaggerUiConfigProperties();
|
|
248
|
+
props.setPath("/swagger-ui");
|
|
249
|
+
props.setTryItOutEnabled(true);
|
|
250
|
+
props.setFilter(true);
|
|
251
|
+
props.setShowExtensions(true);
|
|
252
|
+
props.setPersistAuthorization(true);
|
|
253
|
+
return props;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Custom CSS for dark theme
|
|
257
|
+
@Bean
|
|
258
|
+
public WebMvcConfigurer swaggerUiCustomizer() {
|
|
259
|
+
return new WebMvcConfigurer() {
|
|
260
|
+
@Override
|
|
261
|
+
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
262
|
+
registry.addResourceHandler("/swagger-ui/custom.css")
|
|
263
|
+
.addResourceLocations("classpath:/static/swagger/");
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Redoc Configuration
|
|
270
|
+
|
|
271
|
+
```java
|
|
272
|
+
@Controller
|
|
273
|
+
@ConditionalOnProperty(prefix = "apigen.docs.redoc", name = "enabled", havingValue = "true")
|
|
274
|
+
public class RedocController {
|
|
275
|
+
|
|
276
|
+
@GetMapping("/redoc")
|
|
277
|
+
public String redoc(Model model) {
|
|
278
|
+
model.addAttribute("specUrl", "/api-docs");
|
|
279
|
+
model.addAttribute("options", Map.of(
|
|
280
|
+
"hideDownloadButton", false,
|
|
281
|
+
"hideHostname", false,
|
|
282
|
+
"expandResponses", "200,201",
|
|
283
|
+
"theme", Map.of(
|
|
284
|
+
"colors", Map.of(
|
|
285
|
+
"primary", Map.of("main", "#1976d2")
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
));
|
|
289
|
+
return "redoc";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
```html
|
|
295
|
+
<!-- templates/redoc.html -->
|
|
296
|
+
<!DOCTYPE html>
|
|
297
|
+
<html>
|
|
298
|
+
<head>
|
|
299
|
+
<title>API Documentation</title>
|
|
300
|
+
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700" rel="stylesheet">
|
|
301
|
+
<style>body { margin: 0; padding: 0; }</style>
|
|
302
|
+
</head>
|
|
303
|
+
<body>
|
|
304
|
+
<redoc spec-url="/api-docs" th:attr="spec-url=${specUrl}"></redoc>
|
|
305
|
+
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
|
|
306
|
+
</body>
|
|
307
|
+
</html>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## OpenAPI Customizer
|
|
311
|
+
|
|
312
|
+
```java
|
|
313
|
+
@Component
|
|
314
|
+
public class ApiDocsCustomizer implements OpenApiCustomizer {
|
|
315
|
+
|
|
316
|
+
@Override
|
|
317
|
+
public void customise(OpenAPI openApi) {
|
|
318
|
+
// Add common headers
|
|
319
|
+
openApi.getPaths().values().forEach(pathItem ->
|
|
320
|
+
pathItem.readOperations().forEach(operation -> {
|
|
321
|
+
if (operation.getParameters() == null) {
|
|
322
|
+
operation.setParameters(new ArrayList<>());
|
|
323
|
+
}
|
|
324
|
+
operation.getParameters().add(new Parameter()
|
|
325
|
+
.name("X-Request-ID")
|
|
326
|
+
.in("header")
|
|
327
|
+
.description("Request correlation ID")
|
|
328
|
+
.schema(new StringSchema().format("uuid")));
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
// Add server list
|
|
333
|
+
openApi.setServers(List.of(
|
|
334
|
+
new Server().url("https://api.example.com").description("Production"),
|
|
335
|
+
new Server().url("https://staging.api.example.com").description("Staging"),
|
|
336
|
+
new Server().url("http://localhost:8080").description("Local")
|
|
337
|
+
));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## REST Controller for Docs
|
|
343
|
+
|
|
344
|
+
```java
|
|
345
|
+
@RestController
|
|
346
|
+
@RequestMapping("/api/docs")
|
|
347
|
+
public class DocsController {
|
|
348
|
+
|
|
349
|
+
private final ResourceLoader resourceLoader;
|
|
350
|
+
|
|
351
|
+
@GetMapping("/openapi.yaml")
|
|
352
|
+
public ResponseEntity<Resource> getOpenApiYaml() {
|
|
353
|
+
Resource resource = resourceLoader.getResource("classpath:openapi/api.yaml");
|
|
354
|
+
return ResponseEntity.ok()
|
|
355
|
+
.contentType(MediaType.parseMediaType("application/x-yaml"))
|
|
356
|
+
.body(resource);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@GetMapping("/changelog")
|
|
360
|
+
public ResponseEntity<String> getChangelog() throws IOException {
|
|
361
|
+
Resource resource = resourceLoader.getResource("classpath:docs/CHANGELOG.md");
|
|
362
|
+
String content = StreamUtils.copyToString(
|
|
363
|
+
resource.getInputStream(), StandardCharsets.UTF_8);
|
|
364
|
+
return ResponseEntity.ok()
|
|
365
|
+
.contentType(MediaType.TEXT_MARKDOWN)
|
|
366
|
+
.body(content);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Related Skills
|
|
372
|
+
|
|
373
|
+
- `api-documentation`: API documentation concepts
|
|
374
|
+
- `spring-boot-4`: Spring Boot 4.0 patterns
|
|
375
|
+
- `apigen-architecture`: Overall system architecture
|
|
376
|
+
|
|
377
|
+
|