CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-swagger--swagger-annotations

Java annotations for defining Swagger API documentation and OpenAPI specification metadata

Pending
Overview
Eval results
Files

extensions.mddocs/

Extension System

Annotations for adding custom extensions and examples to Swagger specifications. The extension system allows you to add vendor-specific or custom metadata to any Swagger element, while example annotations provide sample data for better API documentation and testing.

Capabilities

@Extension Annotation

Defines custom extensions for Swagger specification elements. Extensions allow you to add vendor-specific or custom metadata that extends beyond the standard Swagger specification. All extension names are automatically prefixed with "x-" to follow OpenAPI conventions.

/**
 * Defines custom extensions for Swagger specification
 * Target: ANNOTATION_TYPE
 * Retention: RUNTIME
 */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Extension {
    /**
     * Name of the extension
     * Will be prefixed with "x-" automatically in the generated specification
     * Use descriptive names that indicate the extension's purpose
     */
    String name() default "";
    
    /**
     * Array of name/value pairs for the extension
     * Contains the actual extension data as key-value properties
     * REQUIRED ATTRIBUTE
     */
    ExtensionProperty[] properties();
}

Usage Examples:

// Simple extension with single property
@ApiOperation(
    value = "Get user profile",
    extensions = {
        @Extension(
            name = "code-samples",
            properties = {
                @ExtensionProperty(name = "javascript", value = "fetch('/users/123')")
            }
        )
    }
)
@GET
@Path("/{id}")
public User getUser(@PathParam("id") Long id) {
    // implementation
}

// Multiple extensions on model property
public class Product {
    @ApiModelProperty(
        value = "Product price in cents",
        example = "1299",
        extensions = {
            @Extension(
                name = "validation",
                properties = {
                    @ExtensionProperty(name = "min", value = "0"),
                    @ExtensionProperty(name = "max", value = "999999"),
                    @ExtensionProperty(name = "currency", value = "USD")
                }
            ),
            @Extension(
                name = "display",
                properties = {
                    @ExtensionProperty(name = "format", value = "currency"),
                    @ExtensionProperty(name = "symbol", value = "$")
                }
            )
        }
    )
    private Integer price;
}

// Extension with JSON parsing
@ApiOperation(
    value = "Create order",
    extensions = {
        @Extension(
            name = "workflow",
            properties = {
                @ExtensionProperty(
                    name = "steps", 
                    value = "[\"validate\", \"process\", \"fulfill\", \"notify\"]",
                    parseValue = true  // Parse as JSON array
                )
            }
        )
    }
)
@POST
public Order createOrder(CreateOrderRequest request) {
    // implementation
}

// Vendor-specific extensions
@ApiModelProperty(
    value = "Customer ID",
    extensions = {
        @Extension(
            name = "amazon-integration",
            properties = {
                @ExtensionProperty(name = "marketplace-id", value = "A1PA6795UKMFR9"),
                @ExtensionProperty(name = "seller-id", value = "A2EXAMPLE123")
            }
        ),
        @Extension(
            name = "analytics",
            properties = {
                @ExtensionProperty(name = "track-conversions", value = "true"),
                @ExtensionProperty(name = "segment", value = "premium-customers")
            }
        )
    }
)
private String customerId;

@ExtensionProperty Annotation

Defines name/value pairs within extensions. Each property represents a key-value pair of metadata that gets added to the extension object in the generated Swagger specification.

/**
 * Defines name/value pairs within extensions
 * Target: ANNOTATION_TYPE
 * Retention: RUNTIME
 */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ExtensionProperty {
    /**
     * Property name within the extension
     * Used as the key in the extension object
     * REQUIRED ATTRIBUTE
     */
    String name();
    
    /**
     * Property value within the extension
     * Can be any string value or JSON when parseValue is true
     * REQUIRED ATTRIBUTE
     */
    String value();
    
    /**
     * Whether to parse the value as JSON/YAML
     * When true, the value string is parsed as structured data
     * When false (default), the value is treated as a literal string
     */
    boolean parseValue() default false;
}

Usage Examples:

// String properties
@Extension(
    name = "security",
    properties = {
        @ExtensionProperty(name = "classification", value = "confidential"),
        @ExtensionProperty(name = "data-owner", value = "finance-team"),
        @ExtensionProperty(name = "retention-period", value = "7-years")
    }
)

// Numeric and boolean properties  
@Extension(
    name = "rate-limiting",
    properties = {
        @ExtensionProperty(name = "requests-per-minute", value = "100"),
        @ExtensionProperty(name = "burst-limit", value = "20"), 
        @ExtensionProperty(name = "enabled", value = "true")
    }
)

// JSON object properties
@Extension(
    name = "client-config",
    properties = {
        @ExtensionProperty(
            name = "timeout-settings",
            value = "{\"connect\": 5000, \"read\": 30000, \"total\": 35000}",
            parseValue = true
        ),
        @ExtensionProperty(
            name = "retry-policy", 
            value = "{\"maxAttempts\": 3, \"backoffMs\": 1000, \"exponential\": true}",
            parseValue = true
        )
    }
)

// Array properties
@Extension(
    name = "allowed-origins",
    properties = {
        @ExtensionProperty(
            name = "domains",
            value = "[\"https://app.example.com\", \"https://admin.example.com\"]", 
            parseValue = true
        )
    }
)

// Complex nested structure
@Extension(
    name = "monitoring",
    properties = {
        @ExtensionProperty(
            name = "metrics",
            value = "{" +
                "\"enabled\": true, " +
                "\"collectors\": [\"prometheus\", \"datadog\"], " +
                "\"sampling\": {\"rate\": 0.1, \"max-traces\": 1000}" +
            "}",
            parseValue = true
        )
    }
)

@Example Annotation

Container for multiple example properties, used to provide sample data for request/response bodies. Examples help API consumers understand the expected data format and structure.

/**
 * Container for multiple example properties
 * Target: ANNOTATION_TYPE
 * Retention: RUNTIME
 */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Example {
    /**
     * Array of example properties
     * Each property defines an example for a specific media type
     * REQUIRED ATTRIBUTE
     */
    ExampleProperty[] value();
}

Usage Examples:

// Examples for request body
@ApiOperation(value = "Create user account")
@ApiParam(
    value = "User registration data",
    examples = @Example({
        @ExampleProperty(
            mediaType = "application/json",
            value = "{" +
                "\"username\": \"johndoe\"," +
                "\"email\": \"john@example.com\"," +
                "\"firstName\": \"John\"," +
                "\"lastName\": \"Doe\"" +
            "}"
        ),
        @ExampleProperty(
            mediaType = "application/xml", 
            value = "<user>" +
                "<username>johndoe</username>" +
                "<email>john@example.com</email>" +
                "<firstName>John</firstName>" + 
                "<lastName>Doe</lastName>" +
            "</user>"
        )
    })
)
@POST
public User createUser(UserRegistrationRequest request) {
    // implementation
}

// Response examples
@ApiResponse(
    code = 200,
    message = "Product details retrieved successfully",
    response = Product.class,
    examples = @Example({
        @ExampleProperty(
            mediaType = "application/json",
            value = "{" +
                "\"id\": 12345," +
                "\"name\": \"Wireless Headphones\"," +
                "\"description\": \"High-quality wireless headphones with noise cancellation\"," +
                "\"price\": 199.99," +
                "\"category\": \"Electronics\"," +
                "\"inStock\": true," +
                "\"tags\": [\"audio\", \"wireless\", \"noise-cancelling\"]" +
            "}"
        )
    })
)
@GET 
@Path("/{id}")
public Product getProduct(@PathParam("id") Long id) {
    // implementation
}

// Multiple examples for different scenarios
@ApiParam(
    value = "Order data",
    examples = @Example({
        // Minimal order example
        @ExampleProperty(
            mediaType = "application/json",
            value = "{" +
                "\"customerId\": \"cust_123\"," +
                "\"items\": [{\"productId\": \"prod_456\", \"quantity\": 1}]" +
            "}"
        ),
        // Complete order example with all fields
        @ExampleProperty(
            mediaType = "application/json", 
            value = "{" +
                "\"customerId\": \"cust_123\"," +
                "\"items\": [" +
                    "{\"productId\": \"prod_456\", \"quantity\": 2, \"price\": 29.99}," +
                    "{\"productId\": \"prod_789\", \"quantity\": 1, \"price\": 15.50}" +
                "]," +
                "\"shippingAddress\": {" +
                    "\"street\": \"123 Main St\"," +
                    "\"city\": \"Anytown\"," +
                    "\"zipCode\": \"12345\"," +
                    "\"country\": \"US\"" +
                "}," +
                "\"paymentMethod\": \"credit_card\"," +
                "\"notes\": \"Please leave at front door\"" +
            "}"
        )
    })
)
@POST
@Path("/orders") 
public Order createOrder(CreateOrderRequest request) {
    // implementation
}

@ExampleProperty Annotation

Defines media type and example value pairs. Each property associates a specific example with a media type, allowing different examples for different content types.

/**
 * Defines media type and example value pairs
 * Target: ANNOTATION_TYPE
 * Retention: RUNTIME
 */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ExampleProperty {
    /**
     * Media type for this example
     * Specifies the content type this example applies to
     * Common values: "application/json", "application/xml", "text/plain"
     */
    String mediaType() default "";
    
    /**
     * Example value for the specified media type
     * Should be a valid representation in the specified media type format
     * REQUIRED ATTRIBUTE
     */
    String value();
}

Usage Examples:

// JSON examples
@ExampleProperty(
    mediaType = "application/json",
    value = "{\"message\": \"Hello, World!\"}"
)

// XML examples
@ExampleProperty(
    mediaType = "application/xml",
    value = "<message>Hello, World!</message>"
)

// Plain text examples
@ExampleProperty(
    mediaType = "text/plain", 
    value = "Hello, World!"
)

// CSV examples
@ExampleProperty(
    mediaType = "text/csv",
    value = "id,name,email\n1,John Doe,john@example.com\n2,Jane Smith,jane@example.com"
)

// Complex JSON structures
@ExampleProperty(
    mediaType = "application/json",
    value = "{" +
        "\"user\": {" +
            "\"id\": 123," +
            "\"profile\": {" +
                "\"name\": \"John Doe\"," +
                "\"preferences\": {" +
                    "\"theme\": \"dark\"," +
                    "\"notifications\": true," +
                    "\"languages\": [\"en\", \"es\"]" +
                "}" +
            "}" +
        "}," +
        "\"metadata\": {" +
            "\"created\": \"2023-01-15T10:30:00Z\"," +
            "\"lastUpdated\": \"2023-12-01T14:22:33Z\"" +
        "}" +
    "}"
)

// Error response examples
@ExampleProperty(
    mediaType = "application/json",
    value = "{" +
        "\"error\": {" +
            "\"code\": \"VALIDATION_FAILED\"," +
            "\"message\": \"The request contains invalid data\"," +
            "\"details\": [" +
                "{\"field\": \"email\", \"message\": \"Invalid email format\"}," +
                "{\"field\": \"age\", \"message\": \"Age must be between 13 and 120\"}" +
            "]" +
        "}" +
    "}"
)

Complete Integration Example

Here's a comprehensive example showing how all extension and example annotations work together:

@Api(tags = "orders")
@Path("/orders")
public class OrderController {

    @ApiOperation(
        value = "Create a new order",
        notes = "Creates a new order with the provided details. Supports both simple and complex order structures.",
        response = Order.class,
        code = 201,
        extensions = {
            @Extension(
                name = "workflow",
                properties = {
                    @ExtensionProperty(name = "type", value = "async"),
                    @ExtensionProperty(name = "estimated-duration", value = "2-5 seconds"),
                    @ExtensionProperty(
                        name = "steps",
                        value = "[\"validate\", \"inventory-check\", \"payment-process\", \"fulfill\"]",
                        parseValue = true
                    )
                }
            ),
            @Extension(
                name = "rate-limiting",
                properties = {
                    @ExtensionProperty(name = "per-minute", value = "10"),
                    @ExtensionProperty(name = "per-hour", value = "100"),
                    @ExtensionProperty(name = "burst-allowance", value = "5")
                }
            )
        }
    )
    @ApiResponses({
        @ApiResponse(
            code = 201,
            message = "Order created successfully",
            response = Order.class,
            examples = @Example({
                @ExampleProperty(
                    mediaType = "application/json",
                    value = "{" +
                        "\"id\": \"ord_1234567890\"," +
                        "\"status\": \"pending\"," +
                        "\"customerId\": \"cust_123\"," +
                        "\"total\": 75.48," +
                        "\"currency\": \"USD\"," +
                        "\"createdAt\": \"2023-12-01T10:30:00Z\"," +
                        "\"items\": [" +
                            "{\"productId\": \"prod_456\", \"quantity\": 2, \"unitPrice\": 29.99}," +
                            "{\"productId\": \"prod_789\", \"quantity\": 1, \"unitPrice\": 15.50}" +
                        "]" +
                    "}"
                )
            })
        ),
        @ApiResponse(
            code = 400,
            message = "Invalid order data",
            examples = @Example({
                @ExampleProperty(
                    mediaType = "application/json",
                    value = "{" +
                        "\"error\": {" +
                            "\"code\": \"INVALID_ORDER_DATA\"," +
                            "\"message\": \"The order contains invalid information\"," +
                            "\"details\": [" +
                                "{\"field\": \"customerId\", \"message\": \"Customer ID is required\"}," +
                                "{\"field\": \"items[0].quantity\", \"message\": \"Quantity must be positive\"}" +
                            "]" +
                        "}" +
                    "}"
                )
            })
        )
    })
    @POST
    public Response createOrder(
        @ApiParam(
            value = "Order creation request",
            required = true,
            examples = @Example({
                // Simple order example
                @ExampleProperty(
                    mediaType = "application/json",
                    value = "{" +
                        "\"customerId\": \"cust_123\"," +
                        "\"items\": [" +
                            "{\"productId\": \"prod_456\", \"quantity\": 1}" +
                        "]" +
                    "}"
                ),
                // Complex order example
                @ExampleProperty(
                    mediaType = "application/json",
                    value = "{" +
                        "\"customerId\": \"cust_123\"," +
                        "\"items\": [" +
                            "{\"productId\": \"prod_456\", \"quantity\": 2}," +
                            "{\"productId\": \"prod_789\", \"quantity\": 1}" +
                        "]," +
                        "\"shippingAddress\": {" +
                            "\"name\": \"John Doe\"," +
                            "\"street\": \"123 Main St\"," +
                            "\"city\": \"Springfield\"," +
                            "\"state\": \"IL\"," +
                            "\"zipCode\": \"62701\"," +
                            "\"country\": \"US\"" +
                        "}," +
                        "\"billingAddress\": {" +
                            "\"name\": \"John Doe\"," +
                            "\"street\": \"123 Main St\"," +
                            "\"city\": \"Springfield\"," +
                            "\"state\": \"IL\"," +
                            "\"zipCode\": \"62701\"," +
                            "\"country\": \"US\"" +
                        "}," +
                        "\"paymentMethod\": {" +
                            "\"type\": \"credit_card\"," +
                            "\"cardToken\": \"tok_1234567890\"" +
                        "}," +
                        "\"specialInstructions\": \"Please call before delivery\"," +
                        "\"giftMessage\": \"Happy Birthday!\"," +
                        "\"expedited\": false" +
                    "}"
                ),
                // XML format example
                @ExampleProperty(
                    mediaType = "application/xml",
                    value = "<order>" +
                        "<customerId>cust_123</customerId>" +
                        "<items>" +
                            "<item>" +
                                "<productId>prod_456</productId>" +
                                "<quantity>1</quantity>" +
                            "</item>" +
                        "</items>" +
                    "</order>"
                )
            })
        )
        CreateOrderRequest orderRequest
    ) {
        // implementation
        return Response.status(201).build();
    }
}

// Model with extensions and examples
@ApiModel(description = "Order creation request")
public class CreateOrderRequest {
    
    @ApiModelProperty(
        value = "Customer identifier",
        required = true,
        example = "cust_123",
        extensions = {
            @Extension(
                name = "validation",
                properties = {
                    @ExtensionProperty(name = "pattern", value = "^cust_[a-zA-Z0-9]{3,20}$"),
                    @ExtensionProperty(name = "required", value = "true")
                }
            ),
            @Extension(
                name = "database",
                properties = {
                    @ExtensionProperty(name = "table", value = "customers"),
                    @ExtensionProperty(name = "foreign-key", value = "id")
                }
            )
        }
    )
    private String customerId;
    
    @ApiModelProperty(
        value = "List of items to order",
        required = true,
        extensions = {
            @Extension(
                name = "constraints",
                properties = {
                    @ExtensionProperty(name = "min-items", value = "1"),
                    @ExtensionProperty(name = "max-items", value = "50")
                }
            )
        }
    )
    private List<OrderItem> items;
    
    // getters and setters
}

Best Practices

  1. Use meaningful extension names - Choose descriptive names that clearly indicate the extension's purpose
  2. Follow naming conventions - Use lowercase with hyphens for consistency (e.g., "rate-limiting", "custom-validation")
  3. Leverage parseValue for structured data - Use JSON parsing for complex configuration or nested objects
  4. Provide examples for all media types - Include examples for each supported content type (JSON, XML, etc.)
  5. Keep examples realistic - Use representative data that reflects actual API usage
  6. Document extension purposes - Include comments explaining what each extension accomplishes
  7. Validate extension values - Ensure extension values are valid for their intended use
  8. Use extensions sparingly - Only add extensions when they provide genuine value to API consumers
  9. Maintain consistent structure - Use similar patterns across related extensions
  10. Include error examples - Provide examples of error responses to help with error handling
  11. Version your extensions - Consider versioning when extensions change over time
  12. Test generated specifications - Verify that extensions appear correctly in the generated Swagger/OpenAPI spec

Install with Tessl CLI

npx tessl i tessl/maven-io-swagger--swagger-annotations

docs

core-api.md

documentation.md

extensions.md

index.md

models.md

parameters.md

responses.md

security.md

tile.json