0
# Advanced Features
1
2
This document covers advanced OpenAPI annotations including extensions, callbacks for asynchronous operations, webhooks, links between operations, and OpenAPI 3.1 enhanced features.
3
4
## Imports
5
6
```java { .api }
7
import io.swagger.v3.oas.annotations.extensions.Extension;
8
import io.swagger.v3.oas.annotations.extensions.Extensions;
9
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
10
import io.swagger.v3.oas.annotations.callbacks.Callback;
11
import io.swagger.v3.oas.annotations.callbacks.Callbacks;
12
import io.swagger.v3.oas.annotations.Webhook;
13
import io.swagger.v3.oas.annotations.Webhooks;
14
import io.swagger.v3.oas.annotations.links.Link;
15
import io.swagger.v3.oas.annotations.links.LinkParameter;
16
import io.swagger.v3.oas.annotations.headers.Header;
17
import io.swagger.v3.oas.annotations.OpenAPI31;
18
```
19
20
## Extensions
21
22
OpenAPI extensions allow you to add vendor-specific or custom properties to your API specification using the `x-` prefix convention.
23
24
### Basic Extensions
25
26
```java { .api }
27
@Operation(
28
summary = "Get pet by ID",
29
extensions = {
30
@Extension(name = "x-rate-limit", properties = {
31
@ExtensionProperty(name = "requests", value = "100"),
32
@ExtensionProperty(name = "period", value = "minute")
33
}),
34
@Extension(name = "x-cache-duration", properties = {
35
@ExtensionProperty(name = "seconds", value = "300")
36
})
37
}
38
)
39
@GetMapping("/pets/{id}")
40
public ResponseEntity<Pet> getPetById(@PathVariable Long id) {
41
return ResponseEntity.ok(petService.findById(id));
42
}
43
```
44
45
### Schema Extensions
46
47
```java { .api }
48
@Schema(
49
description = "Pet information with enhanced metadata",
50
extensions = {
51
@Extension(name = "x-data-classification", properties = {
52
@ExtensionProperty(name = "level", value = "public"),
53
@ExtensionProperty(name = "retention", value = "7-years"),
54
@ExtensionProperty(name = "pii", value = "false")
55
}),
56
@Extension(name = "x-validation-rules", properties = {
57
@ExtensionProperty(name = "custom-validator", value = "PetNameValidator"),
58
@ExtensionProperty(name = "business-rules", value = "no-duplicate-names-per-owner")
59
}),
60
@Extension(name = "x-ui-hints", properties = {
61
@ExtensionProperty(name = "display-order", value = "name,category,status,tags"),
62
@ExtensionProperty(name = "required-highlight", value = "true"),
63
@ExtensionProperty(name = "collapse-advanced", value = "true")
64
})
65
}
66
)
67
public class Pet {
68
@Schema(
69
description = "Pet name",
70
extensions = {
71
@Extension(name = "x-input-validation", properties = {
72
@ExtensionProperty(name = "trim-whitespace", value = "true"),
73
@ExtensionProperty(name = "capitalize-first", value = "true")
74
})
75
}
76
)
77
private String name;
78
79
@Schema(
80
description = "Pet status",
81
extensions = {
82
@Extension(name = "x-state-machine", properties = {
83
@ExtensionProperty(name = "transitions", value = "available->pending,pending->sold,sold->available"),
84
@ExtensionProperty(name = "initial-state", value = "available")
85
})
86
}
87
)
88
private String status;
89
}
90
```
91
92
### API-Level Extensions
93
94
```java { .api }
95
@OpenAPIDefinition(
96
info = @Info(
97
title = "Pet Store API with Advanced Features",
98
version = "1.0.0",
99
extensions = {
100
@Extension(name = "x-api-capabilities", properties = {
101
@ExtensionProperty(name = "real-time", value = "webhooks,sse"),
102
@ExtensionProperty(name = "batch-operations", value = "supported"),
103
@ExtensionProperty(name = "async-processing", value = "callbacks")
104
}),
105
@Extension(name = "x-service-level", properties = {
106
@ExtensionProperty(name = "availability", value = "99.9%"),
107
@ExtensionProperty(name = "response-time-p95", value = "200ms"),
108
@ExtensionProperty(name = "throughput", value = "1000-rps")
109
})
110
}
111
),
112
extensions = {
113
@Extension(name = "x-monetization", properties = {
114
@ExtensionProperty(name = "pricing-model", value = "usage-based"),
115
@ExtensionProperty(name = "billing-cycle", value = "monthly"),
116
@ExtensionProperty(name = "free-tier", value = "1000-requests")
117
}),
118
@Extension(name = "x-compliance", properties = {
119
@ExtensionProperty(name = "gdpr", value = "compliant"),
120
@ExtensionProperty(name = "soc2", value = "type-2"),
121
@ExtensionProperty(name = "pci-dss", value = "level-1")
122
})
123
}
124
)
125
```
126
127
### Server Extensions
128
129
```java { .api }
130
@Server(
131
url = "https://api.petstore.io/v1",
132
description = "Production server with enhanced features",
133
extensions = {
134
@Extension(name = "x-server-capabilities", properties = {
135
@ExtensionProperty(name = "websockets", value = "supported"),
136
@ExtensionProperty(name = "server-sent-events", value = "supported"),
137
@ExtensionProperty(name = "graphql", value = "/graphql"),
138
@ExtensionProperty(name = "grpc", value = "api.petstore.io:443")
139
}),
140
@Extension(name = "x-infrastructure", properties = {
141
@ExtensionProperty(name = "provider", value = "aws"),
142
@ExtensionProperty(name = "region", value = "us-east-1"),
143
@ExtensionProperty(name = "load-balancer", value = "application"),
144
@ExtensionProperty(name = "cdn", value = "cloudfront")
145
})
146
}
147
)
148
```
149
150
## Callbacks
151
152
Define callback requests that your API will make to client-provided URLs, typically for asynchronous operations and event notifications.
153
154
### Basic Callback Configuration
155
156
```java { .api }
157
@PostMapping("/pets/{id}/process")
158
@Operation(
159
summary = "Start pet processing",
160
description = "Initiates asynchronous pet processing and calls back when complete",
161
callbacks = @Callback(
162
name = "processingComplete",
163
callbackUrlExpression = "{$request.body#/callbackUrl}",
164
operation = @Operation(
165
method = "POST",
166
summary = "Processing completion notification",
167
requestBody = @RequestBody(
168
description = "Processing results",
169
content = @Content(
170
mediaType = "application/json",
171
schema = @Schema(implementation = ProcessingResult.class)
172
)
173
),
174
responses = {
175
@ApiResponse(
176
responseCode = "200",
177
description = "Callback received successfully"
178
),
179
@ApiResponse(
180
responseCode = "400",
181
description = "Invalid callback data"
182
)
183
}
184
)
185
)
186
)
187
public ResponseEntity<ProcessingRequest> startProcessing(
188
@PathVariable Long id,
189
@RequestBody ProcessingRequest request
190
) {
191
// Start async processing
192
ProcessingRequest response = petService.startProcessing(id, request);
193
return ResponseEntity.accepted().body(response);
194
}
195
```
196
197
### Multiple Callbacks with Different Events
198
199
```java { .api }
200
@PostMapping("/orders")
201
@Operation(
202
summary = "Create order with status notifications",
203
callbacks = {
204
@Callback(
205
name = "orderStatusUpdate",
206
callbackUrlExpression = "{$request.body#/webhooks.statusUpdate}",
207
operation = @Operation(
208
method = "POST",
209
summary = "Order status change notification",
210
requestBody = @RequestBody(
211
content = @Content(
212
mediaType = "application/json",
213
schema = @Schema(implementation = OrderStatusEvent.class)
214
)
215
),
216
responses = @ApiResponse(responseCode = "200", description = "Status update received")
217
)
218
),
219
@Callback(
220
name = "paymentProcessed",
221
callbackUrlExpression = "{$request.body#/webhooks.payment}",
222
operation = @Operation(
223
method = "POST",
224
summary = "Payment processing notification",
225
requestBody = @RequestBody(
226
content = @Content(
227
mediaType = "application/json",
228
schema = @Schema(implementation = PaymentEvent.class)
229
)
230
),
231
responses = @ApiResponse(responseCode = "200", description = "Payment notification received")
232
)
233
),
234
@Callback(
235
name = "deliveryUpdate",
236
callbackUrlExpression = "{$request.body#/webhooks.delivery}",
237
operation = @Operation(
238
method = "POST",
239
summary = "Delivery status notification",
240
requestBody = @RequestBody(
241
content = @Content(
242
mediaType = "application/json",
243
schema = @Schema(implementation = DeliveryEvent.class)
244
)
245
),
246
responses = @ApiResponse(responseCode = "200", description = "Delivery update received")
247
)
248
)
249
}
250
)
251
```
252
253
### Advanced Callback with Authentication
254
255
```java { .api }
256
@PostMapping("/subscriptions")
257
@Operation(
258
summary = "Create webhook subscription",
259
callbacks = @Callback(
260
name = "webhookEvent",
261
callbackUrlExpression = "{$request.body#/targetUrl}",
262
operation = @Operation(
263
method = "POST",
264
summary = "Webhook event notification",
265
description = "Called when subscribed events occur",
266
security = @SecurityRequirement(name = "webhook_signature"),
267
parameters = {
268
@Parameter(
269
name = "X-Webhook-Signature",
270
in = ParameterIn.HEADER,
271
description = "HMAC signature for webhook verification",
272
required = true,
273
schema = @Schema(type = "string")
274
),
275
@Parameter(
276
name = "X-Event-Type",
277
in = ParameterIn.HEADER,
278
description = "Type of event being delivered",
279
required = true,
280
schema = @Schema(type = "string", allowableValues = {
281
"pet.created", "pet.updated", "pet.deleted",
282
"order.placed", "order.shipped", "order.delivered"
283
})
284
),
285
@Parameter(
286
name = "X-Delivery-ID",
287
in = ParameterIn.HEADER,
288
description = "Unique delivery attempt identifier",
289
required = true,
290
schema = @Schema(type = "string", format = "uuid")
291
)
292
},
293
requestBody = @RequestBody(
294
description = "Event payload",
295
content = @Content(
296
mediaType = "application/json",
297
schema = @Schema(implementation = WebhookEvent.class),
298
examples = {
299
@ExampleObject(
300
name = "petCreated",
301
summary = "Pet created event",
302
value = """
303
{
304
"eventType": "pet.created",
305
"timestamp": "2024-01-15T10:30:00Z",
306
"data": {
307
"id": 123,
308
"name": "Fluffy",
309
"status": "available"
310
}
311
}
312
"""
313
)
314
}
315
)
316
),
317
responses = {
318
@ApiResponse(
319
responseCode = "200",
320
description = "Webhook processed successfully"
321
),
322
@ApiResponse(
323
responseCode = "400",
324
description = "Invalid webhook payload"
325
),
326
@ApiResponse(
327
responseCode = "401",
328
description = "Invalid webhook signature"
329
)
330
}
331
)
332
)
333
)
334
```
335
336
## Webhooks
337
338
Document webhook endpoints that clients can implement to receive notifications from your API.
339
340
### Basic Webhook Definition
341
342
```java { .api }
343
@Webhook(
344
name = "petUpdated",
345
operation = @Operation(
346
method = "POST",
347
summary = "Pet update notification",
348
description = "Called when a pet's information is updated",
349
requestBody = @RequestBody(
350
description = "Pet update event data",
351
content = @Content(
352
mediaType = "application/json",
353
schema = @Schema(implementation = PetUpdateEvent.class)
354
)
355
),
356
responses = {
357
@ApiResponse(responseCode = "200", description = "Webhook acknowledged"),
358
@ApiResponse(responseCode = "410", description = "Webhook endpoint no longer exists")
359
}
360
)
361
)
362
```
363
364
### Multiple Webhook Events
365
366
```java { .api }
367
@Webhooks({
368
@Webhook(
369
name = "petLifecycle",
370
operation = @Operation(
371
summary = "Pet lifecycle events",
372
description = "Notifications for pet creation, updates, and deletion",
373
requestBody = @RequestBody(
374
content = @Content(
375
mediaType = "application/json",
376
schema = @Schema(implementation = PetLifecycleEvent.class),
377
examples = {
378
@ExampleObject(
379
name = "created",
380
summary = "Pet created",
381
value = """
382
{
383
"event": "pet.created",
384
"timestamp": "2024-01-15T10:30:00Z",
385
"pet": {
386
"id": 123,
387
"name": "New Pet",
388
"status": "available"
389
}
390
}
391
"""
392
),
393
@ExampleObject(
394
name = "updated",
395
summary = "Pet updated",
396
value = """
397
{
398
"event": "pet.updated",
399
"timestamp": "2024-01-15T10:35:00Z",
400
"pet": {
401
"id": 123,
402
"name": "Updated Pet",
403
"status": "sold"
404
},
405
"changes": ["name", "status"]
406
}
407
"""
408
)
409
}
410
)
411
)
412
)
413
),
414
@Webhook(
415
name = "orderStatus",
416
operation = @Operation(
417
summary = "Order status changes",
418
description = "Notifications when order status changes",
419
requestBody = @RequestBody(
420
content = @Content(
421
mediaType = "application/json",
422
schema = @Schema(implementation = OrderStatusEvent.class)
423
)
424
)
425
)
426
),
427
@Webhook(
428
name = "inventoryAlert",
429
operation = @Operation(
430
summary = "Inventory level alerts",
431
description = "Notifications when inventory reaches threshold levels",
432
requestBody = @RequestBody(
433
content = @Content(
434
mediaType = "application/json",
435
schema = @Schema(implementation = InventoryAlert.class)
436
)
437
)
438
)
439
)
440
})
441
```
442
443
### Webhook with Security and Headers
444
445
```java { .api }
446
@Webhook(
447
name = "secureWebhook",
448
operation = @Operation(
449
summary = "Secure webhook with authentication",
450
description = "Webhook with signature verification and custom headers",
451
parameters = {
452
@Parameter(
453
name = "X-Webhook-Signature",
454
in = ParameterIn.HEADER,
455
required = true,
456
description = "HMAC-SHA256 signature of the payload",
457
schema = @Schema(type = "string"),
458
example = "sha256=a8b7c6d5e4f3g2h1..."
459
),
460
@Parameter(
461
name = "X-Event-ID",
462
in = ParameterIn.HEADER,
463
required = true,
464
description = "Unique event identifier for deduplication",
465
schema = @Schema(type = "string", format = "uuid")
466
),
467
@Parameter(
468
name = "X-Retry-Count",
469
in = ParameterIn.HEADER,
470
required = false,
471
description = "Number of delivery attempts (starts at 0)",
472
schema = @Schema(type = "integer", minimum = "0", maximum = "5")
473
)
474
},
475
requestBody = @RequestBody(
476
description = "Signed webhook payload",
477
content = @Content(
478
mediaType = "application/json",
479
schema = @Schema(implementation = SecureWebhookEvent.class)
480
)
481
),
482
responses = {
483
@ApiResponse(
484
responseCode = "200",
485
description = "Webhook processed successfully"
486
),
487
@ApiResponse(
488
responseCode = "401",
489
description = "Invalid signature",
490
content = @Content(
491
mediaType = "application/json",
492
schema = @Schema(implementation = WebhookError.class)
493
)
494
),
495
@ApiResponse(
496
responseCode = "409",
497
description = "Duplicate event (already processed)"
498
),
499
@ApiResponse(
500
responseCode = "422",
501
description = "Valid signature but invalid payload"
502
)
503
}
504
)
505
)
506
```
507
508
### Webhook Attributes
509
510
```java { .api }
511
public @interface Webhook {
512
String name(); // Required - Name of the webhook
513
Operation operation(); // Required - Operation definition for the webhook
514
}
515
516
public @interface Webhooks {
517
Webhook[] value() default {}; // Array of Webhook annotations
518
}
519
```
520
521
## Links
522
523
Define relationships between operations, allowing clients to navigate from one operation's response to related operations.
524
525
### Basic Operation Links
526
527
```java { .api }
528
@PostMapping("/pets")
529
@ApiResponse(
530
responseCode = "201",
531
description = "Pet created successfully",
532
content = @Content(
533
mediaType = "application/json",
534
schema = @Schema(implementation = Pet.class)
535
),
536
links = {
537
@Link(
538
name = "GetPetById",
539
description = "Retrieve the newly created pet",
540
operationId = "getPetById",
541
parameters = @LinkParameter(
542
name = "id",
543
expression = "$response.body#/id"
544
)
545
),
546
@Link(
547
name = "UpdatePet",
548
description = "Update the newly created pet",
549
operationId = "updatePet",
550
parameters = @LinkParameter(
551
name = "id",
552
expression = "$response.body#/id"
553
)
554
),
555
@Link(
556
name = "DeletePet",
557
description = "Delete the newly created pet",
558
operationId = "deletePet",
559
parameters = @LinkParameter(
560
name = "id",
561
expression = "$response.body#/id"
562
)
563
)
564
}
565
)
566
public ResponseEntity<Pet> createPet(@RequestBody Pet pet) {
567
Pet createdPet = petService.create(pet);
568
return ResponseEntity.status(HttpStatus.CREATED).body(createdPet);
569
}
570
```
571
572
### Complex Links with Multiple Parameters
573
574
```java { .api }
575
@GetMapping("/users/{userId}/pets")
576
@ApiResponse(
577
responseCode = "200",
578
description = "User's pets retrieved",
579
content = @Content(
580
mediaType = "application/json",
581
schema = @Schema(implementation = PetListResponse.class)
582
),
583
links = {
584
@Link(
585
name = "GetUserProfile",
586
description = "Get full user profile",
587
operationId = "getUserById",
588
parameters = @LinkParameter(
589
name = "id",
590
expression = "$request.path.userId"
591
)
592
),
593
@Link(
594
name = "CreatePetForUser",
595
description = "Create a new pet for this user",
596
operationId = "createPetForUser",
597
parameters = {
598
@LinkParameter(
599
name = "userId",
600
expression = "$request.path.userId"
601
)
602
}
603
),
604
@Link(
605
name = "GetPetDetails",
606
description = "Get details for any pet in the list",
607
operationId = "getPetById",
608
parameters = @LinkParameter(
609
name = "id",
610
expression = "$response.body#/pets/*/id"
611
)
612
),
613
@Link(
614
name = "GetUserOrders",
615
description = "Get orders placed by this user",
616
operationId = "getUserOrders",
617
parameters = @LinkParameter(
618
name = "userId",
619
expression = "$request.path.userId"
620
)
621
)
622
}
623
)
624
```
625
626
### Conditional Links with Extensions
627
628
```java { .api }
629
@GetMapping("/orders/{id}")
630
@ApiResponse(
631
responseCode = "200",
632
description = "Order details",
633
content = @Content(
634
mediaType = "application/json",
635
schema = @Schema(implementation = Order.class)
636
),
637
links = {
638
@Link(
639
name = "CancelOrder",
640
description = "Cancel this order (if cancellable)",
641
operationId = "cancelOrder",
642
parameters = @LinkParameter(
643
name = "id",
644
expression = "$response.body#/id"
645
),
646
extensions = {
647
@Extension(name = "x-conditional", properties = {
648
@ExtensionProperty(name = "condition", value = "$response.body#/status == 'pending'"),
649
@ExtensionProperty(name = "description", value = "Only available for pending orders")
650
})
651
}
652
),
653
@Link(
654
name = "TrackShipment",
655
description = "Track order shipment",
656
operationId = "getShipmentTracking",
657
parameters = @LinkParameter(
658
name = "trackingNumber",
659
expression = "$response.body#/trackingNumber"
660
),
661
extensions = {
662
@Extension(name = "x-conditional", properties = {
663
@ExtensionProperty(name = "condition", value = "$response.body#/status == 'shipped'"),
664
@ExtensionProperty(name = "description", value = "Only available for shipped orders")
665
})
666
}
667
),
668
@Link(
669
name = "RequestReturn",
670
description = "Request return for delivered order",
671
operationId = "createReturnRequest",
672
parameters = @LinkParameter(
673
name = "orderId",
674
expression = "$response.body#/id"
675
),
676
extensions = {
677
@Extension(name = "x-conditional", properties = {
678
@ExtensionProperty(name = "condition", value = "$response.body#/status == 'delivered'"),
679
@ExtensionProperty(name = "timeLimit", value = "30 days from delivery")
680
})
681
}
682
)
683
}
684
)
685
```
686
687
## OpenAPI 3.1 Features
688
689
Leverage enhanced OpenAPI 3.1 capabilities including improved JSON Schema support, conditional schemas, and enhanced validation.
690
691
### OpenAPI 3.1 Marker Usage
692
693
```java { .api }
694
@Schema(
695
description = "Enhanced pet schema with OpenAPI 3.1 features",
696
types = {"object", "null"}, // Multiple types
697
$id = "https://api.petstore.io/schemas/pet.json",
698
$schema = "https://json-schema.org/draft/2020-12/schema"
699
)
700
@OpenAPI31 // Marker for OpenAPI 3.1 specific features
701
public class EnhancedPet {
702
703
@Schema(
704
description = "Pet identifier with enhanced validation",
705
types = {"integer", "string"}, // Can be either integer or string ID
706
pattern = "^(\\d+|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$"
707
)
708
@OpenAPI31
709
private String id;
710
711
@Schema(
712
description = "Pet metadata with conditional requirements",
713
_if = @Schema(
714
properties = @SchemaProperty(
715
name = "type",
716
schema = @Schema(allowableValues = "premium")
717
)
718
),
719
_then = @Schema(
720
required = {"certification", "insurance"},
721
properties = {
722
@SchemaProperty(
723
name = "certification",
724
schema = @Schema(type = "string", minLength = 1)
725
),
726
@SchemaProperty(
727
name = "insurance",
728
schema = @Schema(type = "object", required = {"provider", "policyNumber"})
729
)
730
}
731
)
732
)
733
@OpenAPI31
734
private Map<String, Object> metadata;
735
736
@Schema(
737
description = "Pet images with enhanced array validation",
738
prefixItems = {
739
@Schema(description = "Primary image", format = "uri"),
740
@Schema(description = "Thumbnail image", format = "uri")
741
},
742
minContains = 1,
743
maxContains = 3,
744
contains = @Schema(
745
description = "Must contain at least one high-resolution image",
746
properties = @SchemaProperty(
747
name = "resolution",
748
schema = @Schema(allowableValues = "high")
749
)
750
)
751
)
752
@OpenAPI31
753
private List<PetImage> images;
754
}
755
```
756
757
### Conditional Schema Validation
758
759
```java { .api }
760
@Schema(
761
description = "Payment method with conditional validation",
762
discriminatorProperty = "type"
763
)
764
@OpenAPI31
765
public class PaymentMethod {
766
767
@Schema(description = "Payment type")
768
private String type;
769
770
@Schema(
771
description = "Payment details with type-specific validation",
772
_if = @Schema(
773
properties = @SchemaProperty(
774
name = "type",
775
schema = @Schema(allowableValues = "credit_card")
776
)
777
),
778
_then = @Schema(
779
required = {"cardNumber", "expiryDate", "cvv"},
780
properties = {
781
@SchemaProperty(
782
name = "cardNumber",
783
schema = @Schema(type = "string", pattern = "^[0-9]{13,19}$")
784
),
785
@SchemaProperty(
786
name = "expiryDate",
787
schema = @Schema(type = "string", pattern = "^(0[1-9]|1[0-2])\\/[0-9]{2}$")
788
),
789
@SchemaProperty(
790
name = "cvv",
791
schema = @Schema(type = "string", pattern = "^[0-9]{3,4}$")
792
)
793
}
794
),
795
_else = @Schema(
796
_if = @Schema(
797
properties = @SchemaProperty(
798
name = "type",
799
schema = @Schema(allowableValues = "paypal")
800
)
801
),
802
_then = @Schema(
803
required = {"paypalEmail"},
804
properties = @SchemaProperty(
805
name = "paypalEmail",
806
schema = @Schema(type = "string", format = "email")
807
)
808
),
809
_else = @Schema(
810
_if = @Schema(
811
properties = @SchemaProperty(
812
name = "type",
813
schema = @Schema(allowableValues = "bank_transfer")
814
)
815
),
816
_then = @Schema(
817
required = {"accountNumber", "routingNumber"},
818
properties = {
819
@SchemaProperty(
820
name = "accountNumber",
821
schema = @Schema(type = "string", minLength = 8, maxLength = 17)
822
),
823
@SchemaProperty(
824
name = "routingNumber",
825
schema = @Schema(type = "string", pattern = "^[0-9]{9}$")
826
)
827
}
828
)
829
)
830
)
831
)
832
@OpenAPI31
833
private Map<String, Object> details;
834
}
835
```
836
837
### Enhanced Composition with OpenAPI 3.1
838
839
```java { .api }
840
@Content(
841
mediaType = "application/json",
842
oneOf = {
843
@Schema(implementation = StandardUser.class),
844
@Schema(implementation = PremiumUser.class),
845
@Schema(implementation = EnterpriseUser.class)
846
},
847
unevaluatedProperties = @Schema(
848
description = "Additional properties not covered by oneOf schemas",
849
additionalProperties = Schema.AdditionalPropertiesValue.FALSE
850
)
851
)
852
@OpenAPI31
853
public class UserResponse {
854
// Base user properties that apply to all user types
855
}
856
857
@Schema(
858
description = "Dynamic configuration with pattern properties",
859
patternProperties = {
860
@PatternProperty(
861
pattern = "^feature_[a-z]+$",
862
schema = @Schema(
863
type = "object",
864
properties = {
865
@SchemaProperty(name = "enabled", schema = @Schema(type = "boolean")),
866
@SchemaProperty(name = "config", schema = @Schema(type = "object"))
867
}
868
)
869
),
870
@PatternProperty(
871
pattern = "^metric_[a-z_]+$",
872
schema = @Schema(
873
type = "number",
874
minimum = "0"
875
)
876
)
877
},
878
unevaluatedProperties = @Schema(additionalProperties = Schema.AdditionalPropertiesValue.FALSE)
879
)
880
@OpenAPI31
881
public class DynamicConfiguration {
882
// Static configuration properties
883
@Schema(description = "Configuration version")
884
private String version;
885
886
@Schema(description = "Last updated timestamp")
887
private Instant updatedAt;
888
889
// Dynamic properties validated by pattern properties
890
}
891
```
892
893
### Content Encoding and Media Type (OpenAPI 3.1)
894
895
```java { .api }
896
@Schema(
897
description = "File data with encoding information",
898
contentEncoding = "base64",
899
contentMediaType = "image/jpeg",
900
type = "string"
901
)
902
@OpenAPI31
903
public class EncodedFileData {
904
905
@Schema(
906
description = "Base64 encoded image data",
907
contentEncoding = "base64",
908
contentMediaType = "image/jpeg"
909
)
910
private String imageData;
911
912
@Schema(
913
description = "Compressed text data",
914
contentEncoding = "gzip",
915
contentMediaType = "text/plain"
916
)
917
private String compressedText;
918
919
@Schema(
920
description = "Binary file data",
921
contentEncoding = "binary",
922
contentMediaType = "application/octet-stream"
923
)
924
private String binaryData;
925
}
926
```
927
928
## Complete Advanced Features Example
929
930
```java { .api }
931
@RestController
932
@OpenAPIDefinition(
933
info = @Info(
934
title = "Advanced Pet Store API",
935
version = "3.1.0",
936
extensions = {
937
@Extension(name = "x-api-features", properties = {
938
@ExtensionProperty(name = "webhooks", value = "supported"),
939
@ExtensionProperty(name = "callbacks", value = "supported"),
940
@ExtensionProperty(name = "openapi-version", value = "3.1")
941
})
942
}
943
)
944
)
945
@Webhooks({
946
@Webhook(
947
name = "petStatusChanged",
948
operation = @Operation(
949
summary = "Pet status change notification",
950
requestBody = @RequestBody(
951
content = @Content(
952
mediaType = "application/json",
953
schema = @Schema(implementation = PetStatusEvent.class)
954
)
955
)
956
)
957
)
958
})
959
public class AdvancedPetStoreController {
960
961
@PostMapping("/pets/async-process")
962
@Operation(
963
summary = "Start asynchronous pet processing",
964
extensions = {
965
@Extension(name = "x-processing-type", properties = {
966
@ExtensionProperty(name = "type", value = "asynchronous"),
967
@ExtensionProperty(name = "estimated-duration", value = "5-10 minutes")
968
})
969
},
970
callbacks = @Callback(
971
name = "processingComplete",
972
callbackUrlExpression = "{$request.body#/callbackUrl}",
973
operation = @Operation(
974
method = "POST",
975
summary = "Processing completion callback",
976
requestBody = @RequestBody(
977
content = @Content(
978
mediaType = "application/json",
979
schema = @Schema(implementation = ProcessingResult.class)
980
)
981
)
982
)
983
)
984
)
985
@ApiResponse(
986
responseCode = "202",
987
description = "Processing started",
988
content = @Content(
989
schema = @Schema(implementation = ProcessingRequest.class)
990
),
991
links = {
992
@Link(
993
name = "CheckStatus",
994
operationId = "getProcessingStatus",
995
parameters = @LinkParameter(
996
name = "processId",
997
expression = "$response.body#/processId"
998
)
999
),
1000
@Link(
1001
name = "CancelProcessing",
1002
operationId = "cancelProcessing",
1003
parameters = @LinkParameter(
1004
name = "processId",
1005
expression = "$response.body#/processId"
1006
)
1007
)
1008
}
1009
)
1010
public ResponseEntity<ProcessingRequest> startAsyncProcessing(
1011
@RequestBody @OpenAPI31 AsyncProcessingRequest request
1012
) {
1013
ProcessingRequest result = petService.startAsyncProcessing(request);
1014
return ResponseEntity.accepted().body(result);
1015
}
1016
}
1017
```