From eae59b0317fba160041ce15bb4de7103b2806401 Mon Sep 17 00:00:00 2001 From: lastpeony Date: Thu, 21 Mar 2024 19:28:06 +0300 Subject: [PATCH 01/12] fix vulnerabiltys --- pom.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pom.xml b/pom.xml index fae3e0e50..5ee1ec42b 100644 --- a/pom.xml +++ b/pom.xml @@ -417,6 +417,27 @@ commons-io ${commons-io} + + + net.sf.ehcache + 2.10.9.2 + ehcache + + + + + org.apache.tika + tika-core + 2.9.1 + + + + + io.netty + netty-codec-http2 + 4.1.101.Final + + org.red5 red5-io From f4b9f3b15cdf6d9d60298fa8ded3d0f297e6b4fe Mon Sep 17 00:00:00 2001 From: lastpeony Date: Thu, 21 Mar 2024 20:11:15 +0300 Subject: [PATCH 02/12] modify test because method is removed on latest tika --- src/test/java/io/antmedia/test/MuxerUnitTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/antmedia/test/MuxerUnitTest.java b/src/test/java/io/antmedia/test/MuxerUnitTest.java index cee562965..5563cd14c 100644 --- a/src/test/java/io/antmedia/test/MuxerUnitTest.java +++ b/src/test/java/io/antmedia/test/MuxerUnitTest.java @@ -2727,7 +2727,8 @@ public void testMp4MuxingWithDirectParams() { try { FileInputStream fis = new FileInputStream("src/test/resources/frame0"); - byte[] byteArray = IOUtils.toByteArray(fis); + + byte[] byteArray = fis.readAllBytes(); fis.close(); @@ -2795,7 +2796,7 @@ public void testHLSMuxingWithDirectParams() { try { FileInputStream fis = new FileInputStream("src/test/resources/frame0"); - byte[] byteArray = IOUtils.toByteArray(fis); + byte[] byteArray = fis.readAllBytes(); fis.close(); @@ -2872,7 +2873,7 @@ public void testRecordMuxingWithDirectParams() { try { FileInputStream fis = new FileInputStream("src/test/resources/frame0"); - byte[] byteArray = IOUtils.toByteArray(fis); + byte[] byteArray = fis.readAllBytes(); fis.close(); From fd64561ce434482b7abbef1ab4bdc308f77b49b3 Mon Sep 17 00:00:00 2001 From: mekya Date: Fri, 22 Mar 2024 21:02:13 +0300 Subject: [PATCH 03/12] Update vertx in parent and remove the override of netty-codec-http2 --- pom.xml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index cdd87e608..887c898b8 100644 --- a/pom.xml +++ b/pom.xml @@ -423,19 +423,12 @@ 2.10.9.2 ehcache - - - org.apache.tika - tika-core - 2.9.1 - - - - - io.netty - netty-codec-http2 - 4.1.101.Final - + + + org.apache.tika + tika-core + 2.9.1 + org.red5 red5-io From aa37d37a56c065f2365e8815fa51121d704db77d Mon Sep 17 00:00:00 2001 From: mekya Date: Thu, 14 Mar 2024 01:20:04 +0300 Subject: [PATCH 04/12] Initial commit for OpenAPI integration --- pom.xml | 49 +- .../antmedia/AntMediaApplicationAdapter.java | 4 +- .../antmedia/console/rest/RestServiceV2.java | 683 ++++++------- .../datastore/db/types/Broadcast.java | 113 ++- .../datastore/db/types/ConferenceRoom.java | 59 +- .../datastore/db/types/ConnectionEvent.java | 137 ++- .../antmedia/datastore/db/types/Endpoint.java | 166 ++-- .../datastore/db/types/P2PConnection.java | 11 +- .../db/types/PushNotificationToken.java | 6 +- .../datastore/db/types/Subscriber.java | 167 ++-- .../db/types/SubscriberMetadata.java | 28 +- .../datastore/db/types/SubscriberStats.java | 58 +- .../datastore/db/types/TensorFlowObject.java | 102 +- .../io/antmedia/datastore/db/types/Token.java | 81 +- .../io/antmedia/datastore/db/types/User.java | 138 ++- .../io/antmedia/datastore/db/types/VoD.java | 69 +- .../datastore/db/types/WebRTCViewerInfo.java | 29 +- .../antmedia/rest/BroadcastRestService.java | 911 +++++++++++++----- .../io/antmedia/rest/RestServiceBase.java | 56 +- .../io/antmedia/rest/RootRestService.java | 55 +- .../java/io/antmedia/rest/VoDRestService.java | 241 +++-- .../io/antmedia/rest/WebRTCClientStats.java | 69 +- .../java/io/antmedia/rest/model/Result.java | 173 ++-- .../java/io/antmedia/rest/model/Version.java | 83 +- .../io/antmedia/test/db/DBStoresUnitTest.java | 6 +- 25 files changed, 1883 insertions(+), 1611 deletions(-) diff --git a/pom.xml b/pom.xml index fae3e0e50..c74928991 100644 --- a/pom.xml +++ b/pom.xml @@ -214,38 +214,7 @@ - - com.github.kongchen - swagger-maven-plugin - 3.1.8 - - - - false - - io.antmedia.rest - io.antmedia.console.rest - - target/swagger - swagger - json - - Ant Media Server REST API Reference - ${project.parent.version} - - test.antmedia.io:5443/Sandbox/rest/ - - - - - - compile - - generate - - - - + org.owasp dependency-check-maven @@ -381,17 +350,11 @@ morphia-core ${morphia.version} - - io.swagger - swagger-jersey2-jaxrs - ${swagger-jersey2-jaxrs.version} - - - jersey-container-servlet-core - org.glassfish.jersey.containers - - - + + io.swagger.core.v3 + swagger-jaxrs2-jakarta + 2.2.7 + io.vertx vertx-dropwizard-metrics diff --git a/src/main/java/io/antmedia/AntMediaApplicationAdapter.java b/src/main/java/io/antmedia/AntMediaApplicationAdapter.java index 46b39e84e..8a0e38f76 100644 --- a/src/main/java/io/antmedia/AntMediaApplicationAdapter.java +++ b/src/main/java/io/antmedia/AntMediaApplicationAdapter.java @@ -12,9 +12,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; - -import javax.validation.constraints.NotNull; - import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.RandomStringUtils; @@ -82,6 +79,7 @@ import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.dropwizard.MetricsService; +import jakarta.validation.constraints.NotNull; public class AntMediaApplicationAdapter extends MultiThreadedApplicationAdapter implements IAntMediaStreamHandler, IShutdownListener { public static final String BEAN_NAME = "web.handler"; diff --git a/src/main/java/io/antmedia/console/rest/RestServiceV2.java b/src/main/java/io/antmedia/console/rest/RestServiceV2.java index 5f8523bb4..201826502 100755 --- a/src/main/java/io/antmedia/console/rest/RestServiceV2.java +++ b/src/main/java/io/antmedia/console/rest/RestServiceV2.java @@ -20,16 +20,13 @@ import io.antmedia.datastore.db.types.User; import io.antmedia.rest.model.Result; import io.antmedia.settings.ServerSettings; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.Contact; -import io.swagger.annotations.ExternalDocs; -import io.swagger.annotations.Info; -import io.swagger.annotations.License; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.License; +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.servers.Server; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -42,666 +39,560 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - - - -@Api(value = "ManagementRestService") -@SwaggerDefinition( - info = @Info( - description = "Ant Media Server Management Panel REST API", - version = "v2.0", - title = "Ant Media Server Management Panel REST API", - contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), - license = @License(name = "Apache 2.0", url = "http://www.apache.org")), - consumes = {"application/json"}, - produces = {"application/json"}, - schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, - externalDocs = @ExternalDocs(value = "External Docs", url = "https://antmedia.io") - ) +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + + +@OpenAPIDefinition( + info = @io.swagger.v3.oas.annotations.info.Info( + title = "Ant Media Server Management Panel REST API", + version = "v2.0", + description = "Ant Media Server Management Panel REST API", + contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), + license = @License(name = "Apache 2.0", url = "http://www.apache.org") + ), + externalDocs = @ExternalDocumentation(description = "External Docs", url = "https://antmedia.io") + ) @Component @Path("/v2") public class RestServiceV2 extends CommonRestService { - @ApiOperation(value = "Creates a new user. If user object is null or if user is not authenticated, new user won't be created.", response = Result.class) + @Operation(summary = "Creates a new user", + description = "Creates a new user. If user object is null or if user is not authenticated, new user won't be created.", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))), + + }) @POST @Path("/users") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Override - public Result addUser(@ApiParam(value = "User object. If it is null, new user won't be created.", required = true, readOnly = true) User user) { + public Result addUser(@Parameter(description = "User object. If it is null, new user won't be created.", required = true) User user) { return super.addUser(user); } - @ApiOperation(value = "Edit the user in the server management panel's user list. It can change password or user type(admin, read only) ", response = List.class) + @Operation(summary = "Edit the user", + description = "Edit the user in the server management panel's user list. It can change password or user type (admin, read-only).", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @PUT @Path("/users") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Override - public Result editUser(@ApiParam(value = "User to be edited. It finds the user with username.", required = true)User user) { + public Result editUser(@Parameter(description = "User to be edited. It finds the user with username.", required = true)User user) { return super.editUser(user); } - @ApiOperation(value = "Delete the user from the server management panel's user list", response = List.class) + @Operation(summary = "Delete the user", + description = "Delete the user from the server management panel's user list", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @DELETE @Path("/users/{username}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Override - public Result deleteUser(@ApiParam(value = "User name or e-mail of the user to be deleted", required = true) @PathParam("username") String userName) { + public Result deleteUser(@Parameter(description = "User name or e-mail of the user to be deleted", required = true) @PathParam("username") String userName) { return super.deleteUser(userName); } - @ApiOperation(value = "Returns if user is blocked. User is blocked for a specific time if there are login attempts") + @Operation(summary = "Returns if user is blocked", + description = "User is blocked for a specific time if there are login attempts", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @GET @Consumes(MediaType.APPLICATION_JSON) @Path("/users/{usermail}/blocked") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result getBlockedStatus(@ApiParam(value="User name or e-mail of the user to check it status") @PathParam("usermail") String usermail) { - return super.getBlockedStatus(usermail); + public Result getBlockedStatus(@Parameter(description="User name or e-mail of the user to check its status") @PathParam("usermail") String usermail) { + return super.getBlockedStatus(usermail); } - @ApiOperation(value = "Returns user list in the server management panel", response = List.class) + + @Operation(summary = "Returns user list", + description = "Returns user list in the server management panel", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = List.class))) + }) @GET @Path("/user-list") @Produces(MediaType.APPLICATION_JSON) - @Override public List getUserList() { - return super.getUserList(); + return super.getUserList(); } - - @ApiOperation(value = "Returns whether current user is admin or not. If user is admin, it can call POST/PUT/DELETE methods", response = Result.class) + @Operation(summary = "Returns admin status", + description = "Returns whether current user is admin or not. If user is admin, it can call POST/PUT/DELETE methods", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @GET @Path("/admin-status") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override public Result isAdmin(){ - return super.isAdmin(); + return super.isAdmin(); } - @ApiOperation(value = "Creates initial user. This is a one time scenario when initial user creation required and shouldn't be used otherwise. User object is required and can't be null", response = Result.class) + @Operation(summary = "Creates initial user", + description = "Creates initial user. This is a one time scenario when initial user creation required and shouldn't be used otherwise. User object is required and can't be null", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @POST @Path("/users/initial") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Result addInitialUser(@ApiParam(value = "User object. If it is null, new user won't be created.", required = true)User user) { - return super.addInitialUser(user); + public Result addInitialUser(@Parameter(description = "User object. If it is null, new user won't be created.", required = true)User user) { + return super.addInitialUser(user); } - @ApiOperation(value = "Checks first login status. If server being logged in first time, it returns true, otherwise false.", response = Result.class) + @Operation(summary = "Checks first login status", + description = "Checks first login status. If server being logged in first time, it returns true, otherwise false.", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @GET @Path("/first-login-status") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Result isFirstLogin() - { - return super.isFirstLogin(); + public Result isFirstLogin() { + return super.isFirstLogin(); } - - /** - * Authenticates user with userName and password - * - * - * @param user: The User object to be authenticated - * @return json that shows user is authenticated or not - */ - @ApiOperation(value = "Authenticates user with given username and password. Requires user object to authenticate.", response = Result.class) + @Operation(summary = "Authenticates user", + description = "Authenticates user with given username and password. Requires user object to authenticate.", + responses = { + @ApiResponse(responseCode = "200", description = "Successful operation", + content = @Content(schema = @Schema(implementation = Result.class))) + }) @POST @Path("/users/authenticate") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Result authenticateUser(@ApiParam(value = "User object to authenticate", required = true) User user) { - return super.authenticateUser(user); + public Result authenticateUser(@Parameter(description = "User object to authenticate", required = true) User user) { + return super.authenticateUser(user); } - @ApiOperation(value = "Changes the given user's password.", response = Result.class) + + @Operation(summary = "Changes the given user's password", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @POST @Path("/users/password") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Result changeUserPassword(@ApiParam(value = "User object to change the password", required = true)User user) { - return super.changeUserPassword(user); + public Result changeUserPassword(@Parameter(description = "User object to change the password", required = true) User user) { + return super.changeUserPassword(user); } - - @ApiOperation(value = "Returns true if user is authenticated to call rest api operations.", response = Result.class) + @Operation(summary = "Returns true if user is authenticated to call rest api operations", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/authentication-status") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result isAuthenticatedRest(){ - return super.isAuthenticatedRest(); - } - - - /* - * os.name :Operating System Name - * os.arch : x86/x64/... - * java.specification.version : Java Version (Required 1.5 or 1.6 and higher to run Red5) - * ------------------------------- - * Runtime.getRuntime()._____ (Java Virtual Machine Memory) - * =============================== - * maxMemory() : Maximum limitation - * totalMemory() : Total can be used - * freeMemory() : Availability - * totalMemory()-freeMemory() : In Use - * availableProcessors() : Total Processors available - * ------------------------------- - * getOperatingSystemMXBean() (Actual Operating System RAM) - * =============================== - * osCommittedVirtualMemory() : Virtual Memory - * osTotalPhysicalMemory() : Total Physical Memory - * osFreePhysicalMemory() : Available Physical Memory - * osInUsePhysicalMemory() : In Use Physical Memory - * osTotalSwapSpace() : Total Swap Space - * osFreeSwapSpace() : Available Swap Space - * osInUseSwapSpace() : In Use Swap Space - * ------------------------------- - * File (Actual Harddrive Info: Supported for JRE 1.6) - * =============================== - * osHDUsableSpace() : Usable Space - * osHDTotalSpace() : Total Space - * osHDFreeSpace() : Available Space - * osHDInUseSpace() : In Use Space - **/ - - @ApiOperation(value = "Returns system information which includes many information such as JVM memory, OS information, Available File Space, Physical memory informations in detail.", response = Result.class) + public Result isAuthenticatedRest() { + return super.isAuthenticatedRest(); + } + + @Operation(summary = "Returns system information", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/system-status") @Produces(MediaType.APPLICATION_JSON) - @Override public String getSystemInfo() { - return super.getSystemInfo(); + return super.getSystemInfo(); } - - /* - * Runtime.getRuntime()._____ (Java Virtual Machine Memory) - * =============================== - * maxMemory() : Maximum limitation - * totalMemory() : Total can be used - * freeMemory() : Availability - * totalMemory()-freeMemory() : In Use - * availableProcessors() : Total Processors available - */ - @ApiOperation(value = "Returns JVM memory informations. Max, total, free, in-use and available processors are returned.", response = Result.class) + @Operation(summary = "Returns JVM memory information", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/jvm-memory-status") @Produces(MediaType.APPLICATION_JSON) - @Override public String getJVMMemoryInfo() { - return super.getJVMMemoryInfo(); + return super.getJVMMemoryInfo(); } - - /* - * osCommittedVirtualMemory() : Virtual Memory - * osTotalPhysicalMemory() : Total Physical Memory - * osFreePhysicalMemory() : Available Physical Memory - * osInUsePhysicalMemory() : In Use Physical Memory - * osTotalSwapSpace() : Total Swap Space - * osFreeSwapSpace() : Available Swap Space - * osInUseSwapSpace() : In Use Swap Space - */ - @ApiOperation(value = "Gets system memory status. Returns Virtual, total physical, available physical, currently in use, total swap space, available swap space and in use swap space. ", response = Result.class) + @Operation(summary = "Gets system memory status", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/system-memory-status") @Produces(MediaType.APPLICATION_JSON) - @Override public String getSystemMemoryInfo() { - return super.getSystemMemoryInfo(); + return super.getSystemMemoryInfo(); } - - /* - * File (Actual Harddrive Info: Supported for JRE 1.6) - * =============================== - * osHDUsableSpace() : Usable Space - * osHDTotalSpace() : Total Space - * osHDFreeSpace() : Available Space - * osHDInUseSpace() : In Use Space - **/ - @ApiOperation(value = "Gets system file status. Returns usable space, total space, available space and in use space.", response = Result.class) + @Operation(summary = "Gets system file status", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/file-system-status") @Produces(MediaType.APPLICATION_JSON) - @Override public String getFileSystemInfo() { - return super.getFileSystemInfo(); + return super.getFileSystemInfo(); } - /** - * getProcessCpuTime: microseconds CPU time used by the process - * - * getSystemCpuLoad: "% recent cpu usage" for the whole system. - * - * getProcessCpuLoad: "% recent cpu usage" for the Java Virtual Machine process. - * @return the CPU load info - */ - @ApiOperation(value = "Returns system cpu load, process cpu load and process cpu time.", response = Result.class) + @Operation(summary = "Returns system cpu load, process cpu load and process cpu time", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/cpu-status") @Produces(MediaType.APPLICATION_JSON) - @Override public String getCPUInfo() { - return super.getCPUInfo(); + return super.getCPUInfo(); } - @ApiOperation(value = "Gets thread dump in plain text.Includes very detailed information such as thread name, thread id, blocked time of thread, thread state and many more information are returned.", response = Result.class) + @Operation(summary = "Gets thread dump in plain text", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/thread-dump") @Produces(MediaType.TEXT_PLAIN) - @Override public String getThreadDump() { - return super.getThreadDump(); + return super.getThreadDump(); } - @ApiOperation(value = "Gets thread dump in json format. Includes very detailed information such as thread name, thread id, blocked time of thread, thread state and many more information are returned.", response = Result.class) + @Operation(summary = "Gets thread dump in json format", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET - @Path("/thread-dump") + @Path("/thread-dump-json") @Produces(MediaType.APPLICATION_JSON) - @Override public String getThreadDumpJSON() { - return super.getThreadDumpJSON(); + return super.getThreadDumpJSON(); } - @ApiOperation(value = "Returns processor's thread information. Includes number of dead locked threads, thread count, and thread peek count.", response = Result.class) + @Operation(summary = "Returns processor's thread information", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/threads") @Produces(MediaType.APPLICATION_JSON) - @Override public String getThreadsInfo() { - return super.getThreadsInfo(); + return super.getThreadsInfo(); } - - - // method path was already Restful - // v2 is added to prevent clashes with older RestService.java - @ApiOperation(value = "Returns heap dump.", response = Result.class) - @ApiResponses(value = {@ApiResponse(code = 200, message = "Returns the heap dump")}) + @Operation(summary = "Returns heap dump", + responses = {@ApiResponse(responseCode = "200", description = "Returns the heap dump")}) @GET @Path("/heap-dump") @Produces(MediaType.APPLICATION_OCTET_STREAM) - @Override public Response getHeapDump() { - - return super.getHeapDump(); + return super.getHeapDump(); } - - /** - * Return server uptime and startime in milliseconds - * @return JSON object contains the server uptime and start time - */ - - // method path was already Restful - // v2 is added to prevent clashes with older RestService.java - @ApiOperation(value = "Gets server time. Returns server uptime and start time in milliseconds in JSON.", response = Result.class) + @Operation(summary = "Gets server time", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/server-time") @Produces(MediaType.APPLICATION_JSON) - @Override public String getServerTime() { - return super.getServerTime(); + return super.getServerTime(); } - @ApiOperation(value = "Gets system resource information. Returns number of total live streams, cpu usage, system information, jvm information, file system information, license status, gpu status etc. Basically returns most of the information in one package.", response = Result.class) + @Operation(summary = "Gets system resource information", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/system-resources") @Produces(MediaType.APPLICATION_JSON) - @Override public String getSystemResourcesInfo() { - - return super.getSystemResourcesInfo(); + return super.getSystemResourcesInfo(); } - - @ApiOperation(value = "Gets GPU information. Returns whether you have GPU or not. If yes, information of the gpu and the number of total gpus.", response = Result.class) + @Operation(summary = "Gets GPU information", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/gpu-status") @Produces(MediaType.APPLICATION_JSON) - @Override - public String getGPUInfo() - { - return super.getGPUInfo(); + public String getGPUInfo() { + return super.getGPUInfo(); } - @ApiOperation(value = "Returns the version of Ant Media Server.", response = Result.class) + @Operation(summary = "Returns the version of Ant Media Server", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/version") @Produces(MediaType.APPLICATION_JSON) - @Override public String getVersion() { - return super.getVersion(); + return super.getVersion(); } - @ApiOperation(value = "Trigger garbage collector.", notes = "", response = Result.class) + @Operation(summary = "Trigger garbage collector", + responses = {@ApiResponse(responseCode = "200", description = "Garbage collection triggered")}) @POST @Path("/system/gc") @Produces(MediaType.APPLICATION_JSON) - @SuppressWarnings("squid:S1215") public Result triggerGc() { - System.gc(); - return new Result(true); + System.gc(); + return new Result(true); } - @ApiOperation(value = "Gets the applications in the server. Returns the name of the applications in JSON format.", response = Result.class) + + @Operation(summary = "Gets the applications in the server", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/applications") @Produces(MediaType.APPLICATION_JSON) - @Override public String getApplications() { - - return super.getApplications(); + return super.getApplications(); } - /** - * Refactor name getTotalLiveStreamSize - * only return totalLiveStreamSize - * @return the number of live clients - */ - @ApiOperation(value = "Returns total number of live streams and total number of connections.", response = Result.class) + @Operation(summary = "Returns total number of live streams", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/live-clients-size") @Produces(MediaType.APPLICATION_JSON) - @Override - public String getLiveClientsSize() - { - - return super.getLiveClientsSize(); + public String getTotalLiveStreamSize() { + return super.getLiveClientsSize(); } - @ApiOperation(value = "Gets application info. Application info includes live stream count, vod count and application name.", response = Result.class) + @Operation(summary = "Gets application info", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/applications-info") @Produces(MediaType.APPLICATION_JSON) - @Override public String getApplicationInfo() { - - return super.getApplicationInfo(); + return super.getApplicationInfo(); } - /** - * Refactor remove this function and use ProxyServlet to get this info - * Before deleting check web panel does not use it - * @param name: application name - * @return live streams in the application - */ - @ApiOperation(value = "Returns live streams in the specified application. Retrieves broadcast names and the consumer size.", response = Result.class) + @Operation(summary = "Returns live streams in the specified application", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @GET @Path("/applications/live-streams/{appname}") @Produces(MediaType.APPLICATION_JSON) - @Override - public String getAppLiveStreams(@ApiParam(value = "Application name", required = true) @PathParam("appname") String name) { - - return super.getAppLiveStreams(name); + public String getAppLiveStreams(@Parameter(description = "Application name", required = true) @PathParam("appname") String name) { + return super.getAppLiveStreams(name); } - @ApiOperation(value = "Changes the application settings with the given settings. Null fields will be set to default values.", response = Result.class) + @Operation(summary = "Changes the application settings", + responses = {@ApiResponse(responseCode = "200", description = "Successful operation")}) @POST @Path("/applications/settings/{appname}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public String changeSettings(@ApiParam(value = "Application name", required = true) @PathParam("appname") String appname, @ApiParam(value = "New application settings, null fields will be set to default values", required = true) AppSettings newSettings){ - - return super.changeSettings(appname, newSettings); + public String changeSettings(@Parameter(description = "Application name", required = true) @PathParam("appname") String appname, @Parameter(description = "New application settings, null fields will be set to default values", required = true) AppSettings newSettings) { + return super.changeSettings(appname, newSettings); } - @ApiOperation(value = "Checks whether application or applications have shutdown properly or not.", response = Result.class) - @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns the shutdown status of entered applications."), - @ApiResponse(code = 400, message = "Either entered in wrong format or typed incorrectly application names")}) + @Operation(summary = "Checks whether application or applications have shutdown properly", + responses = { + @ApiResponse(responseCode = "200", description = "Returns the shutdown status of entered applications."), + @ApiResponse(responseCode = "400", description = "Either entered in wrong format or typed incorrectly application names") + }) @GET @Path("/shutdown-proper-status") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Response isShutdownProperly(@ApiParam(value = "Application name", required = true) @QueryParam("appNames") String appNamesArray) - { - return super.isShutdownProperly(appNamesArray); + public Response isShutdownProperly(@Parameter(description = "Application name", required = true) @QueryParam("appNames") String appNamesArray) { + return super.isShutdownProperly(appNamesArray); } - @ApiOperation(value = "Set application or applications shutdown property to true", response = Result.class) + @Operation(summary = "Set application or applications shutdown property to true", + responses = {@ApiResponse(responseCode = "200", description = "Shutdown status set")}) @GET @Path("/shutdown-properly") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public boolean setShutdownStatus(@ApiParam(value = "Application name", required = true) @QueryParam("appNames") String appNamesArray){ - - return super.setShutdownStatus(appNamesArray); + public boolean setShutdownStatus(@Parameter(description = "Application name", required = true) @QueryParam("appNames") String appNamesArray) { + return super.setShutdownStatus(appNamesArray); } - @ApiOperation(value = "Changes server settings. Sets Server Name, license key, market build status and node group.", response = Result.class) + @Operation(summary = "Changes server settings", + responses = {@ApiResponse(responseCode = "200", description = "Server settings changed")}) @POST @Path("/server-settings") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public String changeServerSettings(@ApiParam(value = "Server settings", required = true) ServerSettings serverSettings){ - return super.changeServerSettings(serverSettings); + public String changeServerSettings(@Parameter(description = "Server settings", required = true) ServerSettings serverSettings) { + return super.changeServerSettings(serverSettings); } - @ApiOperation(value = "Changes ssl settings. Sets ssl configuration type. After this method is called, server will be restarted.", response = Result.class) + @Operation(summary = "Changes ssl settings", + responses = {@ApiResponse(responseCode = "200", description = "SSL settings configured, server will be restarted")}) @POST @Path("/ssl-settings") @Consumes({MediaType.MULTIPART_FORM_DATA}) @Produces(MediaType.APPLICATION_JSON) - @Override - public Result configureSsl(@ApiParam(value = "SSL settings", required = true) @QueryParam("domain") String domain, @QueryParam("type") String type, - @FormDataParam("fullChainFile") InputStream fullChainFile, - @FormDataParam("fullChainFile") FormDataContentDisposition fullChainFileDetail, - @FormDataParam("privateKeyFile") InputStream privateKeyFile, - @FormDataParam("privateKeyFile") FormDataContentDisposition privateKeyFileDetail, - @FormDataParam("chainFile") InputStream chainFile, - @FormDataParam("chainFile") FormDataContentDisposition chainFileDetail) - - { - return super.configureSsl(domain, type, fullChainFile, fullChainFileDetail, privateKeyFile, privateKeyFileDetail, chainFile, chainFileDetail); + public Result configureSsl(@Parameter(description = "SSL settings", required = true) @QueryParam("domain") String domain, @QueryParam("type") String type, + @FormDataParam("fullChainFile") InputStream fullChainFile, + @FormDataParam("fullChainFile") FormDataContentDisposition fullChainFileDetail, + @FormDataParam("privateKeyFile") InputStream privateKeyFile, + @FormDataParam("privateKeyFile") FormDataContentDisposition privateKeyFileDetail, + @FormDataParam("chainFile") InputStream chainFile, + @FormDataParam("chainFile") FormDataContentDisposition chainFileDetail) { + return super.configureSsl(domain, type, fullChainFile, fullChainFileDetail, privateKeyFile, privateKeyFileDetail, chainFile, chainFileDetail); } - @ApiOperation(value = "Returns true if the server is enterprise edition.", response = Result.class) + @Operation(summary = "Returns true if the server is enterprise edition", + responses = {@ApiResponse(responseCode = "200", description = "Enterprise edition status")}) @GET @Path("/enterprise-edition") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result isEnterpriseEdition(){ - - return super.isEnterpriseEdition(); + public Result isEnterpriseEdition() { + return super.isEnterpriseEdition(); } - @ApiOperation(value = "Returns the specified application settings", response = Result.class) + @Operation(summary = "Returns the specified application settings", + responses = {@ApiResponse(responseCode = "200", description = "Application settings returned")}) @GET @Path("/applications/settings/{appname}") @Produces(MediaType.APPLICATION_JSON) - @Override - public AppSettings getSettings(@ApiParam(value = "Application name", required = true) @PathParam("appname") String appname) - { - return super.getSettings(appname); + public AppSettings getSettings(@Parameter(description = "Application name", required = true) @PathParam("appname") String appname) { + return super.getSettings(appname); } - @ApiOperation(value = "Returns the server settings. From log level to measurement period of cpu, license key of the server host address,ssl configuration and many more settings are returned at once.", response = Result.class) + @Operation(summary = "Returns the server settings", + responses = {@ApiResponse(responseCode = "200", description = "Server settings returned")}) @GET @Path("/server-settings") @Produces(MediaType.APPLICATION_JSON) - @Override - public ServerSettings getServerSettings() - { - return super.getServerSettings(); + public ServerSettings getServerSettings() { + return super.getServerSettings(); } - @ApiOperation(value = "Returns license status. Includes license ID, status, owner, start date, end date, type and license count.", response = Result.class) + @Operation(summary = "Returns license status", + responses = {@ApiResponse(responseCode = "200", description = "License status returned")}) @GET @Path("/licence-status") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Licence getLicenceStatus(@ApiParam(value = "License key", required = true) @QueryParam("key") String key) - { - return super.getLicenceStatus(key); + public Licence getLicenceStatus(@Parameter(description = "License key", required = true) @QueryParam("key") String key) { + return super.getLicenceStatus(key); } - @ApiOperation(value = "Returns the last checked license status. Includes license ID, owner, start date, end date, type and license count.", response = Result.class) + @Operation(summary = "Returns the last checked license status", + responses = {@ApiResponse(responseCode = "200", description = "Last checked license status returned")}) @GET @Path("/last-licence-status") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Licence getLicenceStatus() - { - return super.getLicenceStatus(); - } - - /** - * This method resets the viewers counts and broadcast status in the db. - * This should be used to recover db after server crashes. - * It's not intended to use to ignore the crash - * @param appname the application name that broadcasts will be reset - * @return - */ - @ApiOperation(value = "Resets the viewer counts and broadcasts statuses in the db. This can be used after server crashes to recover db. It's not intended to use to ignore the crash.", response = Result.class) + public Licence getLicenceStatus() { + return super.getLicenceStatus(); + } + + + @Operation(summary = "Resets the viewer counts and broadcasts statuses in the db", + responses = {@ApiResponse(responseCode = "200", description = "Broadcasts reset successfully")}) @POST @Path("/applications/{appname}/reset") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @Override - public Result resetBroadcast(@ApiParam(value = "Application name", required = true) @PathParam("appname") String appname) - { - return super.resetBroadcast(appname); + public Result resetBroadcast(@Parameter(description = "Application name", required = true) @PathParam("appname") String appname) { + return super.resetBroadcast(appname); } - @ApiOperation(value = "Returns the server mode. If it is in the cluster mode, result will be true.", response = Result.class) + @Operation(summary = "Returns the server mode", + responses = {@ApiResponse(responseCode = "200", description = "Server mode returned")}) @GET @Path("/cluster-mode-status") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result isInClusterMode(){ - return super.isInClusterMode(); + public Result isInClusterMode() { + return super.isInClusterMode(); } - - - @ApiOperation(value = "Gets log file. Char size of the log, offset or log type can be specified.", response = Result.class) + @Operation(summary = "Gets log file", + responses = {@ApiResponse(responseCode = "200", description = "Log file retrieved successfully")}) @GET @Path("/log-file/{offsetSize}/{charSize}") @Produces(MediaType.APPLICATION_JSON) - @Override - public String getLogFile(@ApiParam(value = "Char size of the log", required = true) @PathParam("charSize") int charSize, @ApiParam(value = "Log type. ERROR can be used to get only error logs", required = true) @QueryParam("logType") String logType, - @ApiParam(value = "Offset of the retrieved log", required = true) @PathParam("offsetSize") long offsetSize) throws IOException { - return super.getLogFile(charSize,logType, offsetSize); - } - - /** - * Create application. It supports both default or custom app - * - * How Custom App Creation works - * 1. Save the custom war file to tmp directory - * 2. Install the app from the tmp directory - * 3. If it's in cluster mode, create a symbolic link in root app to let other apps download the app - * - */ - @ApiOperation(value = "Creates a new application with given name. It just creates default app", response = Result.class) + public String getLogFile(@Parameter(description = "Char size of the log", required = true) @PathParam("charSize") int charSize, @Parameter(description = "Log type. ERROR can be used to get only error logs", required = true) @QueryParam("logType") String logType, + @Parameter(description = "Offset of the retrieved log", required = true) @PathParam("offsetSize") long offsetSize) throws IOException { + return super.getLogFile(charSize, logType, offsetSize); + } + + @Operation(summary = "Creates a new application with given name", + responses = {@ApiResponse(responseCode = "200", description = "Application created successfully")}) @POST @Consumes({MediaType.APPLICATION_JSON}) @Path("/applications/{appName}") @Produces(MediaType.APPLICATION_JSON) - public Result createApplication(@ApiParam(value = "Name for the new application", required = true) @PathParam("appName") String appName) { - return createApplication(appName, null); + public Result createApplication(@Parameter(description = "Name for the new application", required = true) @PathParam("appName") String appName) { + return createApplication(appName, null); } - @ApiOperation(value = "Creates a new application with given name. It supports uploading custom WAR files", response = Result.class) + @Operation(summary = "Creates a new application with given name and supports uploading custom WAR files", + responses = {@ApiResponse(responseCode = "200", description = "Custom application created successfully")}) @PUT @Consumes({MediaType.MULTIPART_FORM_DATA}) @Path("/applications/{appName}") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result createApplication(@ApiParam(value = "Name for the new application", required = true) @PathParam("appName") String appName, @ApiParam(value = "file", required = true) @FormDataParam("file") InputStream inputStream) - { - Result result; - if (appName != null && appName.matches("^[a-zA-Z0-9]*$")) - { - - - boolean applicationAlreadyExist = isApplicationExists(appName); - - if (!applicationAlreadyExist) - { - result = super.createApplication(appName, inputStream); - } - else - { - result = new Result(false, "Application with the same name already exists"); - } - } - else - { - result = new Result(false, "Application name is not alphanumeric. Please provide alphanumeric characters"); - } - - return result; + public Result createApplication(@Parameter(description = "Name for the new application", required = true) @PathParam("appName") String appName, @Parameter(description = "file", required = true) @FormDataParam("file") InputStream inputStream) { + Result result; + if (appName != null && appName.matches("^[a-zA-Z0-9]*$")) { + boolean applicationAlreadyExist = isApplicationExists(appName); + if (!applicationAlreadyExist) { + result = super.createApplication(appName, inputStream); + } else { + result = new Result(false, "Application with the same name already exists"); + } + } else { + result = new Result(false, "Application name is not alphanumeric. Please provide alphanumeric characters"); + } + return result; } + public boolean isApplicationExists(String appName) { return getApplication().getRootScope().getScope(appName) != null; } - @ApiOperation(value = "Deletes application with the given name.", response = Result.class) + @Operation(summary = "Deletes application with the given name", + responses = {@ApiResponse(responseCode = "200", description = "Application deleted successfully")}) @DELETE @Path("/applications/{appName}") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result deleteApplication(@ApiParam(value = "Name of the application to delete", required = true) @PathParam("appName") String appName, - @QueryParam("deleteDB") boolean deleteDB) { - if (appName != null) { - return super.deleteApplication(appName, deleteDB); - } - return new Result(false, "Application name is not defined"); + public Result deleteApplication(@Parameter(description = "Name of the application to delete", required = true) @PathParam("appName") String appName, + @Parameter(description = "Whether to delete associated database", required = true) @QueryParam("deleteDB") boolean deleteDB) { + if (appName != null) { + return super.deleteApplication(appName, deleteDB); + } + return new Result(false, "Application name is not defined"); } - - @ApiOperation(value = "Returns the hostname to check liveness with HTTP type healthcheck.", response = Response.class) + @Operation(summary = "Returns the hostname to check liveness with HTTP type healthcheck", + responses = {@ApiResponse(responseCode = "200", description = "Liveness check response")}) @GET @Path("/liveness") @Produces(MediaType.APPLICATION_JSON) public Response liveness() { - long startTimeMs = System.currentTimeMillis(); - JsonObject jsonObject = new JsonObject(); - - //the following method may take some time to return - - String status; - Status statusCode; - String hostname = getHostname(); - if (hostname != null) { - status = "ok"; - statusCode = Status.OK; - } - else { - hostname = "unknown"; - status = "error"; - statusCode = Status.INTERNAL_SERVER_ERROR; - } - - jsonObject.addProperty("host", hostname); - jsonObject.addProperty("status", status); - - Gson gson = new Gson(); - long elapsedTimeMs = System.currentTimeMillis() - startTimeMs; - if (elapsedTimeMs > 1000) { - logger.warn("GET liveness method takes {}ms to return", elapsedTimeMs); - } - return Response.status(statusCode).entity(gson.toJson(jsonObject)).build(); + long startTimeMs = System.currentTimeMillis(); + JsonObject jsonObject = new JsonObject(); + + //the following method may take some time to return + + String status; + Status statusCode; + String hostname = getHostname(); + if (hostname != null) { + status = "ok"; + statusCode = Status.OK; + } + else { + hostname = "unknown"; + status = "error"; + statusCode = Status.INTERNAL_SERVER_ERROR; + } + + jsonObject.addProperty("host", hostname); + jsonObject.addProperty("status", status); + + Gson gson = new Gson(); + long elapsedTimeMs = System.currentTimeMillis() - startTimeMs; + if (elapsedTimeMs > 1000) { + logger.warn("GET liveness method takes {}ms to return", elapsedTimeMs); + } + return Response.status(statusCode).entity(gson.toJson(jsonObject)).build(); } diff --git a/src/main/java/io/antmedia/datastore/db/types/Broadcast.java b/src/main/java/io/antmedia/datastore/db/types/Broadcast.java index 82b3ca397..cd57eff3a 100644 --- a/src/main/java/io/antmedia/datastore/db/types/Broadcast.java +++ b/src/main/java/io/antmedia/datastore/db/types/Broadcast.java @@ -13,11 +13,10 @@ import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="Broadcast", description="The basic broadcast class") +@Schema(description="The basic broadcast class") @Entity(value = "broadcast") @Indexes({ @Index(fields = @Field(value = "name", type = IndexType.TEXT)), @Index(fields = @Field("streamId")) }) public class Broadcast { @@ -30,54 +29,54 @@ public class Broadcast { /** * id of the broadcast */ - @ApiModelProperty(value = "the id of the stream") + @Schema(description = "the id of the stream") private String streamId; /** * "finished", "broadcasting", "created" */ - @ApiModelProperty(value = "the status of the stream", allowableValues = "finished, broadcasting,created") + @Schema(description = "the status of the stream", allowableValues = "finished,broadcasting,created") private String status; - @ApiModelProperty(value = "The status of the playlist. It's usable if type is playlist", allowableValues = "finished, broadcasting,created") + @Schema(description = "The status of the playlist. It's usable if type is playlist", allowableValues = "finished,broadcasting,created") private String playListStatus; /** * "liveStream", "ipCamera", "streamSource", "VoD" */ - @ApiModelProperty(value = "the type of the stream", allowableValues = "liveStream, ipCamera,streamSource,VoD,playlist") + @Schema(description = "the type of the stream", allowableValues = "liveStream,ipCamera,streamSource,VoD,playlist") private String type; /** * "WebRTC", "RTMP", "Pull" */ - @ApiModelProperty(value = "the publish type of the stream", allowableValues = "WebRTC, RTMP, Pull") + @Schema(description = "The publish type of the stream. It's read-only and its value updated on the server side", allowableValues = "WebRTC,RTMP,Pull") private String publishType; /** * name of the broadcast */ - @ApiModelProperty(value = "the name of the stream") + @Schema(description = "the name of the stream") private String name; /** * description of the broadcast */ - @ApiModelProperty(value = "the description of the stream") + @Schema(description ="the description of the stream") private String description; /** * It is a video filter for the service, this value is controlled by the * user, default value is true in the db */ - @ApiModelProperty(value = "it is a video filter for the service, this value is controlled by the user, default value is true in the db") + @Schema(description ="it is a video filter for the service, this value is controlled by the user, default value is true in the db") private boolean publish = true; /** * date when record is created in milliseconds */ - @ApiModelProperty(value = "the date when record is created in milliseconds") + @Schema(description ="the date when record is created in milliseconds") private long date; /** @@ -87,7 +86,7 @@ public class Broadcast { * This feature is enabled in RTMP and WebRTC streams * Streams are accepting when plannedStartDate is lower than now(Unix Timestamp) */ - @ApiModelProperty(value = "the planned start date") + @Schema(description ="the planned start date") private long plannedStartDate; /** @@ -97,32 +96,32 @@ public class Broadcast { * This feature is enabled in RTMP and WebRTC streams * Streams are accepting when plannedEndDate is higher than now(Unix Timestamp) */ - @ApiModelProperty(value = "the planned end date") + @Schema(description ="the planned end date") private long plannedEndDate; /** * duration of the stream in milliseconds */ - @ApiModelProperty(value = "the duration of the stream in milliseconds") + @Schema(description ="the duration of the stream in milliseconds") private long duration; - @ApiModelProperty(value = "the list of endpoints such as Facebook, Twitter or custom RTMP endpoints ") + @Schema(description ="the list of endpoints such as Facebook, Twitter or custom RTMP endpoints ") private List endPointList; - @ApiModelProperty(value = "the list broadcasts in the playlis. This list has values when the broadcast type is playlist") + @Schema(description ="the list broadcasts in the playlis. This list has values when the broadcast type is playlist") private List playListItemList; /** * is public */ - @ApiModelProperty(value = "the identifier of whether stream is public or not") + @Schema(description ="the identifier of whether stream is public or not") private boolean publicStream = true; /** * If this stream is a 360 degree video */ - @ApiModelProperty(value = "the identifier of whether stream is 360 or not") + @Schema(description ="the identifier of whether stream is 360 or not") private boolean is360 = false; /** @@ -152,59 +151,59 @@ public class Broadcast { * */ - @ApiModelProperty(value = "the url that will be notified when stream is published, ended and muxing finished") + @Schema(description ="the url that will be notified when stream is published, ended and muxing finished") private String listenerHookURL; - @ApiModelProperty(value = "the category of the stream") + @Schema(description ="the category of the stream") private String category; - @ApiModelProperty(value = "the IP Address of the IP Camera or publisher") + @Schema(description ="the IP Address of the IP Camera or publisher") private String ipAddr; - @ApiModelProperty(value = "the user name of the IP Camera") + @Schema(description ="the user name of the IP Camera") private String username; - @ApiModelProperty(value = "the password of the IP Camera") + @Schema(description ="the password of the IP Camera") private String password; - @ApiModelProperty(value = "the quality of the incoming stream during publishing") + @Schema(description ="the quality of the incoming stream during publishing") private String quality; - @ApiModelProperty(value = "the speed of the incoming stream, for better quality and performance it should be around 1.00") + @Schema(description ="the speed of the incoming stream, for better quality and performance it should be around 1.00") private double speed; /** * This is the stream url for fetching stream. * It has a value for IPCameras and streams in the cloud */ - @ApiModelProperty(value = "the stream URL for fetching stream, especially should be defined for IP Cameras or Cloud streams") + @Schema(description ="the stream URL for fetching stream, especially should be defined for IP Cameras or Cloud streams") private String streamUrl; /** * This is the origin address server broadcasting. */ - @ApiModelProperty(value = "the origin address server broadcasting") + @Schema(description ="the origin address server broadcasting") private String originAdress; /** * Mp4 muxing is enabled or not for the stream * 1 means enabled, -1 means disabled, 0 means no settings for the stream */ - @ApiModelProperty(value = "MP4 muxing whether enabled or not for the stream, 1 means enabled, -1 means disabled, 0 means no settings for the stream") + @Schema(description ="MP4 muxing whether enabled or not for the stream, 1 means enabled, -1 means disabled, 0 means no settings for the stream") private int mp4Enabled = 0; /** * WebM muxing is enabled or not for the stream * 1 means enabled, -1 means disabled, 0 means no settings for the stream */ - @ApiModelProperty(value = "WebM muxing whether enabled or not for the stream, 1 means enabled, -1 means disabled, 0 means no settings for the stream") + @Schema(description ="WebM muxing whether enabled or not for the stream, 1 means enabled, -1 means disabled, 0 means no settings for the stream") private int webMEnabled = 0; /** * Initial time to start playing. It can be used in VoD file or stream sources that has seek support * If it's a VoD file, it can seek to that time and start playing there */ - @ApiModelProperty(value = "Initial time to start playing. It can be used in VoD file or stream sources that has seek support") + @Schema(description ="Initial time to start playing. It can be used in VoD file or stream sources that has seek support") private long seekTimeInMs = 0; @Entity @@ -261,13 +260,13 @@ public Broadcast() { * * If expire duration is 0, then stream will never expire */ - @ApiModelProperty(value = "the expire time in milliseconds For instance if this value is 10000 then broadcast should be started in 10 seconds after it is created.If expire duration is 0, then stream will never expire") + @Schema(description ="the expire time in milliseconds For instance if this value is 10000 then broadcast should be started in 10 seconds after it is created.If expire duration is 0, then stream will never expire") private int expireDurationMS; /** * RTMP URL where to publish live stream to */ - @ApiModelProperty(value = "the RTMP URL where to publish live stream to") + @Schema(description ="the RTMP URL where to publish live stream to") private String rtmpURL; /** @@ -275,7 +274,7 @@ public Broadcast() { * rest service or management console It is false by default * */ - @ApiModelProperty(value = "is true, if a broadcast that is not added to data store through rest service or management console It is false by default") + @Schema(description ="is true, if a broadcast that is not added to data store through rest service or management console It is false by default") private boolean zombi = false; /** @@ -284,14 +283,14 @@ public Broadcast() { * in the queue */ - @ApiModelProperty(value = "the number of audio and video packets that is being pending to be encoded in the queue ") + @Schema(description ="the number of audio and video packets that is being pending to be encoded in the queue ") private int pendingPacketSize = 0; /** * number of hls viewers of the stream */ - @ApiModelProperty(value = "the number of HLS viewers of the stream") + @Schema(description ="the number of HLS viewers of the stream") private int hlsViewerCount = 0; @@ -299,67 +298,67 @@ public Broadcast() { * number of dash viewers of the stream */ - @ApiModelProperty(value = "the number of DASH viewers of the stream") + @Schema(description ="the number of DASH viewers of the stream") private int dashViewerCount = 0; - @ApiModelProperty(value = "the number of WebRTC viewers of the stream") + @Schema(description ="the number of WebRTC viewers of the stream") private int webRTCViewerCount = 0; - @ApiModelProperty(value = "the number of RTMP viewers of the stream") + @Schema(description ="the number of RTMP viewers of the stream") private int rtmpViewerCount = 0; - @ApiModelProperty(value = "the publishing start time of the stream") + @Schema(description ="the publishing start time of the stream") private long startTime = 0; - @ApiModelProperty(value = "the received bytes until now") + @Schema(description ="the received bytes until now") private long receivedBytes = 0; - @ApiModelProperty(value = "the received bytes / duration") + @Schema(description ="the received bytes / duration") private long bitrate = 0; - @ApiModelProperty(value = "User - Agent") + @Schema(description ="User - Agent") private String userAgent = "N/A"; - @ApiModelProperty(value = "latitude of the broadcasting location") + @Schema(description ="latitude of the broadcasting location") private String latitude; - @ApiModelProperty(value = "longitude of the broadcasting location") + @Schema(description ="longitude of the broadcasting location") private String longitude; - @ApiModelProperty(value = "altitude of the broadcasting location") + @Schema(description ="altitude of the broadcasting location") private String altitude; - @ApiModelProperty(value = "If this broadcast is a track of a WebRTC stream. This variable is Id of that stream.") + @Schema(description ="If this broadcast is a track of a WebRTC stream. This variable is Id of that stream.") private String mainTrackStreamId; - @ApiModelProperty(value = "If this broadcast is main track. This variable hold sub track ids.") + @Schema(description ="If this broadcast is main track. This variable hold sub track ids.") private List subTrackStreamIds = new ArrayList(); - @ApiModelProperty(value = "Absolute start time in milliseconds - unix timestamp. It's used for measuring the absolute latency") + @Schema(description ="Absolute start time in milliseconds - unix timestamp. It's used for measuring the absolute latency") private long absoluteStartTimeMs; - @ApiModelProperty(value = "Number of the allowed maximum WebRTC viewers for the broadcast") + @Schema(description ="Number of the allowed maximum WebRTC viewers for the broadcast") private int webRTCViewerLimit = -1; - @ApiModelProperty(value = "Number of the allowed maximum HLS viewers for the broadcast") + @Schema(description ="Number of the allowed maximum HLS viewers for the broadcast") private int hlsViewerLimit = -1; - @ApiModelProperty(value = "Number of the allowed maximum DASH viewers for the broadcast") + @Schema(description ="Number of the allowed maximum DASH viewers for the broadcast") private int dashViewerLimit = -1; - @ApiModelProperty(value = "Name of the subfolder that will contain stream files") + @Schema(description ="Name of the subfolder that will contain stream files") private String subFolder; /** * Current playing index for play lists */ - @ApiModelProperty(value = "Current playing index for playlist types") + @Schema(description ="Current playing index for playlist types") private int currentPlayIndex = 0; /** * Meta data filed for the custom usage */ - @ApiModelProperty(value = "Meta data filed for the custom usage") + @Schema(description ="Meta data filed for the custom usage") private String metaData = null; /** @@ -367,7 +366,7 @@ public Broadcast() { * If it's true, playlist will be loop infinitely. If it's false, playlist played once and finished. * It's enable by default */ - @ApiModelProperty(value = "the identifier of playlist loop status") + @Schema(description ="the identifier of playlist loop status") private boolean playlistLoopEnabled = true; /** @@ -376,7 +375,7 @@ public Broadcast() { */ private long updateTime = 0; - @ApiModelProperty(value = "The identifier of whether stream should start/stop automatically. It's effective for Stream Sources/IP Cameras. " + @Schema(description ="The identifier of whether stream should start/stop automatically. It's effective for Stream Sources/IP Cameras. " + "If there is no viewer after certain amount of seconds, it will stop. If there is an user want to watch the stream, it will start automatically") private boolean autoStartStopEnabled = false; diff --git a/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java b/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java index 33ecce45f..7e713e63a 100644 --- a/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java +++ b/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java @@ -13,45 +13,44 @@ import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity("ConferenceRoom") @Indexes({ @Index(fields = @Field("roomId")) }) -@ApiModel(value="ConferenceRoom", description="The Conference Room class") +@Schema(description = "The Conference Room class") public class ConferenceRoom { public static final String MULTI_TRACK_MODE = "multitrack"; public static final String LEGACY_MODE = "legacy"; @JsonIgnore - @Id - @ApiModelProperty(value = "The db id of the Conference Room") - private ObjectId dbId; - - @ApiModelProperty(value = "The id of the Conference Room") - private String roomId; - - @ApiModelProperty(value = "The start date of the Conference Room. It's unix timestamp in seconds.") - private long startDate; - - @ApiModelProperty(value = "The end date of the Conference Room. It's unix timestamp in seconds") - private long endDate; - - @ApiModelProperty(value = "The list of streams in the Conference Room") - private List roomStreamList = new ArrayList<>(); - - @ApiModelProperty(value = "Conference Room Mode: legacy | mcu | multi-track") - private String mode = "legacy"; - - @JsonIgnore - private boolean zombi; - - /** - * This is the origin address of the node hosting the room. - */ - @ApiModelProperty(value = "the origin address of the node hosting the room") - private String originAdress; + @Id + @Schema(description = "The db id of the Conference Room") + private ObjectId dbId; + + @Schema(description = "The id of the Conference Room") + private String roomId; + + @Schema(description = "The start date of the Conference Room. It's unix timestamp in seconds.") + private long startDate; + + @Schema(description = "The end date of the Conference Room. It's unix timestamp in seconds") + private long endDate; + + @Schema(description = "The list of streams in the Conference Room") + private List roomStreamList = new ArrayList<>(); + + @Schema(description = "Conference Room Mode: legacy | mcu | multi-track") + private String mode = "legacy"; + + @JsonIgnore + private boolean zombi; + + /** + * This is the origin address of the node hosting the room. + */ + @Schema(description = "the origin address of the node hosting the room") + private String originAdress; public String getRoomId() { return roomId; diff --git a/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java b/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java index edbf15c8a..afa207658 100644 --- a/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java +++ b/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java @@ -2,80 +2,75 @@ import com.fasterxml.jackson.annotation.JsonIgnore; - import dev.morphia.annotations.Entity; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="ConnectionEvent", description="Connection Event for the subscriber") +@Schema(description = "Connection Event for the subscriber") @Entity public class ConnectionEvent { - @JsonIgnore - public static final String CONNECTED_EVENT = "connected"; - @JsonIgnore - public static final String DISCONNECTED_EVENT = "disconnected"; - - /** - * Timestamp of this event. Unix timestamp in milliseconds - */ - @ApiModelProperty(value = "the unix timestamp of the event in milliseconds") - private long timestamp; - /** - * type of the event Connection/Disconnection - */ - @ApiModelProperty(value = "The type of the event. It can have connected or disconnected values") - private String eventType; - - @ApiModelProperty(value = "IP address of the instance that this event happened") - private String instanceIP; - - @ApiModelProperty(value = "Connection type. It can be publish or play") - private String type; - - @ApiModelProperty(value = "Event protocol. It can be webrtc, hls, dash") - private String eventProtocol; - - public long getTimestamp() { - return timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - public String getEventType() { - return eventType; - } - - public void setEventType(String eventType) { - this.eventType = eventType; - } - - public String getInstanceIP() { - return instanceIP; - } - - public void setInstanceIP(String instanceIP) { - this.instanceIP = instanceIP; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getEventProtocol() { - return eventProtocol; - } - - public void setEventProtocol(String eventProtocol) { - this.eventProtocol = eventProtocol; - } - - - + @JsonIgnore + public static final String CONNECTED_EVENT = "connected"; + @JsonIgnore + public static final String DISCONNECTED_EVENT = "disconnected"; + + /** + * Timestamp of this event. Unix timestamp in milliseconds + */ + @Schema(description = "the unix timestamp of the event in milliseconds") + private long timestamp; + /** + * type of the event Connection/Disconnection + */ + @Schema(description = "The type of the event. It can have connected or disconnected values") + private String eventType; + + @Schema(description = "IP address of the instance that this event happened") + private String instanceIP; + + @Schema(description = "Connection type. It can be publish or play") + private String type; + + @Schema(description = "Event protocol. It can be webrtc, hls, dash") + private String eventProtocol; + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public String getInstanceIP() { + return instanceIP; + } + + public void setInstanceIP(String instanceIP) { + this.instanceIP = instanceIP; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getEventProtocol() { + return eventProtocol; + } + + public void setEventProtocol(String eventProtocol) { + this.eventProtocol = eventProtocol; + } } diff --git a/src/main/java/io/antmedia/datastore/db/types/Endpoint.java b/src/main/java/io/antmedia/datastore/db/types/Endpoint.java index c95bcb877..190b67983 100644 --- a/src/main/java/io/antmedia/datastore/db/types/Endpoint.java +++ b/src/main/java/io/antmedia/datastore/db/types/Endpoint.java @@ -1,93 +1,85 @@ package io.antmedia.datastore.db.types; import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; import io.antmedia.muxer.IAntMediaStreamHandler; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="Endpoint", description="The endpoint class, such as Facebook, Twitter or custom RTMP endpoints") +@Schema(description = "The endpoint class, such as Facebook, Twitter or custom RTMP endpoints") @Entity -public class Endpoint -{ - - /** - * Keeps track of the RTMP endpoint status if it is writing or not - * {@link IAntMediaStreamHandler#BROADCAST_STATUS_*} - */ - @ApiModelProperty(value = "Status of the RTMP muxer, possible values are started, finished, failed, broadcasting, {@link IAntMediaStreamHandler#BROADCAST_STATUS_*}") - private String status; - - /** - * Service name like facebook, periscope, youtube or generic - * it should match the VideoServiceEndpoint names or it can be generic - */ - @ApiModelProperty(value = "the service name like facebook, periscope, youtube or generic") - private String type; - - - /** - * RTMP URL of the endpoint - */ - @ApiModelProperty(value = "the RTMP URL of the endpoint") - private String rtmpUrl; - - - /** - * Endpoint service id, this field holds the id of the endpoint - */ - @ApiModelProperty(value = "the endpoint service id, this field holds the id of the endpoint") - private String endpointServiceId; - - - - - /** - * Default constructor used in BroadcastRestService.addEndpoint - */ - public Endpoint() { - this.status = IAntMediaStreamHandler.BROADCAST_STATUS_CREATED; - - } - - public Endpoint(String rtmpUrl, String type, String endpointServiceId, String status) { - this(); - this.status = status; - this.rtmpUrl = rtmpUrl; - this.type = type; - this.endpointServiceId = endpointServiceId; - } - - - public String getRtmpUrl() { - return rtmpUrl; - } - - public void setRtmpUrl(String rtmpUrl) { - this.rtmpUrl = rtmpUrl; - } - - public String getEndpointServiceId() { - return endpointServiceId; - } - - public void setEndpointServiceId(String endpointServiceId) { - this.endpointServiceId = endpointServiceId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - -} \ No newline at end of file +public class Endpoint { + + /** + * Keeps track of the RTMP endpoint status if it is writing or not + * {@link IAntMediaStreamHandler#BROADCAST_STATUS_*} + */ + @Schema(description = "Status of the RTMP muxer, possible values are started, finished, failed, broadcasting, {@link IAntMediaStreamHandler#BROADCAST_STATUS_*}") + private String status; + + /** + * Service name like facebook, periscope, youtube or generic + * it should match the VideoServiceEndpoint names or it can be generic + */ + @Schema(description = "The service name like facebook, periscope, youtube or generic") + private String type; + + /** + * RTMP URL of the endpoint + */ + @Schema(description = "The RTMP URL of the endpoint") + private String rtmpUrl; + + /** + * Endpoint service id, this field holds the id of the endpoint + */ + @Schema(description = "The endpoint service id, this field holds the id of the endpoint") + @Id + private String endpointServiceId; + + /** + * Default constructor used in BroadcastRestService.addEndpoint + */ + public Endpoint() { + this.status = IAntMediaStreamHandler.BROADCAST_STATUS_CREATED; + } + + public Endpoint(String rtmpUrl, String type, String endpointServiceId, String status) { + this(); + this.status = status; + this.rtmpUrl = rtmpUrl; + this.type = type; + this.endpointServiceId = endpointServiceId; + } + + public String getRtmpUrl() { + return rtmpUrl; + } + + public void setRtmpUrl(String rtmpUrl) { + this.rtmpUrl = rtmpUrl; + } + + public String getEndpointServiceId() { + return endpointServiceId; + } + + public void setEndpointServiceId(String endpointServiceId) { + this.endpointServiceId = endpointServiceId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/src/main/java/io/antmedia/datastore/db/types/P2PConnection.java b/src/main/java/io/antmedia/datastore/db/types/P2PConnection.java index 1f9f5b49a..a12a20158 100644 --- a/src/main/java/io/antmedia/datastore/db/types/P2PConnection.java +++ b/src/main/java/io/antmedia/datastore/db/types/P2PConnection.java @@ -11,30 +11,29 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity("P2PConnection") @Indexes({ @Index(fields = @Field("streamId")) }) -@ApiModel(value="P2PConnection", description="P2P Signalling Connection Info") +@Schema(description = "P2P Signalling Connection Info") public class P2PConnection implements Serializable{ private static final long serialVersionUID = 1L; @JsonIgnore @Id - @ApiModelProperty(value = "the db id of the p2p connection") + @Schema(description = "The db id of the p2p connection") private ObjectId dbId; /** * streamId */ - @ApiModelProperty(value = "the streamId for the p2p connection") + @Schema(description = "The streamId for the p2p connection") private String streamId; /** * originNode */ - @ApiModelProperty(value = "the IP of the originNode to which caller is connected") + @Schema(description = "The IP of the originNode to which caller is connected") private String originNode; public P2PConnection() { diff --git a/src/main/java/io/antmedia/datastore/db/types/PushNotificationToken.java b/src/main/java/io/antmedia/datastore/db/types/PushNotificationToken.java index d1d1e7f83..0f12ff887 100644 --- a/src/main/java/io/antmedia/datastore/db/types/PushNotificationToken.java +++ b/src/main/java/io/antmedia/datastore/db/types/PushNotificationToken.java @@ -2,12 +2,13 @@ import dev.morphia.annotations.Entity; import io.antmedia.pushnotification.IPushNotificationService.PushNotificationServiceTypes; -import io.swagger.annotations.ApiModel; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="PushNotificationToken", description="The endpoint class, such as Facebook, Twitter or custom RTMP endpoints") +@Schema(description = "Push Notification Token") @Entity public class PushNotificationToken { + @Schema(description = "The token value") private String token; /** @@ -16,6 +17,7 @@ public class PushNotificationToken { * fcm: Firebase Cloud Messagnig * apn: Apple Push Notification */ + @Schema(description = "The service name for push notifications. Possible values are 'fcm' for Firebase Cloud Messaging or 'apn' for Apple Push Notification.") private String serviceName; private String extraData; diff --git a/src/main/java/io/antmedia/datastore/db/types/Subscriber.java b/src/main/java/io/antmedia/datastore/db/types/Subscriber.java index 9d19f7388..816c6e9b4 100644 --- a/src/main/java/io/antmedia/datastore/db/types/Subscriber.java +++ b/src/main/java/io/antmedia/datastore/db/types/Subscriber.java @@ -2,100 +2,97 @@ import org.bson.types.ObjectId; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonProperty.Access; import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Field; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; -import dev.morphia.annotations.Field; -import dev.morphia.annotations.Id; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity("subscriber") @Indexes({ @Index(fields = @Field("subscriberId")), @Index(fields = @Field("streamId")) }) -@ApiModel(value="Subscriber", description="The time based token subscriber class. This keeps which subscriber can access to which stream and which TOTP") +@Schema(description = "The time based token subscriber class. This keeps which subscriber can access to which stream and which TOTP") public class Subscriber { - @JsonIgnore - public static final String PLAY_TYPE = "play"; - @JsonIgnore - public static final String PUBLISH_TYPE = "publish"; - - @JsonIgnore - public static final String PUBLISH_AND_PLAY_TYPE = "publish_play"; - - @JsonIgnore - @Id - @ApiModelProperty(value = "the db id of the subscriber") - private ObjectId dbId; - - /** - * random subscriber id - */ - @ApiModelProperty(value = "the subscriber id of the subscriber") - private String subscriberId; - - /** - * related streamId with subscriber - */ - @ApiModelProperty(value = "the stream id of the token") - private String streamId; - - /** - * statistics for this subscriber - */ - @ApiModelProperty(value = "stats for this subscriber") - private SubscriberStats stats = new SubscriberStats(); - - @JsonProperty(access = Access.WRITE_ONLY) - /** - * secret code of the Subscriber - */ - @ApiModelProperty(value = "secret code of the subscriber") - private String b32Secret; - - /** - * type of the subscriber - */ - @ApiModelProperty(value = " type of subscriber (play or publish). Pay attention that 'publish' type can also play the streams for making easy to join video conferencing") - private String type; - - /** - * is subscriber connected - * TODO: Write what the recommend way is to get this information? Let's write some comments when we deprecate something - * @mekya - */ - @Deprecated(since="2.4.3", forRemoval=true) - @ApiModelProperty(value = "is subscriber connected") - private boolean connected; - - /** - * count of subscriber usage - */ - @ApiModelProperty(value = " count of subscriber usage") - private int currentConcurrentConnections = 0; - - /** - * count of subscriber limit - */ - @ApiModelProperty(value = " count of subscriber usage") - private int concurrentConnectionsLimit = 1; - - /** - * Type of block. It can be publish, play or publish_play in static field: {@link Subscriber#PLAY_TYPE}, - * {@link Subscriber#PUBLISH_TYPE}, {@link Subscriber#PUBLISH_AND_PLAY_TYPE} - */ - private String blockedType; - - /** - * If this is set, it means user is blocked until this time - * This is unix timestamp in milliseconds - */ - private long blockedUntilUnitTimeStampMs = 0; - - private String registeredNodeIp; + + @JsonIgnore + public static final String PLAY_TYPE = "play"; + + @JsonIgnore + public static final String PUBLISH_TYPE = "publish"; + + @JsonIgnore + public static final String PUBLISH_AND_PLAY_TYPE = "publish_play"; + + @JsonIgnore + @Schema(hidden = true) + private ObjectId dbId; + + /** + * The subscriber id of the subscriber + */ + @Schema(description = "The subscriber id of the subscriber") + private String subscriberId; + + /** + * The stream id of the token + */ + @Schema(description = "The stream id of the token") + private String streamId; + + /** + * Stats for this subscriber + */ + @Schema(description = "Stats for this subscriber") + private SubscriberStats stats = new SubscriberStats(); + + /** + * Secret code of the Subscriber + */ + @JsonIgnore + @Schema(description = "Secret code of the subscriber") + private String b32Secret; + + /** + * Type of subscriber (play or publish). Pay attention that 'publish' type can also play the streams for making easy to join video conferencing + */ + @Schema(description = "Type of subscriber (play or publish). Pay attention that 'publish' type can also play the streams for making easy to join video conferencing") + private String type; + + /** + * Is subscriber connected + * TODO: Write what the recommend way is to get this information? Let's write some comments when we deprecate something + * @mekya + */ + @Deprecated(since = "2.4.3", forRemoval = true) + @Schema(description = "Is subscriber connected") + private boolean connected; + + /** + * Count of subscriber usage + */ + @Schema(description = "Count of subscriber usage") + private int currentConcurrentConnections = 0; + + /** + * Count of subscriber limit + */ + @Schema(description = "Count of subscriber usage") + private int concurrentConnectionsLimit = 1; + + /** + * Type of block. It can be publish, play or publish_play in static field: {@link Subscriber#PLAY_TYPE}, + * {@link Subscriber#PUBLISH_TYPE}, {@link Subscriber#PUBLISH_AND_PLAY_TYPE} + */ + private String blockedType; + + /** + * If this is set, it means user is blocked until this time + * This is unix timestamp in milliseconds + */ + private long blockedUntilUnitTimeStampMs = 0; + + private String registeredNodeIp; public String getSubscriberId() { return subscriberId; diff --git a/src/main/java/io/antmedia/datastore/db/types/SubscriberMetadata.java b/src/main/java/io/antmedia/datastore/db/types/SubscriberMetadata.java index 7101dd62f..256600ba5 100644 --- a/src/main/java/io/antmedia/datastore/db/types/SubscriberMetadata.java +++ b/src/main/java/io/antmedia/datastore/db/types/SubscriberMetadata.java @@ -1,6 +1,5 @@ package io.antmedia.datastore.db.types; -import java.util.List; import java.util.Map; import org.bson.types.ObjectId; @@ -9,11 +8,9 @@ import dev.morphia.annotations.Entity; import dev.morphia.annotations.Field; -import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; -import dev.morphia.utils.IndexType; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity @Indexes({ @Index(fields = @Field("subscriberId")) }) @@ -21,18 +18,17 @@ public class SubscriberMetadata { @JsonIgnore - @Id - @ApiModelProperty(value = "the db id of the SubscriberMetadata") - private ObjectId dbId; - - /** - * Subscriber id. It can be username, email or any random number - */ - @ApiModelProperty(value = "the subscriber id") - private String subscriberId; - - @ApiModelProperty(value = "Push notification tokens provided by FCM and APN") - private Map pushNotificationTokens; + @Schema(hidden = true) + private ObjectId dbId; + + /** + * The subscriber id. It can be username, email or any random number + */ + @Schema(description = "The subscriber id") + private String subscriberId; + + @Schema(description = "Push notification tokens provided by FCM and APN") + private Map pushNotificationTokens; public ObjectId getDbId() { return dbId; diff --git a/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java b/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java index 5de9cef29..2fd485441 100644 --- a/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java +++ b/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java @@ -4,42 +4,42 @@ import java.util.List; import dev.morphia.annotations.Entity; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="SubscriberStats", description="Statistics for each subsciber to the stream") +@Schema(description="Statistics for each subsciber to the stream") @Entity public class SubscriberStats { /** - * subscriber id to which this statistic belongs to - */ - @ApiModelProperty(value = "the subscriber id of the subscriber") - private String subscriberId; - - /** - * related streamId with subscriber - */ - @ApiModelProperty(value = "the stream id of the token") - private String streamId; - - /** - * connection events happened for this subscriber - */ - @ApiModelProperty(value = "list of connection events") - private List connectionEvents = new ArrayList<>(); + * The subscriber id to which this statistic belongs to. + */ + @Schema(description = "The subscriber id of the subscriber") + private String subscriberId; - /** - * average video bitrate for a subscriber - */ - @ApiModelProperty(value = "average video bitrate for a subscriber") - private long avgVideoBitrate; + /** + * The related streamId with the subscriber. + */ + @Schema(description = "The stream id of the token") + private String streamId; + + /** + * The connection events happened for this subscriber. + */ + @Schema(description = "List of connection events") + private List connectionEvents = new ArrayList<>(); + + /** + * The average video bitrate for a subscriber. + */ + @Schema(description = "Average video bitrate for a subscriber") + private long avgVideoBitrate; + + /** + * The average audio bitrate for a subscriber. + */ + @Schema(description = "Average audio bitrate for a subscriber") + private long avgAudioBitrate; - /** - * average audio bitrate for a subscriber - */ - @ApiModelProperty(value = "average audio bitrate for a subscriber") - private long avgAudioBitrate; public String getSubscriberId() { return subscriberId; diff --git a/src/main/java/io/antmedia/datastore/db/types/TensorFlowObject.java b/src/main/java/io/antmedia/datastore/db/types/TensorFlowObject.java index 244587271..c22b44e58 100644 --- a/src/main/java/io/antmedia/datastore/db/types/TensorFlowObject.java +++ b/src/main/java/io/antmedia/datastore/db/types/TensorFlowObject.java @@ -1,63 +1,73 @@ package io.antmedia.datastore.db.types; import org.bson.types.ObjectId; -import dev.morphia.annotations.Embedded; + import dev.morphia.annotations.Entity; import dev.morphia.annotations.Field; -import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; -import dev.morphia.utils.IndexType; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity("detection") @Indexes({ @Index(fields = @Field("dbId")) }) -@ApiModel(value="TensorFlowObject", description="The TensorFlow detected object class") +@Schema(description="The TensorFlow detected object class") public class TensorFlowObject { - @JsonIgnore - @Id - @ApiModelProperty(value = "the id of the detected object") - private ObjectId dbId; - - /** - * Name of the object - */ - @ApiModelProperty(value = "the name of the detected object") - public String objectName; - - /** - * % percent of the recognition probability - */ - @ApiModelProperty(value = "the probablity of the detected object") - public float probability; - - /** - * Detection time - */ - @ApiModelProperty(value = "the time of the detected object") - public long detectionTime; - - @ApiModelProperty(value = "the id of the detected image") - private String imageId; - - @ApiModelProperty(value = "the x coordinate of upper-left corner of detected object frame") - private double minX; - - @ApiModelProperty(value = "the y coordinate of upper-left corner of detected object frame") - private double minY; - - @ApiModelProperty(value = "the x coordinate of lower-right corner of detected object frame") - private double maxX; - - @ApiModelProperty(value = "the y coordinate of lower-right corner of detected object frame") - private double maxY; + /** + * The id of the detected object. + */ + @Schema(description = "The id of the detected object") + private ObjectId dbId; + + /** + * The name of the object. + */ + @Schema(description = "The name of the detected object") + private String objectName; + + /** + * The percent of the recognition probability. + */ + @Schema(description = "The probability of the detected object") + private float probability; + + /** + * The detection time. + */ + @Schema(description = "The time of the detected object") + private long detectionTime; + + /** + * The id of the detected image. + */ + @Schema(description = "The id of the detected image") + private String imageId; + + /** + * The x coordinate of the upper-left corner of the detected object frame. + */ + @Schema(description = "The x coordinate of the upper-left corner of detected object frame") + private double minX; + + /** + * The y coordinate of the upper-left corner of the detected object frame. + */ + @Schema(description = "The y coordinate of the upper-left corner of detected object frame") + private double minY; + + /** + * The x coordinate of the lower-right corner of the detected object frame. + */ + @Schema(description = "The x coordinate of the lower-right corner of detected object frame") + private double maxX; + + /** + * The y coordinate of the lower-right corner of the detected object frame. + */ + @Schema(description = "The y coordinate of the lower-right corner of detected object frame") + private double maxY; public TensorFlowObject(String name, float probability, String imageId) { diff --git a/src/main/java/io/antmedia/datastore/db/types/Token.java b/src/main/java/io/antmedia/datastore/db/types/Token.java index a4cf8bdef..175383181 100644 --- a/src/main/java/io/antmedia/datastore/db/types/Token.java +++ b/src/main/java/io/antmedia/datastore/db/types/Token.java @@ -1,62 +1,58 @@ package io.antmedia.datastore.db.types; import org.bson.types.ObjectId; -import dev.morphia.annotations.Embedded; + import dev.morphia.annotations.Entity; import dev.morphia.annotations.Field; -import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity("token") @Indexes({ @Index(fields = @Field("tokenId")) }) -@ApiModel(value="Token", description="The one-time token class") +@Schema(description="The one-time token class") public class Token { - @JsonIgnore - @Id - @ApiModelProperty(value = "the db id of the token") - private ObjectId dbId; - public static final String PUBLISH_TOKEN = "publish"; public static final String PLAY_TOKEN = "play"; - /** - * random tokenID - */ - @ApiModelProperty(value = "the token id of the token") - private String tokenId; - - /** - * related streamId with token - */ - @ApiModelProperty(value = "the stream id of the token") - private String streamId; - - /** - * expiration date of the token - */ - @ApiModelProperty(value = "the expire date of the token") - private long expireDate; - - /** - * type of the token, such as publish, play etc. - */ - @ApiModelProperty(value = "the type of the token") - private String type; - - /** - * the id of the conference room which requested streams belongs to. - */ - @ApiModelProperty(value = "the id of the conference room which requested streams belongs to") - private String roomId; + /** + * The db id of the token. + */ + @Schema(description = "The db id of the token") + private ObjectId dbId; + + /** + * The token id. + */ + @Schema(description = "The token id") + private String tokenId; + + /** + * The stream id associated with the token. + */ + @Schema(description = "The stream id associated with the token") + private String streamId; + + /** + * The expiration date of the token. + */ + @Schema(description = "The expiration date of the token") + private long expireDate; + + /** + * The type of the token, such as publish, play, etc. + */ + @Schema(description = "The type of the token") + private String type; + + /** + * The id of the conference room which requested streams belong to. + */ + @Schema(description = "The id of the conference room which requested streams belong to") + private String roomId; public String getRoomId() { @@ -99,5 +95,4 @@ public void setExpireDate(long expireDate) { this.expireDate = expireDate; } - } diff --git a/src/main/java/io/antmedia/datastore/db/types/User.java b/src/main/java/io/antmedia/datastore/db/types/User.java index 06c1ca90e..67158aaaf 100644 --- a/src/main/java/io/antmedia/datastore/db/types/User.java +++ b/src/main/java/io/antmedia/datastore/db/types/User.java @@ -13,91 +13,71 @@ import dev.morphia.annotations.Indexes; import dev.morphia.utils.IndexType; import io.antmedia.rest.model.UserType; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="User", description="The basic user class for accessing the web panel") +@Schema(description = "The user information") @Entity(value = "user") @Indexes({ @Index(fields = @Field("email")), @Index(fields = @Field("fullName")) }) public class User { /** - * Email of the user - */ - @ApiModelProperty(value = "the email of the user") - private String email; - - /** - * Password of the user, ignore password and this field is not set for all user types - */ - @ApiModelProperty(value = "the password of the user") - @JsonProperty(access = Access.WRITE_ONLY) - private String password; - - /** - * ADMIN can do anything in its scope. - * If it's scope is system, it can CRUD anything - * If it's scope is an application, it can CRUD anything in the application. - * it cannot access the web panel services - * - * READ_ONLY can read anything in its scope. - * If it's scope is system, it can READ anything - * If it's scope is an application, it can only READ anything in the application - * it cannot access the web panel services - * - * USER can do anything but cannot change the settings in its scope. - * If it's scope is system, it can CRUD content but cannot change system settings/application settings - * If it's scope is an application, it can CRUD content but cannot change application settings - * it cannot access the web panel services - */ - @ApiModelProperty(value = "the type of the user", allowableValues = "ADMIN, READ-ONLY, USER") - private UserType userType; - - /** - * Scope of the user. If it's scope is system, it can access the stuff in system-level - * It's scope is an application, it can access the stuff in application-level - * - * It makes more sense with UserType - */ - @ApiModelProperty(value = "Scope can be 'system' or name of the application. Scope of the user. If it's scope is system, it can " - + "access the stuff in system-level. If it's scope is an application, it can access the stuff in application-level" - + "It makes more sense with UserType") - private String scope; - - - /** - * New password of the user, below field is not set for all user types - */ - @ApiModelProperty(value = "the new password of the user") - @JsonProperty(access = Access.WRITE_ONLY) - private String newPassword; - - /** - * Name of the user. Use firstname and lastname - */ - @Deprecated - @ApiModelProperty(value = "the name of the user") - private String fullName; - - @ApiModelProperty(value = "Fist name of the user") - private String firstName; - - @ApiModelProperty(value = "last name of the user") - private String lastName; - - /** - * URL of the picture if exists - */ - @ApiModelProperty(value = "the URL of the user picture") - private String picture; - - /** - * ID of the user - */ - @ApiModelProperty(value = "the id of the user") - @JsonIgnore - @Id - private ObjectId id; + * The email of the user. + */ + @Schema(description = "The email of the user") + private String email; + + /** + * The password of the user. This field is only set for certain user types. + */ + @Schema(description = "The password of the user") + private String password; + + /** + * The type of the user. + */ + @Schema(description = "The type of the user", allowableValues = {"ADMIN", "READ-ONLY", "USER"}) + private UserType userType; + + /** + * The scope of the user. It can be 'system' or the name of the application. + */ + @Schema(description = "The scope of the user. If it's 'system', it can access system-level stuff. If it's an application name, it can access application-level stuff.") + private String scope; + + /** + * The new password of the user. This field is only set for certain user types. + */ + @Schema(description = "The new password of the user") + private String newPassword; + + /** + * The first name of the user. + */ + @Schema(description = "The first name of the user") + private String firstName; + + /** + * The last name of the user. + */ + @Schema(description = "The last name of the user") + private String lastName; + + @Deprecated + @Schema(description = "The full name of the user") + private String fullName; + + + /** + * The URL of the user's picture. + */ + @Schema(description = "The URL of the user's picture") + private String picture; + + /** + * The id of the user. + */ + @Schema(description = "The id of the user") + private ObjectId id; public User(String email, String password, UserType userType, String scope) { this.email = email; diff --git a/src/main/java/io/antmedia/datastore/db/types/VoD.java b/src/main/java/io/antmedia/datastore/db/types/VoD.java index 711926d27..f39339aec 100644 --- a/src/main/java/io/antmedia/datastore/db/types/VoD.java +++ b/src/main/java/io/antmedia/datastore/db/types/VoD.java @@ -13,13 +13,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; @Entity("vod") @Indexes({ @Index(fields = @Field("vodId")), @Index(fields = @Field("vodName")), @Index(fields = @Field("streamId")), @Index(fields = @Field("streamName")) }) -@ApiModel(value="VoD", description="The recorded video-on-demand object class") +@Schema(description = "The recorded video-on-demand object class") public class VoD implements Serializable { private static final long serialVersionUID = 1L; @@ -41,40 +40,40 @@ public class VoD implements Serializable { public static final String UPLOADED_VOD = "uploadedVod"; @JsonIgnore - @Id - private ObjectId dbId; - @ApiModelProperty(value = "the object id of the VoD") - private String streamName; - - @ApiModelProperty(value = "the name of the VoD") - private String vodName; - - @ApiModelProperty(value = "the stream id of the VoD") - private String streamId; - - @ApiModelProperty(value = "the creation of the VoD") - private long creationDate; - - @ApiModelProperty(value = "the time when the VoD is being started to get recorded in milliseconds(UTC- Unix epoch)") - private long startTime; + private ObjectId dbId; - @ApiModelProperty(value = "the duration of the VoD") - private long duration; - - @ApiModelProperty(value = "the size of the VoD file in bytes") - private long fileSize; - - @ApiModelProperty(value = "the path of the VoD") - private String filePath; - - @ApiModelProperty(value = "the id of the VoD") - private String vodId; - - @ApiModelProperty(value = "the type of the VoD, such as userVod, streamVod, uploadedVod") - private String type; + @Schema(description = "The object id of the VoD") + private String streamName; + + @Schema(description = "The name of the VoD") + private String vodName; + + @Schema(description = "The stream id of the VoD") + private String streamId; + + @Schema(description = "The creation date of the VoD") + private long creationDate; + + @Schema(description = "The start time of the VoD recording in milliseconds (UTC- Unix epoch)") + private long startTime; + + @Schema(description = "The duration of the VoD") + private long duration; + + @Schema(description = "The size of the VoD file in bytes") + private long fileSize; + + @Schema(description = "The path of the VoD") + private String filePath; + + @Schema(description = "The id of the VoD") + private String vodId; + + @Schema(description = "The type of the VoD, such as userVod, streamVod, uploadedVod") + private String type; - @ApiModelProperty(value = "the type of the VoD, such as userVod, streamVod, uploadedVod") - private String previewFilePath; + @Schema(description = "The file path for the preview of the VoD") + private String previewFilePath; public VoD() { diff --git a/src/main/java/io/antmedia/datastore/db/types/WebRTCViewerInfo.java b/src/main/java/io/antmedia/datastore/db/types/WebRTCViewerInfo.java index 062fe9921..24688e18f 100644 --- a/src/main/java/io/antmedia/datastore/db/types/WebRTCViewerInfo.java +++ b/src/main/java/io/antmedia/datastore/db/types/WebRTCViewerInfo.java @@ -9,8 +9,7 @@ import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.Indexes; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; /** * @deprecated Use subscriber class and rest methods, it will be deleted in coming versions @@ -18,7 +17,7 @@ * */ @Deprecated(since = "2.7.0", forRemoval = true) -@ApiModel(value="WebRTCViewerInfo", description="Stores the info for a WebRTC viewer") +@Schema(description = "Stores the information for a WebRTC viewer") @Entity(value = "WebRTCViewerInfo") @Indexes({ @Index(fields = @Field("viewerId")) }) public class WebRTCViewerInfo { @@ -27,26 +26,16 @@ public class WebRTCViewerInfo { @Id private ObjectId dbId; - /** - * Id of the viewer - */ - @ApiModelProperty(value = "the id of the viewer") + @Schema(description = "The id of the viewer") private String viewerId; - - - /** - * Stream id that viewer views - */ - @ApiModelProperty(value = "stream id that viewer views") + + @Schema(description = "The stream id that viewer views") private String streamId; - - /** - * IP address of the edge to which viewer is connected - */ - @ApiModelProperty(value = "IP address of the edge to which viewer is connected") + + @Schema(description = "The IP address of the edge to which viewer is connected") private String edgeAddress; - + public ObjectId getDbId() { return dbId; } @@ -78,5 +67,5 @@ public String getEdgeAddress() { public void setEdgeAddress(String edgeAddress) { this.edgeAddress = edgeAddress; } - + } diff --git a/src/main/java/io/antmedia/rest/BroadcastRestService.java b/src/main/java/io/antmedia/rest/BroadcastRestService.java index b4e9573ec..52c868af1 100644 --- a/src/main/java/io/antmedia/rest/BroadcastRestService.java +++ b/src/main/java/io/antmedia/rest/BroadcastRestService.java @@ -35,18 +35,9 @@ import io.antmedia.statistic.type.WebRTCVideoSendStats; import io.antmedia.streamsource.StreamFetcher; import io.antmedia.webrtc.api.IWebRTCAdaptor; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.Contact; -import io.swagger.annotations.ExternalDocs; -import io.swagger.annotations.Info; -import io.swagger.annotations.License; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -59,20 +50,31 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; - -@Api(value = "BroadcastRestService") -@SwaggerDefinition( +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.License; +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 io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.servers.Server; + +@OpenAPIDefinition( info = @Info( - description = "Ant Media Server REST API Reference", + description = "Ant Media Server REST API for Broadcasts", version = "v2.0", title = "Ant Media Server REST API Reference", contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), - license = @License(name = "Apache 2.0", url = "http://www.apache.org")), - consumes = {"application/json"}, - produces = {"application/json"}, - schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, - externalDocs = @ExternalDocs(value = "External Docs", url = "https://antmedia.io"), - host = "test.antmedia.io:5443/Sandbox/rest/" + license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0")), + externalDocs = @ExternalDocumentation(description = "Rest Guide", url="https://antmedia.io/docs"), + servers = { + @Server( + description = "test server", + url = "https://test.antmedia.io:5443/Sandbox/rest/" + + )} + ) @Component @Path("/v2/broadcasts") @@ -85,9 +87,9 @@ public class BroadcastRestService extends RestServiceBase{ private static final String ABSOLUTE_MOVE = "absolute"; private static final String CONTINUOUS_MOVE = "continuous"; - @ApiModel(value="SimpleStat", description="Simple generic statistics class to return single values") + @Schema(description="Simple generic statistics class to return single values") public static class SimpleStat { - @ApiModelProperty(value = "the stat value") + @Schema(description = "the stat value") public long number; public SimpleStat(long number) { @@ -99,13 +101,13 @@ public long getNumber() { } } - @ApiModel(value="WebRTCSendStats", description="Aggregation of WebRTC Low Level Send Stats") + @Schema(description="Aggregation of WebRTC Low Level Send Stats") public static class WebRTCSendStats { - @ApiModelProperty(value = "Audio send stats") + @Schema(description = "Audio send stats") private final WebRTCAudioSendStats audioSendStats; - @ApiModelProperty(value = "Video send stats") + @Schema(description = "Video send stats") private final WebRTCVideoSendStats videoSendStats; public WebRTCSendStats(WebRTCAudioSendStats audioSendStats, WebRTCVideoSendStats videoSendStats) { @@ -122,13 +124,13 @@ public WebRTCAudioSendStats getAudioSendStats() { } } - @ApiModel(value="WebRTCReceiveStats", description="Aggregation of WebRTC Low Level Receive Stats") + @Schema(description="Aggregation of WebRTC Low Level Receive Stats") public static class WebRTCReceiveStats { - @ApiModelProperty(value = "Audio receive stats") + @Schema(description = "Audio receive stats") private final WebRTCAudioReceiveStats audioReceiveStats; - @ApiModelProperty(value = "Video receive stats") + @Schema(description = "Video receive stats") private final WebRTCVideoReceiveStats videoReceiveStats; public WebRTCReceiveStats(WebRTCAudioReceiveStats audioReceiveStats, WebRTCVideoReceiveStats videoReceiveStats) { @@ -146,18 +148,27 @@ public WebRTCAudioReceiveStats getAudioReceiveStats() { } - @ApiOperation(value = "Creates a Broadcast, IP Camera or Stream Source and returns the full broadcast object with rtmp address and " + @Operation(description = "Creates a Broadcast, IP Camera or Stream Source and returns the full broadcast object with rtmp address and " + "other information. The different between Broadcast and IP Camera or Stream Source is that Broadcast is ingested by Ant Media Server" + "IP Camera or Stream Source is pulled by Ant Media Server") - @ApiResponses(value = { @ApiResponse(code = 400, message = "If stream id is already used in the data store, it returns error", response=Result.class), - @ApiResponse(code = 200, message = "Returns the created stream", response = Broadcast.class)}) + @ApiResponse(responseCode = "400", description = "If stream id is already used in the data store, it returns error", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + ) + ) + @ApiResponse(responseCode = "200", description = "Returns the created stream", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Broadcast.class) + ) + ) @POST @Consumes({ MediaType.APPLICATION_JSON }) @Path("/create") - @ApiModelProperty(readOnly = true) @Produces(MediaType.APPLICATION_JSON) - public Response createBroadcast(@ApiParam(value = "Broadcast object. Set the required fields, it may be null as well.", required = false) Broadcast broadcast, - @ApiParam(value = "Only effective if stream is IP Camera or Stream Source. If it's true, it starts automatically pulling stream. Its value is false by default", required = false, defaultValue="false") @QueryParam("autoStart") boolean autoStart) { + public Response createBroadcast(@Parameter(description = "Broadcast object. Set the required fields, it may be null as well.", required = false) Broadcast broadcast, + @Parameter(description = "Only effective if stream is IP Camera or Stream Source. If it's true, it starts automatically pulling stream. Its value is false by default", required = false) @QueryParam("autoStart") boolean autoStart) { if (broadcast != null && broadcast.getStreamId() != null) { @@ -219,37 +230,52 @@ else if (!StreamIdValidator.isStreamIdValid(broadcast.getStreamId())) return Response.status(Status.OK).entity(returnObject).build(); } - @ApiOperation(value = "Delete broadcast from data store and stop if it's broadcasting", response = Result.class) - @ApiResponses(value = { @ApiResponse(code = 200, message = "If it's deleted, success is true. If it's not deleted, success if false.") }) + @Operation(summary = "Delete broadcast from data store and stop if it's broadcasting") + @ApiResponse(responseCode = "200", description = "If it's deleted, success is true. If it's not deleted, success if false.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Broadcast.class) + ) + ) @DELETE @Consumes({ MediaType.APPLICATION_JSON }) @Path("/{id}") @Produces(MediaType.APPLICATION_JSON) @Override - public Result deleteBroadcast(@ApiParam(value = " Id of the broadcast", required = true) @PathParam("id") String id) { + public Result deleteBroadcast(@Parameter(description = " Id of the broadcast", required = true) @PathParam("id") String id) { return super.deleteBroadcast(id); } - @ApiOperation(value = "Delete multiple broadcasts from data store and stop if they are broadcasting", response = Result.class) - @ApiResponses(value = { @ApiResponse(code = 200, message = "If it's deleted, success is true. If it's not deleted, success if false.") }) + @Operation(description = "Delete multiple broadcasts from data store and stop if they are broadcasting") + @ApiResponse(responseCode = "200", description = "If it's deleted, success is true. If it's not deleted, success if false.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Broadcast.class) + ) + ) @DELETE @Consumes({ MediaType.APPLICATION_JSON }) @Path("/bulk") @Produces(MediaType.APPLICATION_JSON) @Override - public Result deleteBroadcasts(@ApiParam(value = " Id of the broadcast", required = true) String[] streamIds) + public Result deleteBroadcasts(@Parameter(description = " Id of the broadcast", required = true) String[] streamIds) { return super.deleteBroadcasts(streamIds); } - @ApiOperation(value = "Get broadcast object") - @ApiResponses(value = { @ApiResponse(code = 200, message = "Return the broadcast object"), - @ApiResponse(code = 404, message = "Broadcast object not found")}) + @Operation(description = "Get broadcast object") + @ApiResponse(responseCode = "200", description = "Return the broadcast object", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Broadcast.class) + ) + ) + @ApiResponse(responseCode = "404", description = "Broadcast object not found") @GET @Path("/{id}") @Produces(MediaType.APPLICATION_JSON) - public Response getBroadcast(@ApiParam(value = "id of the broadcast", required = true) @PathParam("id") String id) { + public Response getBroadcast(@Parameter(description = "id of the broadcast", required = true) @PathParam("id") String id) { Broadcast broadcast = null; if (id != null) { broadcast = lookupBroadcast(id); @@ -262,32 +288,34 @@ public Response getBroadcast(@ApiParam(value = "id of the broadcast", required = } } - @ApiOperation(value = "Gets the broadcast list from database. It returns max 50 items at a time", notes = "",responseContainer = "List", response = Broadcast.class) + @Operation(description = "Gets the broadcast list from database. It returns max 50 items at a time") @GET @Path("/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List getBroadcastList(@ApiParam(value = "This is the offset of the list, it is useful for pagination. If you want to use sort mechanism, we recommend using Mongo DB.", required = true) @PathParam("offset") int offset, - @ApiParam(value = "Number of items that will be fetched. If there is not enough item in the datastore, returned list size may less then this value", required = true) @PathParam("size") int size, - @ApiParam(value = "Type of the stream. Possible values are \"liveStream\", \"ipCamera\", \"streamSource\", \"VoD\"", required = false) @QueryParam("type_by") String typeBy, - @ApiParam(value = "Field to sort. Possible values are \"name\", \"date\", \"status\"", required = false) @QueryParam("sort_by") String sortBy, - @ApiParam(value = "\"asc\" for Ascending, \"desc\" Descending order", required = false) @QueryParam("order_by") String orderBy, - @ApiParam(value = "Search parameter, returns specific items that contains search string", required = false) @QueryParam("search") String search + public List getBroadcastList(@Parameter(description = "This is the offset of the list, it is useful for pagination. If you want to use sort mechanism, we recommend using Mongo DB.", required = true) @PathParam("offset") int offset, + @Parameter(description = "Number of items that will be fetched. If there is not enough item in the datastore, returned list size may less then this value", required = true) @PathParam("size") int size, + @Parameter(description = "Type of the stream. Possible values are \"liveStream\", \"ipCamera\", \"streamSource\", \"VoD\"", required = false) @QueryParam("type_by") String typeBy, + @Parameter(description = "Field to sort. Possible values are \"name\", \"date\", \"status\"", required = false) @QueryParam("sort_by") String sortBy, + @Parameter(description = "\"asc\" for Ascending, \"desc\" Descending order", required = false) @QueryParam("order_by") String orderBy, + @Parameter(description = "Search parameter, returns specific items that contains search string", required = false) @QueryParam("search") String search ) { return getDataStore().getBroadcastList(offset, size, typeBy, sortBy, orderBy, search); } - @ApiOperation(value = "Updates the Broadcast objects fields if it's not null." + + @Operation(description = "Updates the Broadcast objects fields if it's not null." + " The updated fields are as follows: name, description, userName, password, IP address, streamUrl of the broadcast. " + - "It also updates the social endpoints", notes = "", response = Result.class) - @ApiResponses(value = { @ApiResponse(code = 200, message = "If it's updated, success field is true. If it's not updated, success field if false.")}) + "It also updates the social endpoints") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "If it's updated, success field is true. If it's not updated, success field is false.") + }) @PUT @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}") @Produces(MediaType.APPLICATION_JSON) @Override - public Result updateBroadcast(@ApiParam(value="Broadcast id", required = true) @PathParam("id") String id, - @ApiParam(value="Broadcast object with the updates") Broadcast broadcast) { + public Result updateBroadcast(@Parameter(description="Broadcast id", required = true) @PathParam("id") String id, + @Parameter(description="Broadcast object with the updates") Broadcast broadcast) { Result result = new Result(false); if (id != null && broadcast != null) { @@ -307,20 +335,19 @@ public Result updateBroadcast(@ApiParam(value="Broadcast id", required = true) @ } return result; } - - @ApiOperation(value = "Seeks the playing stream source, vod or playlist on the fly. It accepts seekTimeMs parameter in milliseconds" - , notes = "", response = Result.class) + + @Operation(description = "Seeks the playing stream source, vod or playlist on the fly. It accepts seekTimeMs parameter in milliseconds") @PUT @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/seek-time/{seekTimeMs}") @Produces(MediaType.APPLICATION_JSON) - public Result updateSeekTime(@ApiParam(value="Broadcast id", required = true) @PathParam("id") String id, - @ApiParam(value="Seek time in milliseconds", required = true) @PathParam("seekTimeMs") long seekTimeMs) { + public Result updateSeekTime(@Parameter(description="Broadcast id", required = true) @PathParam("id") String id, + @Parameter(description="Seek time in milliseconds", required = true) @PathParam("seekTimeMs") long seekTimeMs) { Result result = new Result(false); if (StringUtils.isNotBlank(id)) { - + StreamFetcher streamFetcher = getApplication().getStreamFetcherManager().getStreamFetcher(id); if (streamFetcher != null) { streamFetcher.seekTime(seekTimeMs); @@ -334,7 +361,7 @@ public Result updateSeekTime(@ApiParam(value="Broadcast id", required = true) @P result.setMessage("Id field is blank."); } return result; - + } @Deprecated @@ -342,8 +369,8 @@ public Result updateSeekTime(@ApiParam(value="Broadcast id", required = true) @P @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/endpoint") @Produces(MediaType.APPLICATION_JSON) - public Result addEndpointV2(@ApiParam(value = "Broadcast id", required = true) @PathParam("id") String id, - @ApiParam(value = "RTMP url of the endpoint that stream will be republished. If required, please encode the URL", required = true) @QueryParam("rtmpUrl") String rtmpUrl) { + public Result addEndpointV2(@Parameter(description = "Broadcast id", required = true) @PathParam("id") String id, + @Parameter(description = "RTMP url of the endpoint that stream will be republished. If required, please encode the URL", required = true) @QueryParam("rtmpUrl") String rtmpUrl) { Result result = super.addEndpoint(id, rtmpUrl); if (result.isSuccess()) @@ -363,14 +390,23 @@ public Result addEndpointV2(@ApiParam(value = "Broadcast id", required = true) @ return result; } - @ApiOperation(value = "Adds a third party rtmp end point to the stream. It supports adding after broadcast is started. Resolution can be specified to send a specific adaptive resolution. If an url is already added to a stream, trying to add the same rtmp url will return false.", notes = "", response = Result.class) + @Operation(summary = "Adds a third party RTMP end point to the stream", + description = "It supports adding after broadcast is started. Resolution can be specified to send a specific adaptive resolution. If an URL is already added to a stream, trying to add the same RTMP URL will return false.", + responses = { + @ApiResponse(responseCode = "200", description = "Add RTMP endpoint response", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/rtmp-endpoint") @Produces(MediaType.APPLICATION_JSON) - public Result addEndpointV3(@ApiParam(value = "Broadcast id", required = true) @PathParam("id") String id, - @ApiParam(value = "RTMP url of the endpoint that stream will be republished. If required, please encode the URL", required = true) Endpoint endpoint, - @ApiParam(value = "Resolution height of the broadcast that is wanted to send to the RTMP endpoint. ", required = false) @QueryParam("resolutionHeight") int resolutionHeight) { + public Result addEndpointV3(@Parameter(description = "Broadcast id", required = true) @PathParam("id") String id, + @Parameter(description = "RTMP url of the endpoint that stream will be republished. If required, please encode the URL", required = true) Endpoint endpoint, + @Parameter(description = "Resolution height of the broadcast that is wanted to send to the RTMP endpoint. ", required = false) @QueryParam("resolutionHeight") int resolutionHeight) { String rtmpUrl = null; Result result = new Result(false); @@ -430,8 +466,8 @@ private void logRtmpEndpointInfo(String id, Endpoint endpoint, boolean result) { @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/endpoint") @Produces(MediaType.APPLICATION_JSON) - public Result removeEndpoint(@ApiParam(value = "Broadcast id", required = true) @PathParam("id") String id, - @ApiParam(value = "RTMP url of the endpoint that will be stopped.", required = true) @QueryParam("rtmpUrl") String rtmpUrl ) { + public Result removeEndpoint(@Parameter(description = "Broadcast id", required = true) @PathParam("id") String id, + @Parameter(description = "RTMP url of the endpoint that will be stopped.", required = true) @QueryParam("rtmpUrl") String rtmpUrl ) { Result result = super.removeEndpoint(id, rtmpUrl); if (result.isSuccess()) { @@ -451,14 +487,23 @@ public Result removeEndpoint(@ApiParam(value = "Broadcast id", required = true) return result; } - @ApiOperation(value = "Remove third pary rtmp end point from the stream. For the stream that is broadcasting, it will stop immediately", notes = "", response = Result.class) + @Operation(summary = "Remove third-party RTMP end point from the stream", + description = "For the stream that is broadcasting, it will stop immediately.", + responses = { + @ApiResponse(responseCode = "200", description = "Remove RTMP endpoint response", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @DELETE @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/rtmp-endpoint") @Produces(MediaType.APPLICATION_JSON) - public Result removeEndpointV2(@ApiParam(value = "Broadcast id", required = true) @PathParam("id") String id, - @ApiParam(value = "RTMP url of the endpoint that will be stopped.", required = true) @QueryParam("endpointServiceId") String endpointServiceId, - @ApiParam(value = "Resolution specifier if endpoint has been added with resolution. Only applicable if user added RTMP endpoint with a resolution speficier. Otherwise won't work and won't remove the endpoint.", required = true) + public Result removeEndpointV2(@Parameter(description = "Broadcast id", required = true) @PathParam("id") String id, + @Parameter(description = "RTMP url of the endpoint that will be stopped.", required = true) @QueryParam("endpointServiceId") String endpointServiceId, + @Parameter(description = "Resolution specifier if endpoint has been added with resolution. Only applicable if user added RTMP endpoint with a resolution speficier. Otherwise won't work and won't remove the endpoint.") @QueryParam("resolutionHeight") int resolutionHeight){ //Get rtmpURL with broadcast @@ -514,25 +559,52 @@ private Endpoint getRtmpUrlFromList(String endpointServiceId, Broadcast broadcas } - @ApiOperation(value = "Get detected objects from the stream based on offset and size", notes = "",responseContainer = "List", response = TensorFlowObject.class) + @Operation(summary = "Retrieve detected objects from the stream", + description = "Fetches detected objects from the stream, using specified offset and size parameters.", + responses = { + @ApiResponse(responseCode = "200", description = "List of detected TensorFlow objects", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = TensorFlowObject.class, type = "array") + )) + } + ) @GET @Path("/{id}/detections/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List getDetectionListV2(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String id, - @ApiParam(value = "starting point of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "total size of the return list", required = true) @PathParam("size") int size) { + public List getDetectionListV2(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String id, + @Parameter(description = "starting point of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "total size of the return list", required = true) @PathParam("size") int size) { return super.getDetectionList(id, offset, size); } - @ApiOperation(value = "Get total number of detected objects", notes = "", response = Long.class) + @Operation(summary = "Get total number of detected objects", + description = "Retrieves the total count of objects detected.", + responses = { + @ApiResponse(responseCode = "200", description = "Total number of detected objects", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Long.class) + )) + } + ) @GET @Path("/{id}/detections/count") @Produces(MediaType.APPLICATION_JSON) - public SimpleStat getObjectDetectedTotal(@ApiParam(value = "id of the stream", required = true) @PathParam("id") String id){ + public SimpleStat getObjectDetectedTotal(@Parameter(description = "id of the stream", required = true) @PathParam("id") String id){ return new SimpleStat(getDataStore().getObjectDetectedTotal(id)); } - @ApiOperation(value = "Import Live Streams to Stalker Portal", notes = "", response = Result.class) + @Operation(summary = "Import Live Streams to Stalker Portal", + description = "Imports live streams into the Stalker Portal.", + responses = { + @ApiResponse(responseCode = "200", description = "Import operation result", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Path("/import-to-stalker") @Produces(MediaType.APPLICATION_JSON) @@ -542,7 +614,16 @@ public Result importLiveStreams2StalkerV2() } - @ApiOperation(value = "Get the total number of broadcasts", notes = "", response = SimpleStat.class) + @Operation(summary = "Get the total number of broadcasts", + description = "Retrieves the total number of broadcasts.", + responses = { + @ApiResponse(responseCode = "200", description = "Total number of broadcasts", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SimpleStat.class) + )) + } + ) @GET @Path("/count") @Produces(MediaType.APPLICATION_JSON) @@ -550,17 +631,35 @@ public SimpleStat getTotalBroadcastNumberV2() { return new SimpleStat(getDataStore().getTotalBroadcastNumber()); } - @ApiOperation(value = "Get the number of broadcasts depending on the searched items ", notes = "", response = SimpleStat.class) + @Operation(summary = "Get the number of broadcasts based on search criteria", + description = "Retrieves the number of broadcasts matching the specified search criteria.", + responses = { + @ApiResponse(responseCode = "200", description = "Number of broadcasts for searched items", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SimpleStat.class) + )) + } + ) @GET @Path("/count/{search}") @Produces(MediaType.APPLICATION_JSON) public SimpleStat getTotalBroadcastNumberV2( - @ApiParam(value = "Search parameter to get the number of items including it ", required = true) @PathParam("search") String search) + @Parameter(description = "Search parameter to get the number of items including it ", required = true) @PathParam("search") String search) { return new SimpleStat(getDataStore().getPartialBroadcastNumber(search)); } - @ApiOperation(value = "Return the active live streams", notes = "", response = SimpleStat.class) + @Operation(summary = "Return the active live streams", + description = "Retrieves the currently active live streams.", + responses = { + @ApiResponse(responseCode = "200", description = "Active live streams", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SimpleStat.class) + )) + } + ) @GET @Path("/active-live-stream-count") @Produces(MediaType.APPLICATION_JSON) @@ -571,16 +670,22 @@ public SimpleStat getAppLiveStatistics() { - @ApiOperation(value = "Generates random one-time token for specified stream") - @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns token", response=Token.class), - @ApiResponse(code = 400, message = "When there is an error in creating token", response=Result.class)}) + @Operation(description = "Generates random one-time token for specified stream") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Returns token", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Token.class))), + @ApiResponse(responseCode = "400", description = "When there is an error in creating token", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Result.class))) + }) @GET @Path("/{id}/token") @Produces(MediaType.APPLICATION_JSON) - public Response getTokenV2 (@ApiParam(value = "The id of the stream", required = true) @PathParam("id")String streamId, - @ApiParam(value = "The expire time of the token. It's in unix timestamp seconds", required = true) @QueryParam("expireDate") long expireDate, - @ApiParam(value = "Type of the token. It may be play or publish ", required = true) @QueryParam("type") String type, - @ApiParam(value = "Room Id that token belongs to. It's not mandatory ", required = false) @QueryParam("roomId") String roomId) + public Response getTokenV2 (@Parameter(description = "The id of the stream", required = true) @PathParam("id")String streamId, + @Parameter(description = "The expire time of the token. It's in unix timestamp seconds", required = true) @QueryParam("expireDate") long expireDate, + @Parameter(description = "Type of the token. It may be play or publish ", required = true) @QueryParam("type") String type, + @Parameter(description = "Room Id that token belongs to. It's not mandatory ", required = false) @QueryParam("roomId") String roomId) { Object result = super.getToken(streamId, expireDate, type, roomId); if (result instanceof Token) { @@ -592,16 +697,26 @@ public Response getTokenV2 (@ApiParam(value = "The id of the stream", required = } - @ApiOperation(value = "Generates JWT token for specified stream. It's not required to let the server generate JWT. Generally JWT tokens should be generated on the client side.") - @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns token", response=Token.class), - @ApiResponse(code = 400, message = "When there is an error in creating token", response=Result.class)}) + @Operation(description = "Generates JWT token for specified stream. It's not required to let the server generate JWT. Generally JWT tokens should be generated on the client side.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Returns token", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Token.class) + )), + @ApiResponse(responseCode = "400", description = "When there is an error in creating token", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + }) @GET @Path("/{id}/jwt-token") @Produces(MediaType.APPLICATION_JSON) - public Response getJwtTokenV2 (@ApiParam(value = "The id of the stream", required = true) @PathParam("id")String streamId, - @ApiParam(value = "The expire time of the token. It's in unix timestamp seconds.", required = true) @QueryParam("expireDate") long expireDate, - @ApiParam(value = "Type of the JWT token. It may be play or publish ", required = true) @QueryParam("type") String type, - @ApiParam(value = "Room Id that token belongs to. It's not mandatory ", required = false) @QueryParam("roomId") String roomId) + public Response getJwtTokenV2 (@Parameter(description = "The id of the stream", required = true) @PathParam("id")String streamId, + @Parameter(description = "The expire time of the token. It's in unix timestamp seconds.", required = true) @QueryParam("expireDate") long expireDate, + @Parameter(description = "Type of the JWT token. It may be play or publish ", required = true) @QueryParam("type") String type, + @Parameter(description = "Room Id that token belongs to. It's not mandatory ", required = false) @QueryParam("roomId") String roomId) { Object result = super.getJwtToken(streamId, expireDate, type, roomId); if (result instanceof Token) { @@ -612,13 +727,21 @@ public Response getJwtTokenV2 (@ApiParam(value = "The id of the stream", require } } - @ApiOperation(value = "Perform validation of token for requested stream. If validated, success field is true, " - + "not validated success field false", response = Result.class) + @Operation(summary = "Perform validation of token for requested stream", + description = "If validated, success field is true, not validated success field is false", + responses = { + @ApiResponse(responseCode = "200", description = "Token validation response", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/validate-token") @Produces(MediaType.APPLICATION_JSON) - public Result validateTokenV2(@ApiParam(value = "Token to be validated", required = true) Token token) + public Result validateTokenV2(@Parameter(description = "Token to be validated", required = true) Token token) { boolean result = false; Token validateToken = super.validateToken(token); @@ -630,23 +753,41 @@ public Result validateTokenV2(@ApiParam(value = "Token to be validated", require } - @ApiOperation(value = "Removes all tokens related with requested stream", notes = "", response = Result.class) + @Operation(summary = "Removes all tokens related with requested stream", + description = "", + responses = { + @ApiResponse(responseCode = "200", description = "Removal of tokens response", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @DELETE @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/tokens") @Produces(MediaType.APPLICATION_JSON) - public Result revokeTokensV2(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId) { + public Result revokeTokensV2(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId) { return super.revokeTokens(streamId); } - @ApiOperation(value = "Get the all tokens of requested stream", notes = "",responseContainer = "List", response = Token.class) + @Operation(summary = "Get all tokens of requested stream", + description = "", + responses = { + @ApiResponse(responseCode = "200", description = "List of tokens", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Token.class, type = "array") + )) + } + ) @GET @Path("/{id}/tokens/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List listTokensV2(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "the starting point of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { + public List listTokensV2(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "the starting point of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { List tokens = null; if(streamId != null) { tokens = getDataStore().listAllTokens(streamId, offset, size); @@ -654,13 +795,22 @@ public List listTokensV2(@ApiParam(value = "the id of the stream", requir return tokens; } - @ApiOperation(value = "Get the all subscribers of the requested stream. It does not return subscriber-stats. Please use subscriber-stats method", notes = "",responseContainer = "List", response = Subscriber.class) + @Operation(summary = "Get all subscribers of the requested stream", + description = "It does not return subscriber-stats. Please use subscriber-stats method", + responses = { + @ApiResponse(responseCode = "200", description = "List of subscribers", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Subscriber.class, type = "array") + )) + } + ) @GET @Path("/{id}/subscribers/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List listSubscriberV2(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "the starting point of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { + public List listSubscriberV2(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "the starting point of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { List subscribers = null; if(streamId != null) { subscribers = getDataStore().listAllSubscribers(streamId, offset, size); @@ -668,13 +818,22 @@ public List listSubscriberV2(@ApiParam(value = "the id of the stream return subscribers; } - @ApiOperation(value = "Get the all subscriber statistics of the requested stream", notes = "",responseContainer = "List", response = SubscriberStats.class) + @Operation(summary = "Retrieve all subscriber statistics of the requested stream", + description = "Fetches comprehensive statistics for all subscribers of the specified stream.", + responses = { + @ApiResponse(responseCode = "200", description = "List of subscriber statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SubscriberStats.class, type = "array") + )) + } + ) @GET @Path("/{id}/subscriber-stats/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List listSubscriberStatsV2(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "the starting point of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { + public List listSubscriberStatsV2(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "the starting point of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { List subscriberStats = null; if(streamId != null) { subscriberStats = getDataStore().listAllSubscriberStats(streamId, offset, size); @@ -682,15 +841,23 @@ public List listSubscriberStatsV2(@ApiParam(value = "the id of return subscriberStats; } - @ApiOperation(value = "Add Subscriber to the requested stream. If the subscriber's type is publish, it also can play the stream which is critical in conferencing" - + "If the subscriber's type is play, it only play the stream. If b32Secret is not set, it will use from the AppSettings. b32Secret's length should be multiple of 8 and use b32 characters A–Z, 2–7", response = Result.class) + @Operation(summary = "Add Subscriber to the requested stream", + description = "Adds a subscriber to the requested stream. If the subscriber's type is 'publish', they can also play the stream, which is critical in conferencing. If the subscriber's type is 'play', they can only play the stream. If 'b32Secret' is not set, it will default to the AppSettings. The length of 'b32Secret' should be a multiple of 8 and use base32 characters A–Z, 2–7.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of adding a subscriber", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/subscribers") @Produces(MediaType.APPLICATION_JSON) public Result addSubscriber( - @ApiParam(value = "The id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "Subscriber to be added to this stream", required = true) Subscriber subscriber) { + @Parameter(description = "The id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "Subscriber to be added to this stream", required = true) Subscriber subscriber) { boolean result = false; String message = ""; if (subscriber != null && !StringUtils.isBlank(subscriber.getSubscriberId()) @@ -732,7 +899,7 @@ public Result addSubscriber( return new Result(result, message); } - @ApiOperation(value="Return TOTP for the subscriberId, streamId, type. This is a helper method. You can generate TOTP on your end." + @Operation(description="Return TOTP for the subscriberId, streamId, type. This is a helper method. You can generate TOTP on your end." + "If subscriberId is not in the database, it generates TOTP from the secret in the AppSettings. Secret code is for the subscriberId not in the database" + " secretCode = Base32.encodeAsString({secretFromSettings(publishsecret or playsecret according to the type)} + {subscriberId} + {streamId} + {type(publish or play)} + {Number of X to have the length multiple of 8}" @@ -741,9 +908,9 @@ public Result addSubscriber( @Consumes({ MediaType.APPLICATION_JSON }) @Path("/{id}/subscribers/{sid}/totp") @Produces(MediaType.APPLICATION_JSON) - public Result getTOTP(@ApiParam(value="The id of the stream that TOTP will be generated", required=true) @PathParam("id") String streamId, - @ApiParam(value="The id of the subscriber that TOTP will be generated ", required=true) @PathParam("sid") String subscriberId, - @ApiParam(value="The type of token. It's being used if subscriber is not in the database. It can be publish, play", + public Result getTOTP(@Parameter(description="The id of the stream that TOTP will be generated", required=true) @PathParam("id") String streamId, + @Parameter(description="The id of the subscriber that TOTP will be generated ", required=true) @PathParam("sid") String subscriberId, + @Parameter(description="The type of token. It's being used if subscriber is not in the database. It can be publish, play", required=false) @QueryParam("type") String type) { @@ -789,13 +956,22 @@ public Result getTOTP(@ApiParam(value="The id of the stream that TOTP will be ge } - @ApiOperation(value = "Delete specific subscriber from data store for selected stream", response = Result.class) + @Operation(summary = "Delete specific subscriber from data store", + description = "Deletes a specific subscriber from the data store for the selected stream.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of deleting the subscriber", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @DELETE @Consumes({ MediaType.APPLICATION_JSON }) @Path("/{id}/subscribers/{sid}") @Produces(MediaType.APPLICATION_JSON) - public Result deleteSubscriber(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "the id of the subscriber", required = true) @PathParam("sid") String subscriberId) { + public Result deleteSubscriber(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "the id of the subscriber", required = true) @PathParam("sid") String subscriberId) { boolean result = false; if(streamId != null) { @@ -805,15 +981,24 @@ public Result deleteSubscriber(@ApiParam(value = "the id of the stream", require return new Result(result); } - @ApiOperation(value = "Block specific subscriber. It's secure to use this with TOTP streaming. It blocks the subscriber for seconds from the moment this method is called", response = Result.class) + @Operation(summary = "Block specific subscriber", + description = "Blocks a specific subscriber, enhancing security especially when used with TOTP streaming. The subscriber is blocked for a specified number of seconds from the moment this method is called.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of blocking the subscriber", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @PUT @Consumes({ MediaType.APPLICATION_JSON }) @Path("/{id}/subscribers/{sid}/block/{seconds}/{type}") @Produces(MediaType.APPLICATION_JSON) - public Result blockSubscriber(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "the id of the subscriber", required = true) @PathParam("sid") String subscriberId, - @ApiParam(value = "seconds to block the user", required = true) @PathParam("seconds") int seconds, - @ApiParam(value = "block type it can be 'publish', 'play' or 'publish_play'", required = true) @PathParam("type") String blockType) { + public Result blockSubscriber(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "the id of the subscriber", required = true) @PathParam("sid") String subscriberId, + @Parameter(description = "seconds to block the user", required = true) @PathParam("seconds") int seconds, + @Parameter(description = "block type it can be 'publish', 'play' or 'publish_play'", required = true) @PathParam("type") String blockType) { boolean result = false; String message = ""; @@ -844,12 +1029,21 @@ public Result blockSubscriber(@ApiParam(value = "the id of the stream", required return new Result(result, message); } - @ApiOperation(value = " Removes all subscriber related with the requested stream", notes = "", response = Result.class) + @Operation(summary = "Removes all subscribers related to the requested stream", + description = "Deletes all subscriber data associated with the specified stream.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of removing all subscribers", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @DELETE @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/subscribers") @Produces(MediaType.APPLICATION_JSON) - public Result revokeSubscribers(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId) { + public Result revokeSubscribers(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId) { boolean result = false; if(streamId != null) { @@ -859,16 +1053,34 @@ public Result revokeSubscribers(@ApiParam(value = "the id of the stream", requir return new Result(result); } - @ApiOperation(value = "Get the broadcast live statistics total RTMP watcher count, total HLS watcher count, total WebRTC watcher count", notes = "", response = BroadcastStatistics.class) + @Operation(summary = "Get the broadcast live statistics", + description = "Retrieves live statistics of the broadcast, including total RTMP watcher count, total HLS watcher count, and total WebRTC watcher count.", + responses = { + @ApiResponse(responseCode = "200", description = "Broadcast live statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = BroadcastStatistics.class) + )) + } + ) @GET @Path("/{id}/broadcast-statistics") @Produces(MediaType.APPLICATION_JSON) @Override - public BroadcastStatistics getBroadcastStatistics(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String id) { + public BroadcastStatistics getBroadcastStatistics(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String id) { return super.getBroadcastStatistics(id); } - @ApiOperation(value = "Get the total broadcast live statistics total HLS watcher count, total WebRTC watcher count", notes = "", response = BroadcastStatistics.class) + @Operation(summary = "Get total broadcast live statistics", + description = "Retrieves total live statistics of the broadcast, including total HLS watcher count and total WebRTC watcher count.", + responses = { + @ApiResponse(responseCode = "200", description = "Total broadcast live statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = BroadcastStatistics.class) + )) + } + ) @GET @Path("/total-broadcast-statistics") @Produces(MediaType.APPLICATION_JSON) @@ -877,7 +1089,16 @@ public AppBroadcastStatistics getBroadcastTotalStatistics() { return super.getBroadcastTotalStatistics(); } - @ApiOperation(value = "Get WebRTC Low Level Send stats in general", notes = "",response = WebRTCSendStats.class) + @Operation(summary = "Get WebRTC Low Level Send Stats", + description = "Retrieves general statistics for WebRTC low level send operations.", + responses = { + @ApiResponse(responseCode = "200", description = "WebRTC low level send statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = WebRTCSendStats.class) + )) + } + ) @GET @Path("/webrtc-send-low-level-stats") @Produces(MediaType.APPLICATION_JSON) @@ -886,7 +1107,16 @@ public WebRTCSendStats getWebRTCLowLevelSendStats() return new WebRTCSendStats(getApplication().getWebRTCAudioSendStats(), getApplication().getWebRTCVideoSendStats()); } - @ApiOperation(value = "Get WebRTC Low Level receive stats in general", notes = "",response = WebRTCSendStats.class) + @Operation(summary = "Get WebRTC Low Level Receive Stats", + description = "Retrieves general statistics for WebRTC low level receive operations.", + responses = { + @ApiResponse(responseCode = "200", description = "WebRTC low level receive statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = WebRTCReceiveStats.class) + )) + } + ) @GET @Path("/webrtc-receive-low-level-stats") @Produces(MediaType.APPLICATION_JSON) @@ -895,51 +1125,88 @@ public WebRTCReceiveStats getWebRTCLowLevelReceiveStats() return new WebRTCReceiveStats(getApplication().getWebRTCAudioReceiveStats(), getApplication().getWebRTCVideoReceiveStats()); } - @ApiOperation(value = "Get RTMP to WebRTC path stats in general", notes = "",response = RTMPToWebRTCStats.class) + @Operation(summary = "Get RTMP to WebRTC Path Stats", + description = "Retrieves general statistics for the RTMP to WebRTC path.", + responses = { + @ApiResponse(responseCode = "200", description = "RTMP to WebRTC path statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = RTMPToWebRTCStats.class) + )) + } + ) @GET @Path("/{id}/rtmp-to-webrtc-stats") @Produces(MediaType.APPLICATION_JSON) - public RTMPToWebRTCStats getRTMPToWebRTCStats(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String id) + public RTMPToWebRTCStats getRTMPToWebRTCStats(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String id) { return getApplication().getRTMPToWebRTCStats(id); } - @ApiOperation(value = "Get WebRTC Client Statistics such as : Audio bitrate, Video bitrate, Target bitrate, Video Sent Period etc.", notes = "", responseContainer = "List",response = WebRTCClientStats.class) + @Operation(summary = "Get WebRTC Client Statistics", + description = "Retrieves WebRTC client statistics, including audio bitrate, video bitrate, target bitrate, video sent period, etc.", + responses = { + @ApiResponse(responseCode = "200", description = "WebRTC client statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = WebRTCClientStats.class, type = "array") + )) + } + ) @GET @Path("/{stream_id}/webrtc-client-stats/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List getWebRTCClientStatsListV2(@ApiParam(value = "offset of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "Number of items that will be fetched", required = true) @PathParam("size") int size, - @ApiParam(value = "the id of the stream", required = true) @PathParam("stream_id") String streamId) { + public List getWebRTCClientStatsListV2(@Parameter(description = "offset of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "Number of items that will be fetched", required = true) @PathParam("size") int size, + @Parameter(description = "the id of the stream", required = true) @PathParam("stream_id") String streamId) { return super.getWebRTCClientStatsList(offset, size, streamId); } @Deprecated - @ApiOperation(value = "Returns filtered broadcast list according to type. It's useful for getting IP Camera and Stream Sources from the whole list. If you want to use sort mechanism, we recommend using Mongo DB.", notes = "",responseContainer = "List",response = Broadcast.class) + @Operation(summary = "Returns filtered broadcast list according to type", + description = "Useful for retrieving IP Camera and Stream Sources from the entire broadcast list. For sorting mechanisms, using Mongo DB is recommended.", + responses = { + @ApiResponse(responseCode = "200", description = "Filtered broadcast list", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Broadcast.class, type = "array") + )) + } + ) @GET @Consumes(MediaType.APPLICATION_JSON) @Path("/filter-list/{offset}/{size}/{type}") @Produces(MediaType.APPLICATION_JSON) - public List filterBroadcastListV2(@ApiParam(value = "starting point of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "size of the return list (max:50 )", required = true) @PathParam("size") int size, - @ApiParam(value = "type of the stream. Possible values are \"liveStream\", \"ipCamera\", \"streamSource\", \"VoD\"", required = true) @PathParam("type") String type, - @ApiParam(value = "field to sort", required = false) @QueryParam("sort_by") String sortBy, - @ApiParam(value = "asc for Ascending, desc Descending order", required = false) @QueryParam("order_by") String orderBy + public List filterBroadcastListV2(@Parameter(description = "starting point of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "size of the return list (max:50 )", required = true) @PathParam("size") int size, + @Parameter(description = "type of the stream. Possible values are \"liveStream\", \"ipCamera\", \"streamSource\", \"VoD\"", required = true) @PathParam("type") String type, + @Parameter(description = "field to sort", required = false) @QueryParam("sort_by") String sortBy, + @Parameter(description = "asc for Ascending, desc Descending order", required = false) @QueryParam("order_by") String orderBy ) { return getDataStore().getBroadcastList(offset, size, type, sortBy, orderBy, null); } - @ApiOperation(value = "Set stream specific recording setting, this setting overrides general Mp4 and WebM Muxing Setting", notes = "", response = Result.class) + + @Operation(summary = "Set stream specific recording setting", + description = "This setting overrides the general Mp4 and WebM Muxing Setting for a specific stream.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of setting stream specific recording", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @PUT @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/recording/{recording-status}") @Produces(MediaType.APPLICATION_JSON) - public Result enableRecording(@ApiParam(value = "the id of the stream", required = true) @PathParam("id") String streamId, - @ApiParam(value = "Change recording status. If true, starts recording. If false stop recording", required = true) @PathParam("recording-status") boolean enableRecording, - @ApiParam(value = "Record type: 'mp4' or 'webm'. It's optional parameter.", required = false) @QueryParam("recordType") String recordType, - @ApiParam(value = "Resolution height of the broadcast that is wanted to record. ", required = false) @QueryParam("resolutionHeight") int resolutionHeight + public Result enableRecording(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "Change recording status. If true, starts recording. If false stop recording", required = true) @PathParam("recording-status") boolean enableRecording, + @Parameter(description = "Record type: 'mp4' or 'webm'. It's optional parameter.", required = false) @QueryParam("recordType") String recordType, + @Parameter(description = "Resolution height of the broadcast that is wanted to record. ", required = false) @QueryParam("resolutionHeight") int resolutionHeight ) { if (logger.isInfoEnabled()) { logger.info("Recording method is called for {} to make it {} and record Type: {} resolution:{}", streamId.replaceAll(REPLACE_CHARS, "_"), enableRecording, recordType != null ? recordType.replaceAll(REPLACE_CHARS, "_") : null, resolutionHeight); @@ -948,49 +1215,94 @@ public Result enableRecording(@ApiParam(value = "the id of the stream", required return enableRecordMuxing(streamId, enableRecording, recordType, resolutionHeight); } - @ApiOperation(value = "Get IP Camera Error after connection failure. If returns true, it means there is an error. If returns false, there is no error", notes = "Notes here", response = Result.class) + @Operation(summary = "Get IP Camera Error after connection failure", + description = "Checks for an error after a connection failure with an IP camera. Returning true indicates an error; false indicates no error.", + responses = { + @ApiResponse(responseCode = "200", description = "IP Camera error status", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @GET @Consumes(MediaType.APPLICATION_JSON) @Path("/{streamId}/ip-camera-error") @Produces(MediaType.APPLICATION_JSON) - public Result getCameraErrorV2(@ApiParam(value = "StreamId of the IP Camera Streaming.", required = true) @PathParam("streamId") String streamId) { + public Result getCameraErrorV2(@Parameter(description = "StreamId of the IP Camera Streaming.", required = true) @PathParam("streamId") String streamId) { return super.getCameraErrorById(streamId); } - @ApiOperation(value = "Start streaming sources(IP Cameras, Stream Sources, PlayLists) ", response = Result.class) + @Operation(summary = "Start streaming sources", + description = "Initiates streaming for sources such as IP Cameras, Stream Sources, and PlayLists.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of starting streaming sources", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/start") @Produces(MediaType.APPLICATION_JSON) - public Result startStreamSourceV2(@ApiParam(value = "the id of the stream. The broadcast type should be IP Camera or Stream Source otherwise it does not work", required = true) @PathParam("id") String id) + public Result startStreamSourceV2(@Parameter(description = "the id of the stream. The broadcast type should be IP Camera or Stream Source otherwise it does not work", required = true) @PathParam("id") String id) { return super.startStreamSource(id); } - @ApiOperation(value = "Specify the next playlist item to play according to the index. This method is only for playlists.", response = Result.class) + @Operation(summary = "Specify the next playlist item to play by index", + description = "Sets the next playlist item to be played, based on its index. This method is applicable only to playlists.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of specifying the next playlist item", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/playlists/{id}/next") @Produces(MediaType.APPLICATION_JSON) - public Result playNextItem(@ApiParam(value = "The id of the playlist stream.", required = true) @PathParam("id") String id, - @ApiParam(value = "The next item to play. If it's not specified or it's -1, it plays next item. If it's number, it skips that item in the playlist to play. The first item index is 0. ", required = false) @QueryParam("index") Integer index + public Result playNextItem(@Parameter(description = "The id of the playlist stream.", required = true) @PathParam("id") String id, + @Parameter(description = "The next item to play. If it's not specified or it's -1, it plays next item. If it's number, it skips that item in the playlist to play. The first item index is 0. ", required = false) @QueryParam("index") Integer index ) { return super.playNextItem(id, index); } - @ApiOperation(value = "Stop streaming for the active stream. It both stops ingested(RTMP, WebRTC) or pulled stream sources (IP Cameras and Stream Sources)", response = Result.class) + @Operation(summary = "Stop streaming for the active stream", + description = "Terminates streaming for the active stream, including both ingested (RTMP, WebRTC) and pulled stream sources (IP Cameras and Stream Sources).", + responses = { + @ApiResponse(responseCode = "200", description = "Result of stopping the active stream", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/stop") @Produces(MediaType.APPLICATION_JSON) - public Result stopStreamingV2(@ApiParam(value = "the id of the broadcast.", required = true) @PathParam("id") String id) + public Result stopStreamingV2(@Parameter(description = "the id of the broadcast.", required = true) @PathParam("id") String id) { return super.stopStreaming(id); } - @ApiOperation(value = "Get Discovered ONVIF IP Cameras, this service perform a discovery inside of internal network and get automatically ONVIF enabled camera information", notes = "Notes here", response = Result.class) + @Operation(summary = "Get Discovered ONVIF IP Cameras", + description = "Performs a discovery within the internal network to automatically retrieve information about ONVIF-enabled cameras.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of discovering ONVIF IP cameras", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @GET @Path("/onvif-devices") @Produces(MediaType.APPLICATION_JSON) @@ -998,11 +1310,20 @@ public String[] searchOnvifDevicesV2() { return super.searchOnvifDevices(); } - @ApiOperation(value = "Get The Profile List for an ONVIF IP Cameras", notes = "Notes here", response = Result.class) + @Operation(summary = "Get the Profile List for an ONVIF IP Camera", + description = "Retrieves the profile list for an ONVIF IP camera.", + responses = { + @ApiResponse(responseCode = "200", description = "Profile list for the ONVIF IP camera", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = String[].class) + )) + } + ) @GET @Path("/{id}/ip-camera/device-profiles") @Produces(MediaType.APPLICATION_JSON) - public String[] getOnvifDeviceProfiles(@ApiParam(value = "The id of the IP Camera", required = true) @PathParam("id") String id) { + public String[] getOnvifDeviceProfiles(@Parameter(description = "The id of the IP Camera", required = true) @PathParam("id") String id) { if (id != null && StreamIdValidator.isStreamIdValid(id)) { return super.getOnvifDeviceProfiles(id); } @@ -1010,20 +1331,27 @@ public String[] getOnvifDeviceProfiles(@ApiParam(value = "The id of the IP Camer } - @ApiOperation(value = "Move IP Camera. It support continuous, relative and absolute move. By default it's relative move." - + "Movement parameters should be given according to movement type. " - + "Generally here are the values " - + "For Absolute move, value X and value Y is between -1.0f and 1.0f. Zooom value is between 0.0f and 1.0f" - + "For Relative move, value X, value Y and Zoom Value is between -1.0f and 1.0f" - + "For Continous move,value X, value Y and Zoom Value is between -1.0f and 1.0f ", response = Result.class) + @Operation(summary = "Move IP Camera", + description = "Supports continuous, relative, and absolute movement. By default, it's a relative move. Movement parameters should be provided according to the movement type. Generally, the following values are used: " + + "For Absolute move, value X and value Y are between -1.0f and 1.0f. Zoom value is between 0.0f and 1.0f. " + + "For Relative move, value X, value Y, and Zoom Value are between -1.0f and 1.0f. " + + "For Continuous move, value X, value Y, and Zoom Value are between -1.0f and 1.0f.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of moving the IP camera", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Path("/{id}/ip-camera/move") @Produces(MediaType.APPLICATION_JSON) - public Result moveIPCamera(@ApiParam(value = "The id of the IP Camera", required = true) @PathParam("id") String id, - @ApiParam(value = "Movement in X direction. If not specified, it's assumed to be zero. Valid ranges between -1.0f and 1.0f for all movements ", required = false) @QueryParam("valueX") Float valueX, - @ApiParam(value = "Movement in Y direction. If not specified, it's assumed to be zero. Valid ranges between -1.0f and 1.0f for all movements ", required = false) @QueryParam("valueY") Float valueY, - @ApiParam(value = "Movement in Zoom. If not specified, it's assumed to be zero. Valid ranges for relative and continous move is between -1.0f and 1.0f. For absolute move between 0.0f and 1.0f ", required = false) @QueryParam("valueZ") Float valueZ, - @ApiParam(value = "Movement type. It can be absolute, relative or continuous. If not specified, it's relative", required = false) @QueryParam("movement") String movement + public Result moveIPCamera(@Parameter(description = "The id of the IP Camera", required = true) @PathParam("id") String id, + @Parameter(description = "Movement in X direction. If not specified, it's assumed to be zero. Valid ranges between -1.0f and 1.0f for all movements ", required = false) @QueryParam("valueX") Float valueX, + @Parameter(description = "Movement in Y direction. If not specified, it's assumed to be zero. Valid ranges between -1.0f and 1.0f for all movements ", required = false) @QueryParam("valueY") Float valueY, + @Parameter(description = "Movement in Zoom. If not specified, it's assumed to be zero. Valid ranges for relative and continous move is between -1.0f and 1.0f. For absolute move between 0.0f and 1.0f ", required = false) @QueryParam("valueZ") Float valueZ, + @Parameter(description = "Movement type. It can be absolute, relative or continuous. If not specified, it's relative", required = false) @QueryParam("movement") String movement ) { boolean result = false; String message = STREAM_ID_NOT_VALID; @@ -1061,11 +1389,11 @@ else if (movement.equals(ABSOLUTE_MOVE)) { return new Result(result, message); } - @ApiOperation(value="Stop move for IP Camera.", response = Result.class) + @Operation(description = "Stop move for IP Camera") @POST @Path("/{id}/ip-camera/stop-move") @Produces(MediaType.APPLICATION_JSON) - public Result stopMove(@ApiParam(value = "the id of the IP Camera", required = true) @PathParam("id") String id) { + public Result stopMove(@Parameter(description = "the id of the IP Camera", required = true) @PathParam("id") String id) { boolean result = false; String message = STREAM_ID_NOT_VALID; if (id != null && StreamIdValidator.isStreamIdValid(id)) @@ -1083,14 +1411,25 @@ public Result stopMove(@ApiParam(value = "the id of the IP Camera", required = t } - @ApiOperation(value = "Creates a conference room with the parameters. The room name is key so if this is called with the same room name then new room is overwritten to old one", response = ConferenceRoom.class) - @ApiResponses(value = { @ApiResponse(code = 400, message = "If operation is no completed for any reason", response=Result.class), - @ApiResponse(code = 200, message = "Returns the created conference room", response = ConferenceRoom.class)}) + @Operation(description = "Creates a conference room with the parameters. The room name is key so if this is called with the same room name then new room is overwritten to old one") + @ApiResponses(value = { + @ApiResponse(responseCode = "400", description = "If the operation is not completed for any reason", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )), + @ApiResponse(responseCode = "200", description = "Returns the created conference room", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ConferenceRoom.class) + )) + }) + @POST @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms") @Produces(MediaType.APPLICATION_JSON) - public Response createConferenceRoomV2(@ApiParam(value = "Conference Room object with start and end date", required = true) ConferenceRoom room) { + public Response createConferenceRoomV2(@Parameter(description = "Conference Room object with start and end date", required = true) ConferenceRoom room) { ConferenceRoom confRoom = super.createConferenceRoom(room); if (confRoom != null) { @@ -1100,14 +1439,24 @@ public Response createConferenceRoomV2(@ApiParam(value = "Conference Room object } - @ApiOperation(value = "Edits previously saved conference room", response = Response.class) - @ApiResponses(value = { @ApiResponse(code = 400, message = "If operation is no completed for any reason", response=Result.class), - @ApiResponse(code = 200, message = "Returns the updated Conference room", response = ConferenceRoom.class)}) + @Operation(description = "Edits previously saved conference room") + @ApiResponses(value = { + @ApiResponse(responseCode = "400", description = "If the operation is not completed for any reason", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )), + @ApiResponse(responseCode = "200", description = "Returns the updated Conference room", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ConferenceRoom.class) + )) + }) @PUT @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}") @Produces(MediaType.APPLICATION_JSON) - public Response editConferenceRoom(@ApiParam(value="Room id") @PathParam("room_id") String roomId, @ApiParam(value = "Conference Room object with start and end date", required = true) ConferenceRoom room) { + public Response editConferenceRoom(@Parameter(description="Room id") @PathParam("room_id") String roomId, @Parameter(description = "Conference Room object with start and end date", required = true) ConferenceRoom room) { if(room != null && getDataStore().editConferenceRoom(roomId, room)) { return Response.status(Status.OK).entity(room).build(); @@ -1115,38 +1464,74 @@ public Response editConferenceRoom(@ApiParam(value="Room id") @PathParam("room_i return Response.status(Status.BAD_REQUEST).entity(new Result(false, "Operation not completed")).build(); } - @ApiOperation(value = "Deletes a conference room. The room id is key so if this is called with the same room id then new room is overwritten to old one", response = Result.class) + @Operation(summary = "Delete a conference room", + description = "Deletes a conference room. The room ID is the key, so if this is called with the same room ID, then the new room overwrites the old one.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of deleting the conference room", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @DELETE @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}") @Produces(MediaType.APPLICATION_JSON) - public Result deleteConferenceRoomV2(@ApiParam(value = "the id of the conference room", required = true) @PathParam("room_id") String roomId) { + public Result deleteConferenceRoomV2(@Parameter(description = "the id of the conference room", required = true) @PathParam("room_id") String roomId) { return new Result(super.deleteConferenceRoom(roomId, getDataStore())); } - @ApiOperation(value = "Add a subtrack to a main track (broadcast).", notes = "", response = Result.class) + @Operation(summary = "Add a subtrack to a main track (broadcast)", + description = "Adds a subtrack to a main track (broadcast).", + responses = { + @ApiResponse(responseCode = "200", description = "Result of adding a subtrack", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/subtrack") @Produces(MediaType.APPLICATION_JSON) - public Result addSubTrack(@ApiParam(value = "Broadcast id(main track)", required = true) @PathParam("id") String id, - @ApiParam(value = "Subtrack Stream Id", required = true) @QueryParam("id") String subTrackId) + public Result addSubTrack(@Parameter(description = "Broadcast id(main track)", required = true) @PathParam("id") String id, + @Parameter(description = "Subtrack Stream Id", required = true) @QueryParam("id") String subTrackId) { return RestServiceBase.addSubTrack(id, subTrackId, getDataStore()); } - @ApiOperation(value = "Delete a subtrack from a main track (broadcast).", notes = "", response = Result.class) + @Operation(summary = "Delete a subtrack from a main track (broadcast)", + description = "Deletes a subtrack from a main track (broadcast).", + responses = { + @ApiResponse(responseCode = "200", description = "Result of deleting a subtrack", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @DELETE @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/subtrack") @Produces(MediaType.APPLICATION_JSON) - public Result removeSubTrack(@ApiParam(value = "Broadcast id(main track)", required = true) @PathParam("id") String id, - @ApiParam(value = "Subtrack Stream Id", required = true) @QueryParam("id") String subTrackId) + public Result removeSubTrack(@Parameter(description = "Broadcast id(main track)", required = true) @PathParam("id") String id, + @Parameter(description = "Subtrack Stream Id", required = true) @QueryParam("id") String subTrackId) { return RestServiceBase.removeSubTrack(id, subTrackId, getDataStore()); } - @ApiOperation(value = "Returns the stream info(width, height, bitrates and video codec) of the stream", response= BasicStreamInfo[].class) + @Operation(summary = "Get stream information", + description = "Returns the stream information including width, height, bitrates, and video codec.", + responses = { + @ApiResponse(responseCode = "200", description = "Stream information", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = BasicStreamInfo[].class) + )) + } + ) @GET @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/stream-info") @@ -1176,37 +1561,53 @@ public BasicStreamInfo[] getStreamInfo(@PathParam("id") String streamId) return basicStreamInfo; } - @ApiOperation(value = "Send stream participants a message through Data Channel in a WebRTC stream", notes = "", response = Result.class) + @Operation(summary = "Send message to stream participants via Data Channel", + description = "Sends a message to stream participants through the Data Channel in a WebRTC stream.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of sending the message", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{id}/data") @Produces(MediaType.APPLICATION_JSON) - public Result sendMessage(@ApiParam(value = "Message through Data Channel which will be sent to all WebRTC stream participants", required = true) String message, - @ApiParam(value = "Broadcast id", required = true) @PathParam("id") String id) { + public Result sendMessage(@Parameter(description = "Message through Data Channel which will be sent to all WebRTC stream participants", required = true) String message, + @Parameter(description = "Broadcast id", required = true) @PathParam("id") String id) { return RestServiceBase.sendDataChannelMessage(id, message, getApplication(), getDataStore()); } - @ApiOperation(value = "Gets the conference room list from database", notes = "",responseContainer = "List", response = ConferenceRoom.class) + @Operation(description = "Gets the conference room list from database") @GET @Path("/conference-rooms/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List getConferenceRoomList(@ApiParam(value = "This is the offset of the list, it is useful for pagination. If you want to use sort mechanism, we recommend using Mongo DB.", required = true) @PathParam("offset") int offset, - @ApiParam(value = "Number of items that will be fetched. If there is not enough item in the datastore, returned list size may less then this value", required = true) @PathParam("size") int size, - @ApiParam(value = "field to sort", required = false) @QueryParam("sort_by") String sortBy, - @ApiParam(value = "asc for Ascending, desc Descending order", required = false) @QueryParam("order_by") String orderBy, - @ApiParam(value = "Search parameter, returns specific items that contains search string", required = false) @QueryParam("search") String search + public List getConferenceRoomList(@Parameter(description = "This is the offset of the list, it is useful for pagination. If you want to use sort mechanism, we recommend using Mongo DB.", required = true) @PathParam("offset") int offset, + @Parameter(description = "Number of items that will be fetched. If there is not enough item in the datastore, returned list size may less then this value", required = true) @PathParam("size") int size, + @Parameter(description = "field to sort", required = false) @QueryParam("sort_by") String sortBy, + @Parameter(description = "asc for Ascending, desc Descending order", required = false) @QueryParam("order_by") String orderBy, + @Parameter(description = "Search parameter, returns specific items that contains search string", required = false) @QueryParam("search") String search ) { return getDataStore().getConferenceRoomList(offset, size ,sortBy, orderBy, search); } - @ApiOperation(value = "Get conference room object") - @ApiResponses(value = { @ApiResponse(code = 200, message = "Return the ConferenceRoom object"), - @ApiResponse(code = 404, message = "ConferenceRoom object not found")}) + @Operation(description = "Get conference room object") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Return the ConferenceRoom object", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ConferenceRoom.class) + )), + @ApiResponse(responseCode = "404", description = "ConferenceRoom object not found") + }) + @GET @Path("/conference-rooms/{roomId}") @Produces(MediaType.APPLICATION_JSON) - public Response getConferenceRoom(@ApiParam(value = "id of the room", required = true) @PathParam("roomId") String id) { + public Response getConferenceRoom(@Parameter(description = "id of the room", required = true) @PathParam("roomId") String id) { ConferenceRoom room = null; if (id != null) { room = lookupConference(id); @@ -1219,36 +1620,63 @@ public Response getConferenceRoom(@ApiParam(value = "id of the room", required = } } - @ApiOperation(value="Returns the streams Ids in the room.",responseContainer ="List",response = String.class) + @Operation(summary = "Get stream IDs in the room", + description = "Returns the stream IDs in the room.", + responses = { + @ApiResponse(responseCode = "200", description = "List of stream IDs", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class, type = "array") + )) + } + ) @GET @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/room-info") @Produces(MediaType.APPLICATION_JSON) - public RootRestService.RoomInfo getRoomInfo(@ApiParam(value="Room id", required=true) @PathParam("room_id") String roomId, - @ApiParam(value="If Stream Id is entered, that stream id will be isolated from the result",required = false) @QueryParam("streamId") String streamId){ + public RootRestService.RoomInfo getRoomInfo(@Parameter(description = "Room id", required=true) @PathParam("room_id") String roomId, + @Parameter(description="If Stream Id is entered, that stream id will be isolated from the result",required = false) @QueryParam("streamId") String streamId){ ConferenceRoom room = getDataStore().getConferenceRoom(roomId); return new RootRestService.RoomInfo(roomId,RestServiceBase.getRoomInfoFromConference(roomId,streamId,getDataStore()), room); } - @ApiOperation(value="Adds the specified stream with streamId to the room. Use PUT conference-rooms/{room_id}/{streamId}",response = Result.class) + @Operation(summary = "Add stream to the room", + description = "Adds the specified stream with stream ID to the room. Use PUT conference-rooms/{room_id}/{streamId}.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of adding the stream", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @PUT @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/add") @Produces(MediaType.APPLICATION_JSON) @Deprecated(since="2.6.2", forRemoval=true) - public Result addStreamToTheRoomDeprecated(@ApiParam(value="Room id", required=true) @PathParam("room_id") String roomId, - @ApiParam(value="Stream id to add to the conference room",required = true) @QueryParam("streamId") String streamId){ + public Result addStreamToTheRoomDeprecated(@Parameter(description="Room id", required=true) @PathParam("room_id") String roomId, + @Parameter(description="Stream id to add to the conference room",required = true) @QueryParam("streamId") String streamId){ return addStreamToTheRoom(roomId, streamId); } - @ApiOperation(value="Adds the specified stream with streamId to the room. ",response = Result.class) + @Operation(summary = "Add stream to the room", + description = "Adds the specified stream with stream ID to the room.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of adding the stream", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Result.class) + )) + } + ) @PUT @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/{streamId}") @Produces(MediaType.APPLICATION_JSON) - public Result addStreamToTheRoom(@ApiParam(value="Room id", required=true) @PathParam("room_id") String roomId, - @ApiParam(value="Stream id to add to the conference room",required = true) @PathParam("streamId") String streamId){ + public Result addStreamToTheRoom(@Parameter(description="Room id", required=true) @PathParam("room_id") String roomId, + @Parameter(description="Stream id to add to the conference room",required = true) @PathParam("streamId") String streamId){ boolean result = BroadcastRestService.addStreamToConferenceRoom(roomId,streamId,getDataStore()); if(result) { @@ -1257,25 +1685,30 @@ public Result addStreamToTheRoom(@ApiParam(value="Room id", required=true) @Path return new Result(result); } - @ApiOperation(value="Deletes the specified stream correlated with streamId in the room. Use DELETE /conference-rooms/{room_id}/{streamId}",response = Result.class) + @Operation(summary = "Delete stream from the room", + description = "Deletes the specified stream correlated with stream ID in the room. Use DELETE /conference-rooms/{room_id}/{streamId}.", + responses = { + @ApiResponse(responseCode = "200", description = "Result of deleting the stream") + } + ) @PUT @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/delete") @Produces(MediaType.APPLICATION_JSON) @Deprecated(since="2.6.2", forRemoval=true) - public Result deleteStreamFromTheRoomDeprecated(@ApiParam(value="Room id", required=true) @PathParam("room_id") String roomId, - @ApiParam(value="Stream id to delete from the conference room",required = true) @QueryParam("streamId") String streamId){ + public Result deleteStreamFromTheRoomDeprecated(@Parameter(description ="Room id", required=true) @PathParam("room_id") String roomId, + @Parameter(description="Stream id to delete from the conference room",required = true) @QueryParam("streamId") String streamId){ return deleteStreamFromTheRoom(roomId, streamId); } - @ApiOperation(value="Deletes the specified stream correlated with streamId in the room. Use ",response = Result.class) + @Operation(description ="Deletes the specified stream correlated with streamId in the room.") @DELETE @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/{streamId}") @Produces(MediaType.APPLICATION_JSON) - public Result deleteStreamFromTheRoom(@ApiParam(value="Room id", required=true) @PathParam("room_id") String roomId, - @ApiParam(value="Stream id to delete from the conference room",required = true) @PathParam("streamId") String streamId){ + public Result deleteStreamFromTheRoom(@Parameter(description="Room id", required=true) @PathParam("room_id") String roomId, + @Parameter(description="Stream id to delete from the conference room",required = true) @PathParam("streamId") String streamId){ boolean result = RestServiceBase.removeStreamFromRoom(roomId,streamId,getDataStore()); if(result) { getApplication().leftTheRoom(roomId, streamId); @@ -1296,11 +1729,11 @@ public Result deleteStreamFromTheRoom(@ApiParam(value="Room id", required=true) @GET @Path("/webrtc-viewers/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) - public List getWebRTCViewerList(@ApiParam(value = "This is the offset of the list, it is useful for pagination. If you want to use sort mechanism, we recommend using Mongo DB.", required = true) @PathParam("offset") int offset, - @ApiParam(value = "Number of items that will be fetched. If there is not enough item in the datastore, returned list size may less then this value", required = true) @PathParam("size") int size, - @ApiParam(value = "field to sort", required = false) @QueryParam("sort_by") String sortBy, - @ApiParam(value = "asc for Ascending, desc Descending order", required = false) @QueryParam("order_by") String orderBy, - @ApiParam(value = "Search parameter, returns specific items that contains search string", required = false) @QueryParam("search") String search + public List getWebRTCViewerList(@Parameter(description = "This is the offset of the list, it is useful for pagination. If you want to use sort mechanism, we recommend using Mongo DB.", required = true) @PathParam("offset") int offset, + @Parameter(description = "Number of items that will be fetched. If there is not enough item in the datastore, returned list size may less then this value", required = true) @PathParam("size") int size, + @Parameter(description = "field to sort", required = false) @QueryParam("sort_by") String sortBy, + @Parameter(description = "asc for Ascending, desc Descending order", required = false) @QueryParam("order_by") String orderBy, + @Parameter(description = "Search parameter, returns specific items that contains search string", required = false) @QueryParam("search") String search ) { return getDataStore().getWebRTCViewerList(offset, size ,sortBy, orderBy, search); } @@ -1311,24 +1744,24 @@ public List getWebRTCViewerList(@ApiParam(value = "This is the * @return */ @Deprecated(since = "2.7.0", forRemoval = true) - @ApiOperation(value = "Stop player with a specified id", response = Result.class) + @Operation(description = "Stop player with a specified id") @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/webrtc-viewers/{webrtc-viewer-id}/stop") @Produces(MediaType.APPLICATION_JSON) - public Result stopPlaying(@ApiParam(value = "the id of the webrtc viewer.", required = true) @PathParam("webrtc-viewer-id") String viewerId) + public Result stopPlaying(@Parameter(description = "the id of the webrtc viewer.", required = true) @PathParam("webrtc-viewer-id") String viewerId) { boolean result = getApplication().stopPlaying(viewerId); return new Result(result); } - @ApiOperation(value = "Add ID3 data to HLS stream at the moment", response = Result.class) + @Operation(description = "Add ID3 data to HLS stream at the moment") @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/{stream_id}/id3") @Produces(MediaType.APPLICATION_JSON) - public Result addID3Data(@ApiParam(value = "the id of the stream", required = true) @PathParam("stream_id") String streamId, - @ApiParam(value = "ID3 data.", required = false) String data) { + public Result addID3Data(@Parameter(description = "the id of the stream", required = true) @PathParam("stream_id") String streamId, + @Parameter(description = "ID3 data.", required = false) String data) { if(!getAppSettings().isId3TagEnabled()) { return new Result(false, null, "ID3 tag is not enabled"); } diff --git a/src/main/java/io/antmedia/rest/RestServiceBase.java b/src/main/java/io/antmedia/rest/RestServiceBase.java index 9a461d125..57340a115 100755 --- a/src/main/java/io/antmedia/rest/RestServiceBase.java +++ b/src/main/java/io/antmedia/rest/RestServiceBase.java @@ -70,52 +70,48 @@ import io.antmedia.streamsource.StreamFetcher; import io.antmedia.streamsource.StreamFetcher.IStreamFetcherListener; import io.antmedia.webrtc.api.IWebRTCAdaptor; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.ws.rs.core.Context; public abstract class RestServiceBase { - @ApiModel(value="BroadcastStatistics", description="The statistics class of the broadcasts") - public static class BroadcastStatistics { + public class BroadcastStatistics { - @ApiModelProperty(value = "the total RTMP viewers of the stream") - public final int totalRTMPWatchersCount; + @Schema(description = "The total RTMP viewers of the stream") + public final int totalRTMPWatchersCount; - @ApiModelProperty(value = "the total HLS viewers of the stream") - public final int totalHLSWatchersCount; + @Schema(description = "The total HLS viewers of the stream") + public final int totalHLSWatchersCount; - @ApiModelProperty(value = "the total WebRTC viewers of the stream") - public final int totalWebRTCWatchersCount; + @Schema(description = "The total WebRTC viewers of the stream") + public final int totalWebRTCWatchersCount; - @ApiModelProperty(value = "the total DASH viewers of the stream") - public final int totalDASHWatchersCount; + @Schema(description = "The total DASH viewers of the stream") + public final int totalDASHWatchersCount; - - public BroadcastStatistics(int totalRTMPWatchersCount, int totalHLSWatchersCount, - int totalWebRTCWatchersCount, int totalDASHWatchersCount) { - this.totalRTMPWatchersCount = totalRTMPWatchersCount; - this.totalHLSWatchersCount = totalHLSWatchersCount; - this.totalWebRTCWatchersCount = totalWebRTCWatchersCount; - this.totalDASHWatchersCount = totalDASHWatchersCount; - } + public BroadcastStatistics(int totalRTMPWatchersCount, int totalHLSWatchersCount, + int totalWebRTCWatchersCount, int totalDASHWatchersCount) { + this.totalRTMPWatchersCount = totalRTMPWatchersCount; + this.totalHLSWatchersCount = totalHLSWatchersCount; + this.totalWebRTCWatchersCount = totalWebRTCWatchersCount; + this.totalDASHWatchersCount = totalDASHWatchersCount; + } } - @ApiModel(value="AppBroadcastStatistics", description="The statistics class of the app. It provides total number of viewers and active live streams") - public static class AppBroadcastStatistics extends BroadcastStatistics { - @ApiModelProperty(value = "the total active live stream count") - public final int activeLiveStreamCount; + public class AppBroadcastStatistics extends BroadcastStatistics { - public AppBroadcastStatistics(int totalRTMPWatchersCount, int totalHLSWatchersCount, - int totalWebRTCWatchersCount, int totalDASHWatchersCount, int activeLiveStreamCount ) - { - super(totalRTMPWatchersCount, totalHLSWatchersCount, totalWebRTCWatchersCount, totalDASHWatchersCount); - this.activeLiveStreamCount = activeLiveStreamCount; - } + @Schema(description = "The total active live stream count") + public final int activeLiveStreamCount; + public AppBroadcastStatistics(int totalRTMPWatchersCount, int totalHLSWatchersCount, + int totalWebRTCWatchersCount, int totalDASHWatchersCount, int activeLiveStreamCount) { + super(totalRTMPWatchersCount, totalHLSWatchersCount, totalWebRTCWatchersCount, totalDASHWatchersCount); + this.activeLiveStreamCount = activeLiveStreamCount; + } } + public interface ProcessBuilderFactory { Process make(String...args); } diff --git a/src/main/java/io/antmedia/rest/RootRestService.java b/src/main/java/io/antmedia/rest/RootRestService.java index e3ef5e079..e11f6b3c5 100644 --- a/src/main/java/io/antmedia/rest/RootRestService.java +++ b/src/main/java/io/antmedia/rest/RootRestService.java @@ -8,31 +8,35 @@ import io.antmedia.datastore.db.types.ConferenceRoom; import io.antmedia.rest.model.Version; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.Contact; -import io.swagger.annotations.ExternalDocs; -import io.swagger.annotations.Info; -import io.swagger.annotations.License; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.License; +import io.swagger.v3.oas.annotations.servers.Server; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; + + + +@OpenAPIDefinition( + info = @Info( + description = "Ant Media Server REST API Reference", + version = "V2.0", + title = "Ant Media Server REST API Reference", + contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), + license = @License(name = "Apache 2.0", url = "http://www.apache.org")), + servers = {@Server( description = "test server", + url = "https://test.antmedia.io:5443/Sandbox/rest/")}, + externalDocs = @ExternalDocumentation(url = "https://antmedia.io") + ) -@Api(value = "Rest Service") -@SwaggerDefinition( - info = @Info( - description = "Ant Media Server REST API Reference", - version = "V2.0", - title = "Ant Media Server REST API Reference", - contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), - license = @License(name = "Apache 2.0", url = "http://www.apache.org")), - consumes = {"application/json"}, - produces = {"application/json"}, - schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, - externalDocs = @ExternalDocs(value = "External Docs", url = "https://antmedia.io") -) @Component @Path("/v2") public class RootRestService extends RestServiceBase { @@ -40,7 +44,16 @@ public class RootRestService extends RestServiceBase { protected static Logger logger = LoggerFactory.getLogger(RootRestService.class); - @ApiOperation(value = "Returns the Ant Media Server Version", notes = "", response = Version.class) + @Operation(summary = "Returns the Ant Media Server Version", + description = "Retrieves the version information of the Ant Media Server.", + responses = { + @ApiResponse(responseCode = "200", description = "Ant Media Server Version", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = Version.class) + )) + } + ) @GET @Path("/version") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/io/antmedia/rest/VoDRestService.java b/src/main/java/io/antmedia/rest/VoDRestService.java index e91ad6090..b84201368 100644 --- a/src/main/java/io/antmedia/rest/VoDRestService.java +++ b/src/main/java/io/antmedia/rest/VoDRestService.java @@ -9,14 +9,14 @@ import io.antmedia.datastore.db.types.VoD; import io.antmedia.rest.BroadcastRestService.SimpleStat; import io.antmedia.rest.model.Result; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.Contact; -import io.swagger.annotations.ExternalDocs; -import io.swagger.annotations.Info; -import io.swagger.annotations.License; -import io.swagger.annotations.SwaggerDefinition; +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.License; +import io.swagger.v3.oas.annotations.servers.Server; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -26,135 +26,166 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.media.Schema; + + + +@OpenAPIDefinition( + info = @Info( + description = "Ant Media Server REST API Reference", + version = "V2.0", + title = "Ant Media Server REST API Reference", + contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), + license = @License(name = "Apache 2.0", url = "http://www.apache.org")), + servers = { + @Server( + description = "test server", + url = "https://test.antmedia.io:5443/Sandbox/rest/" + + )}, + externalDocs = @ExternalDocumentation(url = "https://antmedia.io") + ) -@Api(value = "VoD Rest Service") -@SwaggerDefinition( - info = @Info( - description = "Ant Media Server REST API Reference", - version = "V2.0", - title = "Ant Media Server REST API Reference", - contact = @Contact(name = "Ant Media Info", email = "contact@antmedia.io", url = "https://antmedia.io"), - license = @License(name = "Apache 2.0", url = "http://www.apache.org")), - consumes = {"application/json"}, - produces = {"application/json"}, - schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, - externalDocs = @ExternalDocs(value = "External Docs", url = "https://antmedia.io") -) @Component @Path("/v2/vods") public class VoDRestService extends RestServiceBase{ - - @ApiOperation(value = "VoD file from database", response = VoD.class) - @GET + + @Operation(summary = "VoD file from database", description = "Retrieves a VoD file from the database by its ID.", responses = { + @ApiResponse(responseCode = "200", description = "VoD file retrieved successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = VoD.class))), + @ApiResponse(responseCode = "404", description = "VoD file not found") + }) @GET @Path("/{id}") @Produces(MediaType.APPLICATION_JSON) @Override - public VoD getVoD(@ApiParam(value = "id of the VoD", required = true) @PathParam("id") String id) { + public VoD getVoD(@Parameter(description = "ID of the VoD file", required = true) @PathParam("id") String id) { return super.getVoD(id); } - @ApiOperation(value = "Import VoDs to Stalker Portal", response = Result.class) + @Operation(summary = "Import VoDs to Stalker Portal", description = "Imports VoDs to the Stalker Portal.", responses = { + @ApiResponse(responseCode = "200", description = "VoDs imported successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) @POST @Path("/import-to-stalker") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result importVoDsToStalker() - { + public Result importVoDsToStalker() { return super.importVoDsToStalker(); } - - - @ApiOperation(value = " Get the VoD list from database. It returns 50 items at max. You can use offset value to get result page by page ", responseContainer = "List",response = VoD.class) + + + @Operation(summary = "Get the VoD list from database", description = "Retrieves the list of VoD files from the database. It returns up to 50 items. You can use offset value to get result page by page.", responses = { + @ApiResponse(responseCode = "200", description = "VoD list retrieved successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = VoD.class, type = "array"))) + }) @GET @Path("/list/{offset}/{size}") - @Produces(MediaType.APPLICATION_JSON) - public List getVodList(@ApiParam(value = "Offset of the list", required = true) @PathParam("offset") int offset, - @ApiParam(value = "Number of items that will be fetched", required = true) @PathParam("size") int size, - @ApiParam(value = "Field to sort. Possible values are \"name\", \"date\"", required = false) @QueryParam("sort_by") String sortBy, - @ApiParam(value ="\"asc\" for Ascending, \"desc\" Descening order", required = false) @QueryParam("order_by") String orderBy, - @ApiParam(value = "Id of the stream to filter the results by stream id", required = false) @QueryParam("streamId") String streamId, - @ApiParam(value = "Search string", required = false) @QueryParam("search") String search) - { + public List getVodList( + @Parameter(description = "Offset of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "Number of items that will be fetched", required = true) @PathParam("size") int size, + @Parameter(description = "Field to sort. Possible values are 'name', 'date'") @QueryParam("sort_by") String sortBy, + @Parameter(description = "'asc' for Ascending, 'desc' for Descending order") @QueryParam("order_by") String orderBy, + @Parameter(description = "ID of the stream to filter the results by stream ID") @QueryParam("streamId") String streamId, + @Parameter(description = "Search string") @QueryParam("search") String search) { return getDataStore().getVodList(offset, size, sortBy, orderBy, streamId, search); } - - @ApiOperation(value = "Get the total number of VoDs", response = Long.class) + + @Operation(summary = "Get the total number of VoDs", description = "Retrieves the total number of VoD files in the database.", responses = { + @ApiResponse(responseCode = "200", description = "Total number of VoDs retrieved successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = SimpleStat.class))) + }) @GET @Path("/count") - @Produces(MediaType.APPLICATION_JSON) public SimpleStat getTotalVodNumber() { return new SimpleStat(getDataStore().getTotalVodNumber()); } - @ApiOperation(value = "Get the partial number of VoDs depending on the searched items", response = Long.class) + @Operation(summary = "Get the partial number of VoDs depending on the searched items", description = "Retrieves the number of VoD files that include the specified search parameter.", responses = { + @ApiResponse(responseCode = "200", description = "Partial number of VoDs retrieved successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = SimpleStat.class))) + }) @GET @Path("/count/{search}") - @Produces(MediaType.APPLICATION_JSON) public SimpleStat getTotalVodNumber( - @ApiParam(value = "Search parameter to get the number of items including it ", required = true) @PathParam("search") String search) - { + @Parameter(description = "Search parameter to get the number of items including it", required = true) @PathParam("search") String search) { return new SimpleStat(getDataStore().getPartialVodNumber(search)); } - - @ApiOperation(value = "Delete specific VoD File", response = Result.class) + + @Operation(summary = "Delete specific VoD File", description = "Deletes a specific VoD file from the database by its ID.", responses = { + @ApiResponse(responseCode = "200", description = "VoD file deleted successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))), + @ApiResponse(responseCode = "404", description = "VoD file not found") + }) @DELETE - @Consumes({ MediaType.APPLICATION_JSON }) @Path("/{id}") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result deleteVoD(@ApiParam(value = "the id of the VoD file", required = true) @PathParam("id") String id) { + public Result deleteVoD( + @Parameter(description = "ID of the VoD file", required = true) + @PathParam("id") String id) { return super.deleteVoD(id); } - @ApiOperation(value = "Delete bulk VoD Files based on Vod Id", response = Result.class) - @DELETE - @Consumes({ MediaType.APPLICATION_JSON }) - @Path("/bulk") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result deleteVoDs(@ApiParam(value = "the ids of the VoD file", required = true) String[] vodIds) { - return super.deleteVoDs(vodIds); - } - - @ApiOperation(value = "Upload external VoD file to Ant Media Server", notes = "", response = Result.class) - @POST - @Consumes({MediaType.MULTIPART_FORM_DATA}) - @Path("/create") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result uploadVoDFile(@ApiParam(value = "the name of the VoD File", required = true) @QueryParam("name") String fileName, - @ApiParam(value = "file", required = true) @FormDataParam("file") InputStream inputStream) { - return super.uploadVoDFile(fileName, inputStream); - } - - @ApiOperation(value = "Import VoD files from a directory and make it streamable.", response = Result.class) - @POST - @Path("/directory") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result importVoDs(@ApiParam(value="the full path of the directory that VoD files will be imported to datastore and linked to the streams") @QueryParam("directory") String directory) { - return super.importVoDs(directory); - } - - - @ApiOperation(value = "Unlinks VoD path from streams directory and delete the database record. It does not delete the files. It only unlinks folder and delete database records", response = Result.class) - @DELETE - @Path("/directory") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result unlinksVoD(@ApiParam(value="the full path of the directory that imported VoD files will be deleted from database. ") @QueryParam("directory") String directory) { - return super.unlinksVoD(directory); - } - - - - @Deprecated - @ApiOperation(value = "Deprecated. Use import VoDs. Synchronize VoD Folder and add them to VoD database if any file exist and create symbolic links to that folder", notes = "Notes here", response = Result.class) - @POST - @Path("/synch-user-vod-list") - @Produces(MediaType.APPLICATION_JSON) - @Override - public Result synchUserVodList() { - return super.synchUserVodList(); - } + @Operation(summary = "Delete bulk VoD Files based on Vod Id", description = "Deletes multiple VoD files from the database by their IDs.", responses = { + @ApiResponse(responseCode = "200", description = "VoD files deleted successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) + @DELETE + @Path("/bulk") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces(MediaType.APPLICATION_JSON) + @Override + public Result deleteVoDs( + @Parameter(description = "IDs of the VoD files", required = true) String[] vodIds) { + return super.deleteVoDs(vodIds); + } + + + @Operation(summary = "Upload external VoD file to Ant Media Server", description = "Uploads an external VoD file to Ant Media Server.", responses = { + @ApiResponse(responseCode = "200", description = "VoD file uploaded successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) + @POST + @Path("/create") + @Consumes({MediaType.MULTIPART_FORM_DATA}) + @Produces(MediaType.APPLICATION_JSON) + @Override + public Result uploadVoDFile( + @Parameter(description = "Name of the VoD File", required = true) @QueryParam("name") String fileName, + @Parameter(description = "VoD file", required = true) @FormDataParam("file") InputStream inputStream) { + return super.uploadVoDFile(fileName, inputStream); + } + + + @Operation(summary = "Import VoD files from a directory and make them streamable.", description = "Imports VoD files from a directory to the datastore and links them to the streams.", responses = { + @ApiResponse(responseCode = "200", description = "VoD files imported successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) + @POST + @Path("/directory") + @Produces(MediaType.APPLICATION_JSON) + @Override + public Result importVoDs( + @Parameter(description = "The full path of the directory that VoD files will be imported to the datastore and linked to the streams", required = true) @QueryParam("directory") String directory) { + return super.importVoDs(directory); + } + + + + @Operation(summary = "Unlinks VoD path from streams directory and delete the database record.", description = "Deletes the database record associated with the specified directory, without deleting the files themselves.", responses = { + @ApiResponse(responseCode = "200", description = "VoD records unlinked successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) + @DELETE + @Path("/directory") + @Produces(MediaType.APPLICATION_JSON) + @Override + public Result unlinksVoD( + @Parameter(description = "The full path of the directory from which imported VoD files will be deleted from the database", required = true) @QueryParam("directory") String directory) { + return super.unlinksVoD(directory); + } + + + + + @Operation(summary = "Deprecated. Use import VoDs.", description = "Synchronizes VoD Folder and adds them to VoD database if any file exist and creates symbolic links to that folder.", deprecated = true, responses = { + @ApiResponse(responseCode = "200", description = "VoD files synchronized successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) + @POST + @Path("/synch-user-vod-list") + @Produces(MediaType.APPLICATION_JSON) + @Override + public Result synchUserVodList() { + return super.synchUserVodList(); + } + } diff --git a/src/main/java/io/antmedia/rest/WebRTCClientStats.java b/src/main/java/io/antmedia/rest/WebRTCClientStats.java index 1963a5d85..2291d8dcb 100644 --- a/src/main/java/io/antmedia/rest/WebRTCClientStats.java +++ b/src/main/java/io/antmedia/rest/WebRTCClientStats.java @@ -2,44 +2,43 @@ import io.antmedia.statistic.type.WebRTCAudioSendStats; import io.antmedia.statistic.type.WebRTCVideoSendStats; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="WebRTCClientStats", description="The WebRTC Client Statistics class") +@Schema(description = "WebRTC Client statistics.") public class WebRTCClientStats { - @ApiModelProperty(value = "the measured bitrate of the WebRTC Client") - private int measuredBitrate; - - @ApiModelProperty(value = "the sent bitrate of the WebRTC Client") - private int sendBitrate; - - @ApiModelProperty(value = "the video frame sent period of the WebRTC Client") - private double videoFrameSendPeriod; - - @ApiModelProperty(value = "the audio frame send period of the WebRTC Client") - private double audioFrameSendPeriod; - - @ApiModelProperty(value = "WebRTC Client Id which is basically hash of the object") - private int clientId; - - @ApiModelProperty(value = "Number of video packets sent") - private long videoPacketCount; - - @ApiModelProperty(value = "Number of audio packets sent") - private long audioPacketCount; - - @ApiModelProperty(value = "Video sent low level stats") - private WebRTCVideoSendStats videoSentStats; - - @ApiModelProperty(value = "Audio sent low level stats") - private WebRTCAudioSendStats audioSentStats; - - @ApiModelProperty(value = "Free text information for the client") - private String clientInfo; - - @ApiModelProperty(value = "WebRTC Client's ip address") - private String clientIp; + @Schema(description = "The measured bitrate of the WebRTC Client") + private int measuredBitrate; + + @Schema(description = "The sent bitrate of the WebRTC Client") + private int sendBitrate; + + @Schema(description = "The video frame sent period of the WebRTC Client") + private double videoFrameSendPeriod; + + @Schema(description = "The audio frame send period of the WebRTC Client") + private double audioFrameSendPeriod; + + @Schema(description = "WebRTC Client Id which is basically hash of the object") + private int clientId; + + @Schema(description = "Number of video packets sent") + private long videoPacketCount; + + @Schema(description = "Number of audio packets sent") + private long audioPacketCount; + + @Schema(description = "Video sent low level stats") + private WebRTCVideoSendStats videoSentStats; + + @Schema(description = "Audio sent low level stats") + private WebRTCAudioSendStats audioSentStats; + + @Schema(description = "Free text information for the client") + private String clientInfo; + + @Schema(description = "WebRTC Client's ip address") + private String clientIp; public WebRTCClientStats(int measuredBitrate, int sendBitrate, double videoFrameSendPeriod, double audioFrameSendPeriod, long videoPacketCount, long audioPacketCount, int clientId, String clientInfo, String clientIp) { diff --git a/src/main/java/io/antmedia/rest/model/Result.java b/src/main/java/io/antmedia/rest/model/Result.java index d8aabc311..2f8eef17c 100644 --- a/src/main/java/io/antmedia/rest/model/Result.java +++ b/src/main/java/io/antmedia/rest/model/Result.java @@ -1,92 +1,91 @@ package io.antmedia.rest.model; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="Result", description="The basic result class") +@Schema(description = "The basic result class") public class Result { - /** - * Gives information about the operation. - * If it is true, operation is successful - * if it is false, operation is failed - */ - @ApiModelProperty(value = "the result of the operation") - private boolean success = false; - - /** - * Message may be filled error or information. - * It is filled with error when success field - * is false so that developer may understand what the problem is. - * It is filled with information if user makes a request, it returns success as true with additional information - */ - @ApiModelProperty(value = "the message of the operation result") - private String message; - - /** - * If operation is about adding a record, then the below field have the id of the record - */ - @ApiModelProperty(value = "the id of the record if operation is about adding a record") - private String dataId; - - @ApiModelProperty(value = "the id of errror of the operation result") - private int errorId; - - /** - * Constructor for the object - * - * @param success - * @param message - */ - public Result(boolean success, String message) { - this(success, "", message); - } - - public Result(boolean success) { - this(success, "", ""); - } - - public Result(boolean success, String dataId, String message) { - this.success = success; - this.message = message; - this.dataId = dataId; - } - - public Result(boolean result, String message, int errorId) { - this.success = result; - this.message = message; - this.errorId = errorId; - } - - public boolean isSuccess() { - return success; - } - - public void setSuccess(boolean success) { - this.success = success; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public int getErrorId() { - return errorId; - } - - public void setErrorId(int errorId) { - this.errorId = errorId; - } - - public String getDataId() { - return dataId; - } - - public void setDataId(String dataId) { - this.dataId = dataId; - } -} + /** + * Gives information about the operation. + * If it is true, operation is successful + * if it is false, operation is failed + */ + @Schema(description = "The result of the operation") + private boolean success = false; + + /** + * Message may be filled error or information. + * It is filled with error when success field + * is false so that developer may understand what the problem is. + * It is filled with information if user makes a request, it returns success as true with additional information + */ + @Schema(description = "The message of the operation result") + private String message; + + /** + * If operation is about adding a record, then the below field have the id of the record + */ + @Schema(description = "The id of the record if operation is about adding a record") + private String dataId; + + @Schema(description = "The id of error of the operation result") + private int errorId; + + /** + * Constructor for the object + * + * @param success + * @param message + */ + public Result(boolean success, String message) { + this(success, "", message); + } + + public Result(boolean success) { + this(success, "", ""); + } + + public Result(boolean success, String dataId, String message) { + this.success = success; + this.message = message; + this.dataId = dataId; + } + + public Result(boolean result, String message, int errorId) { + this.success = result; + this.message = message; + this.errorId = errorId; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getErrorId() { + return errorId; + } + + public void setErrorId(int errorId) { + this.errorId = errorId; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } +} \ No newline at end of file diff --git a/src/main/java/io/antmedia/rest/model/Version.java b/src/main/java/io/antmedia/rest/model/Version.java index b3c113006..1793b2779 100644 --- a/src/main/java/io/antmedia/rest/model/Version.java +++ b/src/main/java/io/antmedia/rest/model/Version.java @@ -1,49 +1,46 @@ package io.antmedia.rest.model; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; -@ApiModel(value="Version", description="The version class") +@Schema(description = "The version class") public class Version { - /** - * Gives information about the version name. - */ - @ApiModelProperty(value = "Version of the software") - public String versionName; - - /** - * Gives information about the version type - */ - @ApiModelProperty(value = "Version type of the software (Community or Enterprise)") - public String versionType; - - @ApiModelProperty(value = "Build number(timestamp) of the software.") - private String buildNumber; - - public String getVersionName() { - return versionName; - } - - public String getVersionType() { - return versionType; - } - - public void setVersionName(String versionName) { - this.versionName = versionName; - } - - public void setVersionType(String versionType) { - this.versionType = versionType; - } - - public void setBuildNumber(String value) { - this.buildNumber = value; - } - - public String getBuildNumber() { - return buildNumber; - } - - + /** + * Gives information about the version name. + */ + @Schema(description = "Version of the software") + public String versionName; + + /** + * Gives information about the version type + */ + @Schema(description = "Version type of the software (Community or Enterprise)") + public String versionType; + + @Schema(description = "Build number(timestamp) of the software.") + private String buildNumber; + + public String getVersionName() { + return versionName; + } + + public String getVersionType() { + return versionType; + } + + public void setVersionName(String versionName) { + this.versionName = versionName; + } + + public void setVersionType(String versionType) { + this.versionType = versionType; + } + + public void setBuildNumber(String value) { + this.buildNumber = value; + } + + public String getBuildNumber() { + return buildNumber; + } } diff --git a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java index a8c70962a..0381dddb7 100644 --- a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java +++ b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java @@ -1876,9 +1876,9 @@ public void testSaveDetection(DataStore dataStore){ List list = dataStore.getDetectionList("id", 0, 10); assertEquals(1,list.size()); - assertEquals(item1, list.get(0).objectName); - assertEquals(probability1, list.get(0).probability,0.1F); - assertEquals(detectionTime, list.get(0).detectionTime); + assertEquals(item1, list.get(0).getObjectName()); + assertEquals(probability1, list.get(0).getProbability(),0.1F); + assertEquals(detectionTime, list.get(0).getDetectionTime()); assertEquals(minX, list.get(0).getMinX(), 0.0001); assertEquals(minY, list.get(0).getMinY(), 0.0001); From 387ab95e9a8f684ad441dfb28f837f9c39e58a37 Mon Sep 17 00:00:00 2001 From: mekya Date: Sat, 23 Mar 2024 03:17:57 +0300 Subject: [PATCH 05/12] Fix compatibility issue and add OpenAPIServlet --- pom.xml | 10 +++--- .../antmedia/rest/BroadcastRestService.java | 34 +++++++++++++++++-- .../java/io/antmedia/rest/VoDRestService.java | 31 ++++++++++++++--- src/main/server/webapps/root/WEB-INF/web.xml | 15 ++++++++ 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index c74928991..3af500651 100644 --- a/pom.xml +++ b/pom.xml @@ -350,11 +350,11 @@ morphia-core ${morphia.version} - - io.swagger.core.v3 - swagger-jaxrs2-jakarta - 2.2.7 - + + io.swagger.core.v3 + swagger-jaxrs2-jakarta + ${swagger-jaxrs2-jakarta.version} + io.vertx vertx-dropwizard-metrics diff --git a/src/main/java/io/antmedia/rest/BroadcastRestService.java b/src/main/java/io/antmedia/rest/BroadcastRestService.java index 52c868af1..24c688506 100644 --- a/src/main/java/io/antmedia/rest/BroadcastRestService.java +++ b/src/main/java/io/antmedia/rest/BroadcastRestService.java @@ -58,6 +58,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.servers.Server; @OpenAPIDefinition( @@ -246,6 +247,22 @@ public Result deleteBroadcast(@Parameter(description = " Id of the broadcast", r return super.deleteBroadcast(id); } + + /** + * Use {@link #deleteBroadcastsBulk(String[])} for compliance + */ + @Hidden + @Deprecated + @DELETE + @Consumes({ MediaType.APPLICATION_JSON }) + @Path("/bulk") + @Produces(MediaType.APPLICATION_JSON) + @Override + public Result deleteBroadcasts(@Parameter(description = "Id of the broadcast", required = true) String[] streamIds) + { + return super.deleteBroadcasts(streamIds); + } + @Operation(description = "Delete multiple broadcasts from data store and stop if they are broadcasting") @ApiResponse(responseCode = "200", description = "If it's deleted, success is true. If it's not deleted, success if false.", content = @Content( @@ -257,10 +274,14 @@ public Result deleteBroadcast(@Parameter(description = " Id of the broadcast", r @Consumes({ MediaType.APPLICATION_JSON }) @Path("/bulk") @Produces(MediaType.APPLICATION_JSON) - @Override - public Result deleteBroadcasts(@Parameter(description = " Id of the broadcast", required = true) String[] streamIds) + public Result deleteBroadcastsBulk(@Parameter(description = "Comma separated stream Ids", required = true) @QueryParam("ids") String streamIds) { - return super.deleteBroadcasts(streamIds); + if (StringUtils.isNotBlank(streamIds)) { + return super.deleteBroadcasts(streamIds.split(",")); + } + else { + return new Result(false, "ids parameter is blank"); + } } @@ -364,6 +385,7 @@ public Result updateSeekTime(@Parameter(description="Broadcast id", required = t } + @Hidden @Deprecated @POST @Consumes(MediaType.APPLICATION_JSON) @@ -461,6 +483,7 @@ private void logRtmpEndpointInfo(String id, Endpoint endpoint, boolean result) { } } + @Hidden @Deprecated @DELETE @Consumes(MediaType.APPLICATION_JSON) @@ -1164,6 +1187,7 @@ public List getWebRTCClientStatsListV2(@Parameter(description return super.getWebRTCClientStatsList(offset, size, streamId); } + @Hidden @Deprecated @Operation(summary = "Returns filtered broadcast list according to type", description = "Useful for retrieving IP Camera and Stream Sources from the entire broadcast list. For sorting mechanisms, using Mongo DB is recommended.", @@ -1654,6 +1678,7 @@ public RootRestService.RoomInfo getRoomInfo(@Parameter(description = "Room id", @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/add") @Produces(MediaType.APPLICATION_JSON) + @Hidden @Deprecated(since="2.6.2", forRemoval=true) public Result addStreamToTheRoomDeprecated(@Parameter(description="Room id", required=true) @PathParam("room_id") String roomId, @Parameter(description="Stream id to add to the conference room",required = true) @QueryParam("streamId") String streamId){ @@ -1695,6 +1720,7 @@ public Result addStreamToTheRoom(@Parameter(description="Room id", required=true @Consumes({ MediaType.APPLICATION_JSON }) @Path("/conference-rooms/{room_id}/delete") @Produces(MediaType.APPLICATION_JSON) + @Hidden @Deprecated(since="2.6.2", forRemoval=true) public Result deleteStreamFromTheRoomDeprecated(@Parameter(description ="Room id", required=true) @PathParam("room_id") String roomId, @Parameter(description="Stream id to delete from the conference room",required = true) @QueryParam("streamId") String streamId){ @@ -1725,6 +1751,7 @@ public Result deleteStreamFromTheRoom(@Parameter(description="Room id", required * @param search * @return */ + @Hidden @Deprecated(since = "2.7.0", forRemoval = true) @GET @Path("/webrtc-viewers/list/{offset}/{size}") @@ -1743,6 +1770,7 @@ public List getWebRTCViewerList(@Parameter(description = "This * @param viewerId * @return */ + @Hidden @Deprecated(since = "2.7.0", forRemoval = true) @Operation(description = "Stop player with a specified id") @POST diff --git a/src/main/java/io/antmedia/rest/VoDRestService.java b/src/main/java/io/antmedia/rest/VoDRestService.java index b84201368..f6cc501d5 100644 --- a/src/main/java/io/antmedia/rest/VoDRestService.java +++ b/src/main/java/io/antmedia/rest/VoDRestService.java @@ -3,6 +3,7 @@ import java.io.InputStream; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.media.multipart.FormDataParam; import org.springframework.stereotype.Component; @@ -10,6 +11,7 @@ import io.antmedia.rest.BroadcastRestService.SimpleStat; import io.antmedia.rest.model.Result; import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -119,9 +121,11 @@ public Result deleteVoD( return super.deleteVoD(id); } - @Operation(summary = "Delete bulk VoD Files based on Vod Id", description = "Deletes multiple VoD files from the database by their IDs.", responses = { - @ApiResponse(responseCode = "200", description = "VoD files deleted successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) - }) + /** + * Use {@link #deleteVoDsBulk(String)} + */ + @Deprecated + @Hidden @DELETE @Path("/bulk") @Consumes({ MediaType.APPLICATION_JSON }) @@ -131,6 +135,25 @@ public Result deleteVoDs( @Parameter(description = "IDs of the VoD files", required = true) String[] vodIds) { return super.deleteVoDs(vodIds); } + + + @Operation(summary = "Delete bulk VoD Files based on Vod Id", description = "Deletes multiple VoD files from the database by their IDs.", responses = { + @ApiResponse(responseCode = "200", description = "VoD files deleted successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) + }) + @DELETE + @Path("/bulk") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces(MediaType.APPLICATION_JSON) + public Result deleteVoDsBulk( + @Parameter(description = "Comma separated IDs of the VoD files", required = true) @QueryParam("ids") String vodIds) { + if (StringUtils.isNotBlank(vodIds)) { + return super.deleteVoDs(vodIds.split(",")); + } + else { + return new Result(false, "ids parameter is blank. Please give comma-separated vod ids to the 'ids' as query parameter"); + } + } + @Operation(summary = "Upload external VoD file to Ant Media Server", description = "Uploads an external VoD file to Ant Media Server.", responses = { @@ -176,7 +199,7 @@ public Result unlinksVoD( - + @Hidden @Operation(summary = "Deprecated. Use import VoDs.", description = "Synchronizes VoD Folder and adds them to VoD database if any file exist and creates symbolic links to that folder.", deprecated = true, responses = { @ApiResponse(responseCode = "200", description = "VoD files synchronized successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Result.class))) }) diff --git a/src/main/server/webapps/root/WEB-INF/web.xml b/src/main/server/webapps/root/WEB-INF/web.xml index 22be4d89d..2674cb9cf 100644 --- a/src/main/server/webapps/root/WEB-INF/web.xml +++ b/src/main/server/webapps/root/WEB-INF/web.xml @@ -113,6 +113,21 @@ /rest/v2/request/* + + OpenApi + io.swagger.v3.jaxrs2.integration.OpenApiServlet + + + openApi.configuration.resourcePackages + io.antmedia.console.rest + + + + + OpenApi + /openapi/* + +