yifan-c commented on code in PR #239:
URL: https://github.com/apache/cassandra-sidecar/pull/239#discussion_r2252811145


##########
server/src/main/java/org/apache/cassandra/sidecar/cluster/locator/CachedLocalTokenRanges.java:
##########


Review Comment:
   Please revert the change. It is unrelated to the patch. 
   
   ```
   git checkout upstream/trunk -- 
server/src/main/java/org/apache/cassandra/sidecar/cluster/locator/CachedLocalTokenRanges.java
   ```
   
   I reverted the change and `gw :server:clean :server:spotbugsMain` passes. 



##########
server/src/main/java/org/apache/cassandra/sidecar/handlers/OpenApiHandler.java:
##########
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.handlers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.swagger.v3.core.util.Json;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.cassandra.sidecar.config.OpenApiConfiguration;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * Handler that serves the OpenAPI specification
+ * 
+ * This handler serves the OpenAPI specification generated by the Gradle 
openApiGenerate task
+ * which processes JAX-RS/MicroProfile annotations to create comprehensive API 
documentation.
+ */
+@Singleton
+public class OpenApiHandler implements Handler<RoutingContext>
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(OpenApiHandler.class);
+    private final OpenApiConfiguration openApiConfig;
+    
+    // Cache for both JSON and YAML specifications
+    private volatile String cachedJsonSpec;
+    private volatile String cachedYamlSpec;
+    private volatile long lastModified;

Review Comment:
   I do not think we would modify the spec at the runtime. Can you remove this 
field and it should reduce the implementation complexity from assuming spec can 
change. 



##########
server/build.gradle:
##########
@@ -201,6 +225,12 @@ test {
 apply from: "${project.rootDir}/gradle/common/integrationTestTask.gradle"
 apply from: "${project.rootDir}/gradle/common/java11Options.gradle"
 
+tasks.withType(JavaCompile) {
+    doFirst {
+        println "AnnotationProcessorPath for $name is 
${options.getAnnotationProcessorPath().getFiles()}"
+    }
+}
+

Review Comment:
   This can be removed. I added it for debugging. Forgot to remove



##########
OPENAPI.md:
##########


Review Comment:
   The doc.. reads too AI. 
   
   I think it only needs to cover the following points
   - what technologies are used for OpenAPI generation (i.e. the annotations 
and the plugin)
   - how to generate the openAPI spec
   - how to write the annotations in order to generate the API in the spec
   - what endpoints to access the specs (endpoints, e.g. /openapi.yaml)
   
   The rest like "architecture", client generation, migration guide are either 
unnecessary or wrong. They should be removed. Let's keep the doc succinct. 



##########
server/src/main/java/org/apache/cassandra/sidecar/handlers/OpenApiHandler.java:
##########
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.handlers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.swagger.v3.core.util.Json;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.cassandra.sidecar.config.OpenApiConfiguration;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * Handler that serves the OpenAPI specification
+ * 
+ * This handler serves the OpenAPI specification generated by the Gradle 
openApiGenerate task
+ * which processes JAX-RS/MicroProfile annotations to create comprehensive API 
documentation.
+ */
+@Singleton
+public class OpenApiHandler implements Handler<RoutingContext>
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(OpenApiHandler.class);
+    private final OpenApiConfiguration openApiConfig;
+    
+    // Cache for both JSON and YAML specifications
+    private volatile String cachedJsonSpec;
+    private volatile String cachedYamlSpec;
+    private volatile long lastModified;
+
+    @Inject
+    public OpenApiHandler(SidecarConfiguration sidecarConfiguration)
+    {
+        this.openApiConfig = sidecarConfiguration.openApiConfiguration();
+    }
+
+    @Override
+    public void handle(RoutingContext context)
+    {
+        try
+        {
+            // Determine format from request path or Accept header
+            boolean isYaml = isYamlRequest(context);
+            String openApiContent = loadGeneratedOpenApiSpec(isYaml);
+            String contentType = isYaml ? "application/yaml" : 
"application/json";
+            
+            context.response()
+                   .putHeader("Content-Type", contentType)
+                   .end(openApiContent);
+        }
+        catch (Exception e)
+        {
+            logger.warn("Failed to load generated OpenAPI specification, 
falling back to basic config", e);
+            
+            // Fallback to basic OpenAPI config if generated file is not 
available
+            OpenAPI openAPI = createOpenApiFromConfig(openApiConfig);
+            String openApiJson = Json.pretty(openAPI);
+            
+            context.response()
+                   .putHeader("Content-Type", "application/json")
+                   .end(openApiJson);
+        }
+    }
+
+    /**
+     * Determines if the request is for YAML format based on path or Accept 
header
+     */
+    private boolean isYamlRequest(RoutingContext context)
+    {
+        String path = context.request().path();
+        if (path != null && path.endsWith(".yaml"))
+        {
+            return true;
+        }
+        
+        String acceptHeader = context.request().getHeader("Accept");
+        return acceptHeader != null && 
acceptHeader.contains("application/yaml");
+    }
+    
+    /**
+     * Loads the generated OpenAPI specification from resources or build 
output with caching
+     */
+    private String loadGeneratedOpenApiSpec(boolean isYaml) throws IOException
+    {
+        // First try to load from classpath resources (for packaged 
deployments)
+        String cached = isYaml ? cachedYamlSpec : cachedJsonSpec;
+        if (cached == null)
+        {
+            cached = loadFromClasspathResource(isYaml);
+            if (isYaml)
+            {
+                cachedYamlSpec = cached;
+            }
+            else
+            {
+                cachedJsonSpec = cached;
+            }
+        }
+        
+        if (cached != null)
+        {
+            return cached;
+        }
+
+        // Fallback to file system paths (for development)
+        return loadFromFileSystem(isYaml);
+    }
+
+    /**
+     * Attempts to load the OpenAPI spec from classpath resources
+     */
+    private String loadFromClasspathResource(boolean isYaml) throws IOException
+    {
+        String fileName = isYaml ? "openapi.yaml" : "openapi.json";
+        String resourcePath = "/openapi/" + fileName;
+        InputStream inputStream = getClass().getResourceAsStream(resourcePath);
+        if (inputStream == null)
+        {
+            return null;
+        }
+        
+        try
+        {
+            logger.debug("Loading OpenAPI specification from classpath: {}", 
resourcePath);
+            String content = new String(inputStream.readAllBytes(), 
StandardCharsets.UTF_8);
+            logger.info("Loaded OpenAPI specification from classpath 
resource");
+            return content;
+        }
+        finally
+        {
+            inputStream.close();
+        }
+    }
+
+    /**
+     * Attempts to load the OpenAPI spec from file system (development mode)
+     */
+    private String loadFromFileSystem(boolean isYaml) throws IOException
+    {
+        String fileName = isYaml ? "openapi.yaml" : "openapi.json";
+        String[] possiblePaths = {
+            "server/build/generated/openapi/" + fileName,    // From project 
root
+            "build/generated/openapi/" + fileName,           // From server 
directory
+            "../server/build/generated/openapi/" + fileName, // Alternative 
relative path
+        };
+        
+        Path foundPath = null;
+        for (String pathStr : possiblePaths)
+        {
+            Path path = Paths.get(pathStr);
+            if (Files.exists(path))
+            {
+                foundPath = path;
+                break;
+            }
+        }
+        
+        if (foundPath == null)
+        {
+            throw new IOException("Generated OpenAPI specification not found 
in classpath resources or file system. " +
+                                "Please run './gradlew openApiGenerate' and 
ensure the generated file is included in the build.");
+        }
+        
+        // Check if we need to reload the file (if it's newer than our cached 
version)
+        long fileLastModified = 
Files.getLastModifiedTime(foundPath).toMillis();
+        String cached = isYaml ? cachedYamlSpec : cachedJsonSpec;
+        if (cached == null || fileLastModified > lastModified)
+        {
+            logger.debug("Loading OpenAPI specification from: {}", 
foundPath.toAbsolutePath());
+            String content = Files.readString(foundPath, 
StandardCharsets.UTF_8);
+            lastModified = fileLastModified;
+            logger.info("Loaded OpenAPI specification from {}", 
foundPath.toAbsolutePath());
+            
+            // Update the appropriate cache
+            if (isYaml)
+            {
+                cachedYamlSpec = content;
+            }
+            else
+            {
+                cachedJsonSpec = content;
+            }
+            
+            return content;
+        }
+        
+        return cached;
+    }
+    
+    /**
+     * Creates a basic OpenAPI configuration as fallback when generated spec 
is not available
+     */
+    private static OpenAPI createOpenApiFromConfig(OpenApiConfiguration config)
+    {
+        OpenAPI openApi = new OpenAPI();
+        
+        // Set basic info
+        io.swagger.v3.oas.models.info.Info info = new 
io.swagger.v3.oas.models.info.Info();
+        info.setTitle(config.title());
+        info.setDescription(config.description() + " (Generated specification 
not available - run './gradlew openApiGenerate')");
+        info.setVersion(config.version());
+        
+        // Set license info
+        io.swagger.v3.oas.models.info.License license = new 
io.swagger.v3.oas.models.info.License();
+        license.setName(config.licenseName());
+        license.setUrl(config.licenseUrl());
+        info.setLicense(license);
+        
+        openApi.setInfo(info);
+        
+        // Set server info
+        io.swagger.v3.oas.models.servers.Server server = new 
io.swagger.v3.oas.models.servers.Server();
+        server.setUrl(config.serverUrl());
+        server.setDescription(config.serverDescription());
+        openApi.setServers(Collections.singletonList(server));
+        
+        return openApi;
+    }

Review Comment:
   There is no use of this fallback. Can we just return a 404 if no spec is 
available? 



##########
server/build.gradle:
##########
@@ -240,3 +273,22 @@ checkstyleContainerTest.onlyIf { "true" != 
System.getenv("skipContainerTest") }
 spotbugsContainerTest.onlyIf { "true" != System.getenv("skipContainerTest") }
 
 check.dependsOn containerTest, integrationTest, jacocoTestReport
+
+// Configure OpenAPI generation to work with packaged deployments
+// Copy generated OpenAPI spec to resources build directory so it's available 
in the JAR
+tasks.register('copyOpenApiSpec', Copy) {
+    dependsOn generateOpenApiSpec
+    from layout.buildDirectory.dir('generated/openapi')
+    include 'openapi.json', 'openapi.yaml'
+    into layout.buildDirectory.dir('resources/main/openapi')
+    doFirst {
+        mkdir layout.buildDirectory.dir('resources/main/openapi')
+    }
+}
+
+// Ensure the OpenAPI spec is copied before building the JAR
+processResources.dependsOn copyOpenApiSpec
+
+// OpenAPI generation now uses standard JAX-RS/MicroProfile annotations
+// and can be generated using external tools like the swagger-codegen plugin  
+// or the official MicroProfile OpenAPI Maven/Gradle plugins

Review Comment:
   This comment can be removed. 
   
   In the git history of trunk, the openAPI spec generation is new. There is no 
prior implementation, which makes the comment useless. 



##########
server/build.gradle:
##########
@@ -232,6 +262,9 @@ jib {
     to {
         image = "cassandra-sidecar:${project.version}"
     }
+    container {
+        mainClass = 'org.apache.cassandra.sidecar.CassandraSidecarDaemon'
+    }

Review Comment:
   I am not sure why this is needed. 
   To run the application, the `run` task is defined in `build.gradle` at the 
project root already. 



##########
server/src/main/java/org/apache/cassandra/sidecar/modules/OpenApiModule.java:
##########
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.modules;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.ProvidesIntoMap;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import org.apache.cassandra.sidecar.common.ApiEndpointsV1;
+import org.apache.cassandra.sidecar.handlers.OpenApiHandler;
+import org.apache.cassandra.sidecar.handlers.SwaggerUIHandler;
+import org.apache.cassandra.sidecar.modules.multibindings.KeyClassMapKey;
+import org.apache.cassandra.sidecar.modules.multibindings.VertxRouteMapKeys;
+import org.apache.cassandra.sidecar.routes.RouteBuilder;
+import org.apache.cassandra.sidecar.routes.VertxRoute;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
+import org.eclipse.microprofile.openapi.annotations.media.Content;
+import org.eclipse.microprofile.openapi.annotations.media.Schema;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+
+/**
+ * Module for OpenAPI documentation
+ */
+@Path("/")
+public class OpenApiModule extends AbstractModule
+{
+    @GET
+    @Path(ApiEndpointsV1.OPENAPI_ROUTE)
+    @Operation(summary = "Get OpenAPI specification",
+               description = "Returns the OpenAPI specification for the 
Cassandra Sidecar API")
+    @APIResponse(description = "OpenAPI specification retrieved successfully",
+                 responseCode = "200",
+                 content = @Content(mediaType = "application/json",
+                 schema = @Schema(type = SchemaType.OBJECT)))
+    @ProvidesIntoMap
+    @KeyClassMapKey(VertxRouteMapKeys.OpenApiRouteKey.class)
+    VertxRoute openApiRoute(RouteBuilder.Factory factory, OpenApiHandler 
openApiHandler)
+    {
+        return factory.builderForUnauthorizedRoute()

Review Comment:
   Should the openapi spec be placed behind the unauthorized route? I would 
like to hear from @sarankk and @frankgh. 
   I guess one argument of not having auth is that client can easily get the 
spec from the github.  



##########
server/build.gradle:
##########
@@ -240,3 +273,22 @@ checkstyleContainerTest.onlyIf { "true" != 
System.getenv("skipContainerTest") }
 spotbugsContainerTest.onlyIf { "true" != System.getenv("skipContainerTest") }
 
 check.dependsOn containerTest, integrationTest, jacocoTestReport
+
+// Configure OpenAPI generation to work with packaged deployments
+// Copy generated OpenAPI spec to resources build directory so it's available 
in the JAR
+tasks.register('copyOpenApiSpec', Copy) {
+    dependsOn generateOpenApiSpec
+    from layout.buildDirectory.dir('generated/openapi')
+    include 'openapi.json', 'openapi.yaml'
+    into layout.buildDirectory.dir('resources/main/openapi')
+    doFirst {
+        mkdir layout.buildDirectory.dir('resources/main/openapi')
+    }
+}

Review Comment:
   I think you can configure `outputDirectory` for the `generateOpenApiSpec` 
task. Then, you can remove this task. It is not very well documented. You can 
find all properties here 
https://github.com/smallrye/smallrye-open-api/tree/main/tools/maven-plugin



##########
server/src/main/java/org/apache/cassandra/sidecar/modules/multibindings/VertxRouteMapKeys.java:
##########
@@ -192,6 +193,21 @@ interface ListSnapshotRouteKey extends RouteClassKey
         HttpMethod HTTP_METHOD = HttpMethod.GET;
         String ROUTE_URI = ApiEndpointsV1.SNAPSHOTS_ROUTE;
     }
+    interface OpenApiRouteKey extends RouteClassKey
+    {
+        HttpMethod HTTP_METHOD = HttpMethod.GET;
+        String ROUTE_URI = ApiEndpointsV1.OPENAPI_ROUTE;
+    }
+    interface OpenApiYamlRouteKey extends RouteClassKey
+    {
+        HttpMethod HTTP_METHOD = HttpMethod.GET;
+        String ROUTE_URI = ApiEndpointsV1.OPENAPI_YAML_ROUTE;
+    }
+    interface SwaggerUIRouteKey extends RouteClassKey

Review Comment:
   `OpenApiHtmlRouteKey`?



##########
client-common/src/main/java/org/apache/cassandra/sidecar/common/ApiEndpointsV1.java:
##########
@@ -157,6 +157,10 @@ public final class ApiEndpointsV1
     public static final String LIVE_MIGRATION_FILE_TRANSFER_API = 
LIVE_MIGRATION_FILES_API + "/:" + DIR_TYPE_PARAM
                                                                   + "/:" + 
DIR_INDEX_PARAM + "/*";
 
+    public static final String OPENAPI_ROUTE = API_V1 + "/openapi.json";
+    public static final String OPENAPI_YAML_ROUTE = API_V1 + "/openapi.yaml";
+    public static final String SWAGGER_UI_ROUTE = API_V1 + "/docs";

Review Comment:
   The data being served from those endpoints can contain multiple api 
versions. It would make better sense to have it at the root level. For example, 
`localhost:9043/openapi{.type}`.
   
   Nit: instead of `docs`, how about `openapi.html`?



##########
OPENAPI.md:
##########
@@ -0,0 +1,173 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+# OpenAPI Documentation
+
+This project uses standard JAX-RS and MicroProfile OpenAPI annotations to 
generate comprehensive API documentation for the Cassandra Sidecar.
+
+## Architecture Overview
+
+### Standard Annotations Approach
+The Cassandra Sidecar uses industry-standard annotations for OpenAPI 
documentation generation:
+
+- **JAX-RS Annotations**: `@GET`, `@POST`, `@PUT`, `@DELETE`, `@Path` - Define 
HTTP methods and routes
+- **MicroProfile OpenAPI Annotations**: `@Operation`, `@APIResponse`, 
`@Schema` - Add comprehensive API documentation
+- **SmallRye OpenAPI Plugin**: Gradle plugin that processes annotations to 
generate OpenAPI specifications

Review Comment:
   please add links to the annotations and the plugin



##########
server/src/main/java/org/apache/cassandra/sidecar/handlers/OpenApiHandler.java:
##########
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.handlers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.swagger.v3.core.util.Json;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.cassandra.sidecar.config.OpenApiConfiguration;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * Handler that serves the OpenAPI specification
+ * 
+ * This handler serves the OpenAPI specification generated by the Gradle 
openApiGenerate task
+ * which processes JAX-RS/MicroProfile annotations to create comprehensive API 
documentation.
+ */
+@Singleton
+public class OpenApiHandler implements Handler<RoutingContext>
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(OpenApiHandler.class);
+    private final OpenApiConfiguration openApiConfig;
+    
+    // Cache for both JSON and YAML specifications
+    private volatile String cachedJsonSpec;
+    private volatile String cachedYamlSpec;
+    private volatile long lastModified;
+
+    @Inject
+    public OpenApiHandler(SidecarConfiguration sidecarConfiguration)
+    {
+        this.openApiConfig = sidecarConfiguration.openApiConfiguration();
+    }
+
+    @Override
+    public void handle(RoutingContext context)
+    {
+        try
+        {
+            // Determine format from request path or Accept header
+            boolean isYaml = isYamlRequest(context);
+            String openApiContent = loadGeneratedOpenApiSpec(isYaml);
+            String contentType = isYaml ? "application/yaml" : 
"application/json";
+            
+            context.response()
+                   .putHeader("Content-Type", contentType)
+                   .end(openApiContent);
+        }
+        catch (Exception e)
+        {
+            logger.warn("Failed to load generated OpenAPI specification, 
falling back to basic config", e);
+            
+            // Fallback to basic OpenAPI config if generated file is not 
available
+            OpenAPI openAPI = createOpenApiFromConfig(openApiConfig);
+            String openApiJson = Json.pretty(openAPI);
+            
+            context.response()
+                   .putHeader("Content-Type", "application/json")
+                   .end(openApiJson);
+        }
+    }
+
+    /**
+     * Determines if the request is for YAML format based on path or Accept 
header
+     */
+    private boolean isYamlRequest(RoutingContext context)
+    {
+        String path = context.request().path();
+        if (path != null && path.endsWith(".yaml"))
+        {
+            return true;
+        }
+        
+        String acceptHeader = context.request().getHeader("Accept");
+        return acceptHeader != null && 
acceptHeader.contains("application/yaml");
+    }
+    
+    /**
+     * Loads the generated OpenAPI specification from resources or build 
output with caching
+     */
+    private String loadGeneratedOpenApiSpec(boolean isYaml) throws IOException
+    {
+        // First try to load from classpath resources (for packaged 
deployments)
+        String cached = isYaml ? cachedYamlSpec : cachedJsonSpec;
+        if (cached == null)
+        {
+            cached = loadFromClasspathResource(isYaml);
+            if (isYaml)
+            {
+                cachedYamlSpec = cached;
+            }
+            else
+            {
+                cachedJsonSpec = cached;
+            }
+        }
+        
+        if (cached != null)
+        {
+            return cached;
+        }
+
+        // Fallback to file system paths (for development)
+        return loadFromFileSystem(isYaml);
+    }
+
+    /**
+     * Attempts to load the OpenAPI spec from classpath resources
+     */
+    private String loadFromClasspathResource(boolean isYaml) throws IOException
+    {
+        String fileName = isYaml ? "openapi.yaml" : "openapi.json";
+        String resourcePath = "/openapi/" + fileName;
+        InputStream inputStream = getClass().getResourceAsStream(resourcePath);
+        if (inputStream == null)
+        {
+            return null;
+        }
+        
+        try
+        {
+            logger.debug("Loading OpenAPI specification from classpath: {}", 
resourcePath);
+            String content = new String(inputStream.readAllBytes(), 
StandardCharsets.UTF_8);
+            logger.info("Loaded OpenAPI specification from classpath 
resource");
+            return content;
+        }
+        finally
+        {
+            inputStream.close();
+        }

Review Comment:
   nit: use try-with-resources. 
   
   
   ```suggestion
           try (InputStream inputStream = 
getClass().getResourceAsStream(resourcePath))
           {
               if (inputStream == null)
               {
                   return null;
               }
   
               logger.debug("Loading OpenAPI specification from classpath: {}", 
resourcePath);
               String content = new String(inputStream.readAllBytes(), 
StandardCharsets.UTF_8);
               logger.info("Loaded OpenAPI specification from classpath 
resource");
               return content;
           }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org
For additional commands, e-mail: pr-h...@cassandra.apache.org

Reply via email to