Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJ
include(dependency("io.opencensus:.*"))
include(dependency("io.perfmark:.*"))
include(dependency("io.prometheus:.*"))
include(dependency("io.swagger:.*"))
include(dependency("io.swagger.core.v3:.*"))
include(dependency("jakarta.activation:jakarta.activation-api"))
include(dependency("jakarta.annotation:jakarta.annotation-api"))
include(dependency("jakarta.inject:jakarta.inject-api"))
Expand Down
4 changes: 1 addition & 3 deletions distribution/server/src/assemble/LICENSE.bin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,7 @@ The Apache Software License, Version 2.0
* J2ObjC Annotations -- com.google.j2objc-j2objc-annotations-1.3.jar
* Netty Reactive Streams -- com.typesafe.netty-netty-reactive-streams-2.0.6.jar
* Swagger
- io.swagger-swagger-annotations-1.6.2.jar
- io.swagger-swagger-core-1.6.2.jar
- io.swagger-swagger-models-1.6.2.jar
- io.swagger.core.v3-swagger-annotations-jakarta-2.2.50.jar
* slog -- io.github.merlimat.slog-slog-0.9.7.jar
* DataSketches
- com.yahoo.datasketches-memory-0.8.3.jar
Expand Down
2 changes: 1 addition & 1 deletion distribution/shell/src/assemble/LICENSE.bin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ The Apache Software License, Version 2.0
- listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
* J2ObjC Annotations -- j2objc-annotations-1.3.jar
* Netty Reactive Streams -- netty-reactive-streams-2.0.6.jar
* Swagger -- swagger-annotations-1.6.2.jar
* Swagger -- swagger-annotations-jakarta-2.2.50.jar
* DataSketches
- memory-0.8.3.jar
- sketches-core-0.8.3.jar
Expand Down
9 changes: 5 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ audience-annotations = "0.12.0"
# Misc
curator = "5.7.1"
reflections = "0.10.2"
# swagger stays 1.6.2 in Phase A (javax→jakarta core); migrated to Swagger Core 2.x (io.swagger.core.v3) in Phase B
swagger = "1.6.2"
# OpenAPI 3 annotations (io.swagger.core.v3, jakarta variant) used for REST API and config docs annotations
swagger = "2.2.50"
typetools = "0.5.0"
jna = "5.18.1"
java-semver = "0.9.0"
Expand Down Expand Up @@ -338,8 +338,8 @@ aircompressor = { module = "io.airlift:aircompressor", version.ref = "aircompres
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
re2j = { module = "com.google.re2j:re2j", version.ref = "re2j" }
completable-futures = { module = "com.spotify:completable-futures", version.ref = "completable-futures" }
swagger-annotations = { module = "io.swagger:swagger-annotations", version.ref = "swagger" }
swagger-core = { module = "io.swagger:swagger-core", version.ref = "swagger" }
swagger-annotations = { module = "io.swagger.core.v3:swagger-annotations-jakarta", version.ref = "swagger" }
swagger-jaxrs2 = { module = "io.swagger.core.v3:swagger-jaxrs2-jakarta", version.ref = "swagger" }
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
picocli-shell-jline3 = { module = "info.picocli:picocli-shell-jline3", version.ref = "picocli" }
jline = { module = "org.jline:jline", version.ref = "jline3" }
Expand Down Expand Up @@ -475,6 +475,7 @@ protobuf = "com.google.protobuf:0.9.6"
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
rat = "org.nosphere.apache.rat:0.8.1"
spotless = "com.diffplug.spotless:8.4.0"
swagger = { id = "io.swagger.core.v3.swagger-gradle-plugin", version.ref = "swagger" }
version-catalog-update = "nl.littlerobots.version-catalog-update:1.1.0"
versions = "com.github.ben-manes.versions:0.53.0"
crlf = "com.github.vlsi.crlf:3.0.1"
Expand Down
2 changes: 2 additions & 0 deletions pulsar-broker-auth-oidc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ dependencies {
implementation(libs.jackson.annotations)
implementation(libs.kubernetes.client.java) {
exclude(group = "software.amazon.awssdk")
// Swagger 1.x annotations on the generated k8s models are inert metadata; nothing reads them at runtime
exclude(group = "io.swagger", module = "swagger-annotations")
}
implementation(libs.okhttp3)
implementation(libs.commons.lang3)
Expand Down
99 changes: 97 additions & 2 deletions pulsar-broker/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ plugins {
id("pulsar.test-certs-conventions")
alias(libs.plugins.protobuf)
alias(libs.plugins.lightproto)
alias(libs.plugins.swagger)
}

dependencies {
Expand All @@ -38,7 +39,7 @@ dependencies {
implementation(project(":pulsar-client-messagecrypto-bc"))
implementation(project(":pulsar-functions:pulsar-functions-worker"))
implementation(project(":pulsar-docs-tools")) {
exclude(group = "io.swagger")
exclude(group = "io.swagger.core.v3")
}
implementation(project(":pulsar-package-management:pulsar-package-core"))
implementation(project(":pulsar-package-management:pulsar-package-filesystem-storage"))
Expand Down Expand Up @@ -104,7 +105,6 @@ dependencies {
implementation(project(":pulsar-functions:pulsar-functions-proto"))

compileOnly(libs.swagger.annotations)
compileOnly(libs.swagger.core)
compileOnly(libs.jsr305)

testImplementation(project(":testmocks"))
Expand Down Expand Up @@ -212,3 +212,98 @@ lightproto {
// TransactionPendingAck.proto imports PulsarApi.proto from pulsar-common
extraProtoPaths.from(rootProject.layout.projectDirectory)
}

// ── OpenAPI (Swagger) REST API documentation ────────────────────────────────
// Mirrors the Maven build's `swagger` profile (kongchen swagger-maven-plugin, Swagger 1.x),
// ported to the official Swagger Core v3 gradle plugin. Run on demand:
// ./gradlew :pulsar-broker:generateOpenApiSpecs (outputs to pulsar-broker/build/openapi/)
// The plugin's default `swaggerDeps` resolver dependencies target javax.ws.rs; declaring
// our own dependencies on the configuration replaces them with the jakarta variants.
dependencies {
"swaggerDeps"(libs.commons.lang3)
"swaggerDeps"(libs.swagger.jaxrs2)
"swaggerDeps"(libs.jakarta.ws.rs.api)
"swaggerDeps"(libs.jakarta.servlet.api)
}

fun registerSwaggerTask(
name: String,
fileName: String,
baseInfoFile: String,
configure: io.swagger.v3.plugins.gradle.tasks.ResolveTask.() -> Unit,
) = tasks.register<io.swagger.v3.plugins.gradle.tasks.ResolveTask>(name) {
group = "documentation"
description = "Generates $fileName.json OpenAPI documentation"
buildClasspath.setFrom(configurations["swaggerDeps"])
classpath.setFrom(sourceSets["main"].runtimeClasspath)
outputDir.set(layout.buildDirectory.dir("swagger/$name"))
outputFileName.set(fileName)
outputFormat.set(io.swagger.v3.plugins.gradle.tasks.ResolveTask.Format.JSON)
openApiFile.set(file("src/main/openapi/$baseInfoFile"))
prettyPrint.set(true)
sortOutput.set(true)
readAllResources.set(true)
configure()
}

registerSwaggerTask("swaggerAdminV2", "swagger", "admin-v2.json") {
resourceClasses.set(setOf(
"org.apache.pulsar.broker.admin.v2.Bookies",
"org.apache.pulsar.broker.admin.v2.BrokerStats",
"org.apache.pulsar.broker.admin.v2.Brokers",
"org.apache.pulsar.broker.admin.v2.Clusters",
"org.apache.pulsar.broker.admin.v2.Functions",
"org.apache.pulsar.broker.admin.v2.Namespaces",
"org.apache.pulsar.broker.admin.v2.NonPersistentTopics",
"org.apache.pulsar.broker.admin.v2.PersistentTopics",
"org.apache.pulsar.broker.admin.v2.ResourceGroups",
"org.apache.pulsar.broker.admin.v2.ResourceQuotas",
"org.apache.pulsar.broker.admin.v2.SchemasResource",
"org.apache.pulsar.broker.admin.v2.Tenants",
"org.apache.pulsar.broker.admin.v2.Worker",
"org.apache.pulsar.broker.admin.v2.WorkerStats",
))
}

registerSwaggerTask("swaggerLookup", "swaggerlookup", "lookup-v2.json") {
resourcePackages.set(setOf("org.apache.pulsar.broker.lookup.v2"))
}

registerSwaggerTask("swaggerFunctions", "swaggerfunctions", "functions-v3.json") {
resourceClasses.set(setOf("org.apache.pulsar.broker.admin.v3.Functions"))
}

registerSwaggerTask("swaggerTransactions", "swaggertransactions", "transactions-v3.json") {
resourceClasses.set(setOf("org.apache.pulsar.broker.admin.v3.Transactions"))
}

registerSwaggerTask("swaggerSource", "swaggersource", "source-v3.json") {
resourceClasses.set(setOf("org.apache.pulsar.broker.admin.v3.Sources"))
}

registerSwaggerTask("swaggerSink", "swaggersink", "sink-v3.json") {
resourceClasses.set(setOf("org.apache.pulsar.broker.admin.v3.Sinks"))
}

registerSwaggerTask("swaggerPackages", "swaggerpackages", "packages-v3.json") {
resourceClasses.set(setOf("org.apache.pulsar.broker.admin.v3.Packages"))
}

// Assemble the documentation set in the layout published on pulsar.apache.org (see e.g.
// pulsar-site static/swagger/<version>/): all files flat, plus v2/ and v3/ subdirectory copies
// grouped by REST API version.
tasks.register<Sync>("generateOpenApiSpecs") {
group = "documentation"
description = "Generates all OpenAPI REST API documentation files to build/openapi"
into(layout.buildDirectory.dir("openapi"))
val v2Tasks = listOf("swaggerAdminV2", "swaggerLookup")
val v3Tasks = listOf("swaggerFunctions", "swaggerTransactions", "swaggerSource", "swaggerSink", "swaggerPackages")
v2Tasks.forEach { t ->
from(tasks.named(t))
from(tasks.named(t)) { into("v2") }
}
v3Tasks.forEach { t ->
from(tasks.named(t))
from(tasks.named(t)) { into("v3") }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
*/
package org.apache.pulsar.broker.admin.impl;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.StreamingOutput;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;
import org.apache.bookkeeper.mledger.proto.PendingBookieOpsStats;
Expand All @@ -48,10 +50,12 @@
public class BrokerStatsBase extends AdminResource {
@GET
@Path("/metrics")
@ApiOperation(value = "Gets the metrics for Monitoring",
notes = "Requested should be executed by Monitoring agent on each broker to fetch the metrics",
response = Metrics.class, responseContainer = "List")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
@Operation(summary = "Gets the metrics for Monitoring",
description = "The request should be executed by the Monitoring agent on each broker to fetch the metrics")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Gets the metrics for Monitoring",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = Metrics.class)))),
@ApiResponse(responseCode = "403", description = "Don't have admin permission") })
public Collection<Metrics> getMetrics() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
Expand All @@ -66,9 +70,11 @@ public Collection<Metrics> getMetrics() throws Exception {

@GET
@Path("/mbeans")
@ApiOperation(value = "Get all the mbean details of this broker JVM",
response = Metrics.class, responseContainer = "List")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
@Operation(summary = "Get all the mbean details of this broker JVM")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Get all the mbean details of this broker JVM",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = Metrics.class)))),
@ApiResponse(responseCode = "403", description = "Don't have admin permission") })
public Collection<Metrics> getMBeans() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
Expand All @@ -83,10 +89,13 @@ public Collection<Metrics> getMBeans() throws Exception {

@GET
@Path("/destinations")
@ApiOperation(value = "Get all the topic stats by namespace", response = OutputStream.class,
responseContainer = "OutputStream") // https://github.com/swagger-api/swagger-ui/issues/558
// map support missing
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
@Operation(summary = "Get all the topic stats by namespace")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Get all the topic stats by namespace",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Nested JSON object:"
+ " namespace -> bundle range -> persistent/non-persistent -> topic -> stats"))),
@ApiResponse(responseCode = "403", description = "Don't have admin permission") })
public StreamingOutput getTopics2() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
Expand All @@ -101,9 +110,13 @@ public StreamingOutput getTopics2() throws Exception {

@GET
@Path("/allocator-stats/{allocator}")
@ApiOperation(value = "Get the stats for the Netty allocator. Available allocators are 'default' and 'ml-cache'",
response = AllocatorStats.class)
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
@Operation(summary = "Get the stats for the Netty allocator. Available allocators are 'default' and 'ml-cache'")
@ApiResponses(value = {
@ApiResponse(responseCode = "200",
description = "Get the stats for the Netty allocator. Available allocators are 'default' "
+ "and 'ml-cache'",
content = @Content(schema = @Schema(implementation = AllocatorStats.class))),
@ApiResponse(responseCode = "403", description = "Don't have admin permission") })
public AllocatorStats getAllocatorStats(@PathParam("allocator") String allocatorName) throws Exception {
// Ensure super user access only
validateSuperUserAccess();
Expand All @@ -120,15 +133,14 @@ public AllocatorStats getAllocatorStats(@PathParam("allocator") String allocator

@GET
@Path("/bookieops")
@ApiOperation(value = "Get pending bookie client op stats by namespace",
notes = "Returns a nested map structure which Swagger does not fully support for display. "
+ "Structure: Map<String, Map<String, PendingBookieOpsStats>>."
+ " Please refer to this structure for details.",
response = PendingBookieOpsStats.class,
// https://github.com/swagger-api/swagger-core/issues/449
// nested containers are not supported
responseContainer = "Map")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
@Operation(summary = "Get pending bookie client op stats by namespace",
description = "Returns a nested map structure: Map<String, Map<String, PendingBookieOpsStats>>.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Get pending bookie client op stats by namespace",
content = @Content(schema = @Schema(type = "object"),
additionalPropertiesSchema =
@Schema(additionalPropertiesSchema = PendingBookieOpsStats.class))),
@ApiResponse(responseCode = "403", description = "Don't have admin permission") })
public Map<String, Map<String, PendingBookieOpsStats>> getPendingBookieOpsStats() {
// Ensure super user access only
validateSuperUserAccess();
Expand All @@ -144,9 +156,11 @@ public Map<String, Map<String, PendingBookieOpsStats>> getPendingBookieOpsStats(

@GET
@Path("/load-report")
@ApiOperation(value = "Get Load for this broker", notes = "consists of topics stats & systemResourceUsage",
response = LoadReport.class)
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission") })
@Operation(summary = "Get Load for this broker", description = "consists of topics stats & systemResourceUsage")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Get Load for this broker",
content = @Content(schema = @Schema(implementation = LoadReport.class))),
@ApiResponse(responseCode = "403", description = "Don't have admin permission") })
public LoadManagerReport getLoadReport() throws Exception {
// Ensure super user access only
validateSuperUserAccess();
Expand Down
Loading
Loading