Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions code/chapter04/catalog/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<properties>

<!-- Setting the source and target of the Java Compiler -->
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
Expand All @@ -31,7 +31,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>

Expand All @@ -47,7 +47,7 @@
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>6.1</version>
<version>7.1</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
Expand Down Expand Up @@ -119,6 +119,16 @@
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Maven Compiler Plugin for Java 21 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>21</release>
</configuration>
</plugin>

<!-- Enable liberty-maven plugin -->
<plugin>
<groupId>io.openliberty.tools</groupId>
Expand Down Expand Up @@ -167,4 +177,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.microprofile.tutorial.store.product;

import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.OASModelReader;
import org.eclipse.microprofile.openapi.models.OpenAPI;

/**
* Custom OpenAPI model reader demonstrating jsonSchemaDialect support.
* This reader is called once during application startup to build/enhance the OpenAPI model.
*/
public class CustomModelReader implements OASModelReader {

@Override
public OpenAPI buildModel() {
// Create an OpenAPI object with jsonSchemaDialect
return OASFactory.createOpenAPI()
.openapi("3.1.0")
.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base")
.info(OASFactory.createInfo()
.title("Product API")
.version("1.0.0")
.description("API for managing products with MicroProfile OpenAPI 4.1"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.microprofile.tutorial.store.product;

import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.Operation;

/**
* OpenAPI filter demonstrating the use of getExtension() and hasExtension() methods.
* This filter is called for each element in the OpenAPI model tree.
*/
public class ExtensionFilter implements OASFilter {

@Override
public Operation filterOperation(Operation operation) {
// Check if a custom extension exists using the new hasExtension method
if (operation.hasExtension("x-custom-timeout")) {
// Retrieve the extension value using the new getExtension method
Object timeout = operation.getExtension("x-custom-timeout");
System.out.println("Custom timeout found: " + timeout);

// Modify based on the extension
if (timeout != null && timeout instanceof Integer && (Integer) timeout > 30) {
operation.addExtension("x-requires-approval", true);
}
}

// Check for rate limiting extension
if (operation.hasExtension("x-rate-limit")) {
Object rateLimit = operation.getExtension("x-rate-limit");
System.out.println("Rate limit: " + rateLimit);
}

return operation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.microprofile.tutorial.store.product;

import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition;
import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme;
import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes;
import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType;
import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeIn;
import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows;
import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow;
import org.eclipse.microprofile.openapi.annotations.security.OAuthScope;
import org.eclipse.microprofile.openapi.annotations.info.Info;
import org.eclipse.microprofile.openapi.annotations.info.Contact;
import org.eclipse.microprofile.openapi.annotations.info.License;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

/**
* Application class demonstrating security scheme definitions in MicroProfile OpenAPI 4.1.
* This shows how to document multiple security mechanisms for your API.
*/
@ApplicationPath("/api")
@OpenAPIDefinition(
info = @Info(
title = "Secured Product API",
version = "1.0.0",
description = "Product API with multiple security schemes demonstrating MicroProfile OpenAPI 4.1 capabilities",
contact = @Contact(
name = "API Support",
email = "support@example.com",
url = "https://example.com/support"
),
license = @License(
name = "Apache 2.0",
url = "https://www.apache.org/licenses/LICENSE-2.0.html"
)
)
)
@SecuritySchemes({
@SecurityScheme(
securitySchemeName = "apiKey",
type = SecuritySchemeType.APIKEY,
description = "API Key authentication - provide your API key in the X-API-Key header",
in = SecuritySchemeIn.HEADER,
apiKeyName = "X-API-Key"
),
@SecurityScheme(
securitySchemeName = "bearerAuth",
type = SecuritySchemeType.HTTP,
description = "JWT Bearer token authentication - obtain token from /auth/login endpoint",
scheme = "bearer",
bearerFormat = "JWT"
),
@SecurityScheme(
securitySchemeName = "oauth2",
type = SecuritySchemeType.OAUTH2,
description = "OAuth2 authentication with authorization code flow",
flows = @OAuthFlows(
authorizationCode = @OAuthFlow(
authorizationUrl = "https://example.com/oauth/authorize",
tokenUrl = "https://example.com/oauth/token",
refreshUrl = "https://example.com/oauth/refresh",
scopes = {
@OAuthScope(
name = "read:products",
description = "Read product information"
),
@OAuthScope(
name = "write:products",
description = "Create and modify product information"
),
@OAuthScope(
name = "delete:products",
description = "Delete product information"
)
}
)
)
),
@SecurityScheme(
securitySchemeName = "basicAuth",
type = SecuritySchemeType.HTTP,
description = "Basic HTTP authentication",
scheme = "basic"
)
})
public class SecuredProductApplication extends Application {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.microprofile.tutorial.store.product.entity;

import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* Request object for async product processing.
*/
@Schema(description = "Async product processing request")
public class AsyncRequest {

@Schema(description = "Product to process", required = true)
private Product product;

@Schema(description = "Callback URL to notify when processing completes",
example = "https://example.com/callback",
required = true)
private String callbackUrl;

public AsyncRequest() {
}

public AsyncRequest(Product product, String callbackUrl) {
this.product = product;
this.callbackUrl = callbackUrl;
}

public Product getProduct() {
return product;
}

public void setProduct(Product product) {
this.product = product;
}

public String getCallbackUrl() {
return callbackUrl;
}

public void setCallbackUrl(String callbackUrl) {
this.callbackUrl = callbackUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.microprofile.tutorial.store.product.entity;

import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.media.DependentRequired;

/**
* Product with conditional validation.
* Demonstrates the @DependentRequired annotation in MicroProfile OpenAPI 4.1.
*/
@Schema(
description = "Product with conditional validation",
dependentRequired = {
@DependentRequired(
name = "discount",
requires = {"discountReason"}
)
}
)
public class ConditionalProduct {

@Schema(description = "Product ID", example = "1")
private Long id;

@Schema(description = "Product name", example = "Laptop", required = true)
private String name;

@Schema(description = "Product price", example = "999.99", required = true)
private Double price;

@Schema(description = "Discount amount (requires discountReason if set)", example = "100.0")
private Double discount;

@Schema(description = "Reason for discount", example = "Black Friday Sale")
private String discountReason;

public ConditionalProduct() {
}

public ConditionalProduct(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}

// Getters and setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Double getPrice() {
return price;
}

public void setPrice(Double price) {
this.price = price;
}

public Double getDiscount() {
return discount;
}

public void setDiscount(Double discount) {
this.discount = discount;
}

public String getDiscountReason() {
return discountReason;
}

public void setDiscountReason(String discountReason) {
this.discountReason = discountReason;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.microprofile.tutorial.store.product.entity;

import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* Result object for async product processing.
*/
@Schema(description = "Result of async product processing")
public class ProcessResult {

@Schema(description = "ID of the processed product", example = "123")
private Long productId;

@Schema(description = "Processing status",
example = "COMPLETED",
enumeration = {"COMPLETED", "FAILED", "PENDING"})
private String status;

@Schema(description = "Processing message or error details",
example = "Product processed successfully")
private String message;

@Schema(description = "Timestamp when processing completed",
example = "2025-01-31T16:00:00Z")
private String timestamp;

public ProcessResult() {
}

public ProcessResult(Long productId, String status, String message) {
this.productId = productId;
this.status = status;
this.message = message;
this.timestamp = java.time.Instant.now().toString();
}

public Long getProductId() {
return productId;
}

public void setProductId(Long productId) {
this.productId = productId;
}

public String getStatus() {
return status;
}

public void setStatus(String status) {
this.status = status;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getTimestamp() {
return timestamp;
}

public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
Loading
Loading