From af31ebe95da70c43eb3c9c6c0baef57c1c10a8bb Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Fri, 7 Jan 2022 17:19:32 +0100 Subject: [PATCH] Add Swagger annotations to document endpoints By adding certain annotations, the documentation as generated by Swagger becomes enriched. --- .../rest/service/CustomOpenApiResource.java | 1 - .../plugin/rest/service/GroupService.java | 82 +++++++--- .../rest/service/MUCRoomAdminsService.java | 74 ++++++--- .../rest/service/MUCRoomMembersService.java | 74 ++++++--- .../rest/service/MUCRoomOutcastsService.java | 74 ++++++--- .../rest/service/MUCRoomOwnersService.java | 76 ++++++--- .../plugin/rest/service/MUCRoomService.java | 150 +++++++++++++----- .../plugin/rest/service/MessageService.java | 26 ++- .../rest/service/MsgArchiveService.java | 16 +- .../plugin/rest/service/RestAPIService.java | 78 +++++++-- .../rest/service/SecurityAuditLogService.java | 38 +++-- .../plugin/rest/service/SessionService.java | 46 ++++-- .../rest/service/StatisticsService.java | 19 ++- .../plugin/rest/service/UserGroupService.java | 85 +++++++--- .../rest/service/UserLockoutService.java | 33 +++- .../rest/service/UserRosterService.java | 79 ++++++--- .../plugin/rest/service/UserService.java | 80 +++++++--- .../rest/service/UserServiceLegacy.java | 26 +-- 18 files changed, 801 insertions(+), 256 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/CustomOpenApiResource.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/CustomOpenApiResource.java index 5fbe69e445..a180d25602 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/CustomOpenApiResource.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/CustomOpenApiResource.java @@ -26,7 +26,6 @@ import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; -import org.glassfish.jersey.server.ServerProperties; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.container.PluginMetadataHelper; import org.jivesoftware.openfire.plugin.rest.RESTServicePlugin; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/GroupService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/GroupService.java index e5428a632c..2abeac82b8 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/GroupService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/GroupService.java @@ -1,22 +1,24 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.jivesoftware.openfire.plugin.rest.controller.GroupController; import org.jivesoftware.openfire.plugin.rest.entity.GroupEntities; import org.jivesoftware.openfire.plugin.rest.entity.GroupEntity; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + @Path("restapi/v1/groups") +@Tag(name="User Group", description = "Managing Openfire user groupings.") public class GroupService { private GroupController groupController; @@ -27,34 +29,78 @@ public void init() { } @GET + @Operation( summary = "Get groups", + description = "Get a list of all user groups.", + responses = { + @ApiResponse(responseCode = "200", description = "All groups", content = @Content(schema = @Schema(implementation = GroupEntities.class))) + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public GroupEntities getGroups() throws ServiceException { + public GroupEntities getGroups() throws ServiceException + { return new GroupEntities(groupController.getGroups()); } @POST - public Response createGroup(GroupEntity groupEntity) throws ServiceException { + @Operation( summary = "Create group", + description = "Create a new user group.", + responses = { + @ApiResponse(responseCode = "201", description = "Group created."), + @ApiResponse(responseCode = "400", description = "Group or group name missing."), + @ApiResponse(responseCode = "409", description = "Group already exists.") + }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response createGroup( + @RequestBody(description = "The group that needs to be created.", required = true) GroupEntity groupEntity) + throws ServiceException + { groupController.createGroup(groupEntity); return Response.status(Response.Status.CREATED).build(); } - + @GET @Path("/{groupName}") + @Operation( summary = "Get group", + description = "Get one specific user group by name.", + responses = { + @ApiResponse(responseCode = "200", description = "The group.", content = @Content(schema = @Schema(implementation = GroupEntity.class))), + @ApiResponse(responseCode = "404", description = "Group with this name not found.") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public GroupEntity getGroup(@PathParam("groupName") String groupName) throws ServiceException { + public GroupEntity getGroup(@Parameter(description = "The name of the group that needs to be fetched.", example = "Colleagues", required = true) @PathParam("groupName") String groupName) + throws ServiceException + { return groupController.getGroup(groupName); } - + @PUT @Path("/{groupName}") - public Response updateGroup(@PathParam("groupName") String groupName, GroupEntity groupEntity) throws ServiceException { + @Operation( summary = "Update group", + description = "Updates / overwrites an existing user group. Note that the name of the group cannot be changed.", + responses = { + @ApiResponse(responseCode = "200", description = "Group updated."), + @ApiResponse(responseCode = "400", description = "Group or group name missing, or name does not match existing group."), + @ApiResponse(responseCode = "404", description = "Group with this name not found."), + }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response updateGroup(@Parameter(description = "The name of the group that needs to be fetched.", example = "Colleagues", required = true) @PathParam("groupName") String groupName, + @RequestBody(description = "The new group definition that needs to overwrite the old definition.", required = true) GroupEntity groupEntity ) + throws ServiceException + { groupController.updateGroup(groupName, groupEntity); return Response.status(Response.Status.OK).build(); } @DELETE @Path("/{groupName}") - public Response deleteGroup(@PathParam("groupName") String groupName) throws ServiceException { + @Operation( summary = "Delete group", + description = "Removes an existing user group.", + responses = { + @ApiResponse(responseCode = "200", description = "Group deleted."), + @ApiResponse(responseCode = "400", description = "Group not found.") + }) + public Response deleteGroup(@Parameter(description = "The name of the group that needs to be removed.", example = "Colleagues", required = true) @PathParam("groupName") String groupName) + throws ServiceException + { groupController.deleteGroup(groupName); return Response.status(Response.Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAdminsService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAdminsService.java index 57d10bc878..defee2f8dd 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAdminsService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAdminsService.java @@ -1,50 +1,86 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + +import javax.ws.rs.*; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; -import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; - @Path("restapi/v1/chatrooms/{roomName}/admins") +@Tag(name = "Chat room", description = "Managing Multi-User chat rooms.") public class MUCRoomAdminsService { @POST @Path("/{jid}") - public Response addMUCRoomAdmin(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("jid") String jid, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room admin", + description = "Adds an administrator to a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Administrator added to the room.") + }) + public Response addMUCRoomAdmin( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The (bare) JID of the entity that is to be added as an admin.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC room to which an administrator is to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addAdmin(serviceName, roomName, jid); return Response.status(Status.CREATED).build(); } @POST @Path("/group/{groupname}") - public Response addMUCRoomAdminGroup(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("groupname") String groupname, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room admins", + description = "Adds all members of an Openfire user group as administrator to a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Administrators added to the room.") + }) + public Response addMUCRoomAdminGroup( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the user group from which all members will be administrators of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC room to which administrators are to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addAdmin(serviceName, roomName, groupname); return Response.status(Status.CREATED).build(); } @DELETE @Path("/{jid}") - public Response deleteMUCRoomAdmin(@PathParam("jid") String jid, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room admin", + description = "Removes a user as an administrator of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Administrator removed from the room.") + }) + public Response deleteMUCRoomAdmin( + @Parameter(description = "The (bare) JID of the entity that is to be removed as an administrators of the room.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room from which an administrator is to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, jid); return Response.status(Status.OK).build(); } @DELETE @Path("/group/{groupname}") - public Response deleteMUCRoomAdminGroup(@PathParam("groupname") String groupname, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room admins", + description = "Removes all members of an Openfire user group as administrator of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Administrators removed from the room.") + }) + public Response deleteMUCRoomAdminGroup( + @Parameter(description = "The name of the user group from which all members will be removed as administrators of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room to which administrators are to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, groupname); return Response.status(Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomMembersService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomMembersService.java index 161ca484c6..fa72b021ac 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomMembersService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomMembersService.java @@ -1,50 +1,86 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + +import javax.ws.rs.*; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; -import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; - @Path("restapi/v1/chatrooms/{roomName}/members") +@Tag(name = "Chat room", description = "Managing Multi-User chat rooms.") public class MUCRoomMembersService { @POST @Path("/{jid}") - public Response addMUCRoomMember(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("jid") String jid, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room member", + description = "Registers a JID as a member of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Member added to the room.") + }) + public Response addMUCRoomMember( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The (bare) JID of the entity that is to be a registered member.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC room to which a member is to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addMember(serviceName, roomName, jid); return Response.status(Status.CREATED).build(); } @POST @Path("/group/{groupname}") - public Response addMUCRoomMemberGroup(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("groupname") String groupname, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room members", + description = "Adds all members of an Openfire user group as registered members of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Members added to the room.") + }) + public Response addMUCRoomMemberGroup( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the user group from which all members will be registered members of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC room to which members are to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addMember(serviceName, roomName, groupname); return Response.status(Status.CREATED).build(); } @DELETE @Path("/{jid}") - public Response deleteMUCRoomMember(@PathParam("jid") String jid, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room member", + description = "Removes a JID as a registered member of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Member removed from the room.") + }) + public Response deleteMUCRoomMember( + @Parameter(description = "The (bare) JID of the entity that is to be removed as a registered member of the room.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room from which a member is to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, jid); return Response.status(Status.OK).build(); } @DELETE @Path("/group/{groupname}") - public Response deleteMUCRoomMemberGroup(@PathParam("groupname") String groupname, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room members", + description = "Removes all members of an Openfire user group as registered members of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Member removed from the room.") + }) + public Response deleteMUCRoomMemberGroup( + @Parameter(description = "The name of the user group from which all members will be removed as registered members of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room to which members are to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, groupname); return Response.status(Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOutcastsService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOutcastsService.java index 1aa450ebee..18362a9efa 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOutcastsService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOutcastsService.java @@ -1,50 +1,86 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + +import javax.ws.rs.*; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; -import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; - @Path("restapi/v1/chatrooms/{roomName}/outcasts") +@Tag(name = "Chat room", description = "Managing Multi-User chat rooms.") public class MUCRoomOutcastsService { @POST @Path("/{jid}") - public Response addMUCRoomOutcast(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("jid") String jid, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room outcast", + description = "Marks a JID as outcast of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "JID marked as outcast.") + }) + public Response addMUCRoomOutcast( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The (bare) JID of the entity that is to be marked as an outcast.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC room in which the JID is outcast.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addOutcast(serviceName, roomName, jid); return Response.status(Status.CREATED).build(); } @POST @Path("/group/{groupname}") - public Response addMUCRoomOutcastGroup(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("groupname") String groupname, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room outcasts", + description = "Marks all members of an Openfire user group as outcasts of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Group members marked as outcast.") + }) + public Response addMUCRoomOutcastGroup( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the user group from which all members will be marked as outcast of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC room in which the group members are outcast.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addOutcast(serviceName, roomName, groupname); return Response.status(Status.CREATED).build(); } @DELETE @Path("/{jid}") - public Response deleteMUCRoomOutcast(@PathParam("jid") String jid, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room outcast", + description = "Unmarks a JID as outcast of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "JID no longer marked as outcast.") + }) + public Response deleteMUCRoomOutcast( + @Parameter(description = "The (bare) JID of the entity that is to be removed as an outcast of the room.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room from which an outcast is to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, jid); return Response.status(Status.OK).build(); } @DELETE @Path("/group/{groupname}") - public Response deleteMUCRoomOutcastGroup(@PathParam("groupname") String groupname, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room outcasts", + description = "Removes all members of an Openfire user group as outcast of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Group members no longer marked as outcast.") + }) + public Response deleteMUCRoomOutcastGroup( + @Parameter(description = "The name of the user group from which all members will be removed as outcast of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room to which outcast are to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, groupname); return Response.status(Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOwnersService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOwnersService.java index 7749060311..5fc855632f 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOwnersService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomOwnersService.java @@ -1,50 +1,88 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + +import javax.ws.rs.*; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; -import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; - @Path("restapi/v1/chatrooms/{roomName}/owners") +@Tag(name = "Chat room", description = "Managing Multi-User chat rooms.") public class MUCRoomOwnersService { @POST @Path("/{jid}") - public Response addMUCRoomOwner(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("jid") String jid, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room owner", + description = "Adds an owner to a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Owner added to the room.") + }) + public Response addMUCRoomOwner( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The (bare) JID of the entity that is to be added as an owner.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC room to which an owner is to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addOwner(serviceName, roomName, jid); return Response.status(Status.CREATED).build(); } @POST @Path("/group/{groupname}") - public Response addMUCRoomOwnerGroup(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("groupname") String groupname, @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Add room owners", + description = "Adds all members of an Openfire user group as owners to a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Owners added to the room.") + }) + public Response addMUCRoomOwnerGroup( + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the user group from which all members will be owners of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC room to which owners are to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { MUCRoomController.getInstance().addOwner(serviceName, roomName, groupname); return Response.status(Status.CREATED).build(); } @DELETE @Path("/{jid}") - public Response deleteMUCRoomOwner(@PathParam("jid") String jid, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room owner", + description = "Removes a user as an owner of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Owner removed from the room."), + @ApiResponse(responseCode = "409", description = "When removal of this owner would leave the room without any owners (which is not allowed: a room must always have an owner).") + }) + public Response deleteMUCRoomOwner( + @Parameter(description = "The (bare) JID of the entity that is to be removed as an owner of the room.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room from which an owner is to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, jid); return Response.status(Status.OK).build(); } @DELETE @Path("/group/{groupname}") - public Response deleteMUCRoomOwnerGroup(@PathParam("groupname") String groupname, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @PathParam("roomName") String roomName) throws ServiceException { + @Operation( summary = "Remove room owners", + description = "Removes all members of an Openfire user group as owner of a multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Owners removed from the room."), + @ApiResponse(responseCode = "409", description = "When removal of these owners would leave the room without any owners (which is not allowed: a room must always have an owner).") + }) + public Response deleteMUCRoomOwnerGroup( + @Parameter(description = "The name of the user group from which all members will be removed as owners of the room.", example = "Operators", required = true) @PathParam("groupname") String groupname, + @Parameter(description = "The name of the MUC service that the MUC room is part of.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "The name of the MUC room to which owners are to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) + throws ServiceException + { + // FIXME: check if this removes _all_ affiliations, which probably would be more than we're bargaining for. MUCRoomController.getInstance().deleteAffiliation(serviceName, roomName, groupname); return Response.status(Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java index 833cd5b3ad..2b705ad401 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java @@ -1,97 +1,173 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; +import org.jivesoftware.openfire.plugin.rest.entity.*; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + +import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.jivesoftware.openfire.plugin.rest.entity.*; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; -import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; - @Path("restapi/v1/chatrooms") +@Tag(name = "Chat room", description = "Managing Multi-User chat rooms.") public class MUCRoomService { @GET + @Operation( summary = "Get chat rooms", + description = "Get a list of all multi-user chat rooms of a particular chat room service.", + responses = { + @ApiResponse(responseCode = "200", description = "All chat rooms", content = @Content(schema = @Schema(implementation = MUCRoomEntities.class))) + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public MUCRoomEntities getMUCRooms(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - @DefaultValue(MUCChannelType.PUBLIC) @QueryParam("type") String channelType, - @QueryParam("search") String roomSearch, - @DefaultValue("false") @QueryParam("expandGroups") Boolean expand) { + public MUCRoomEntities getMUCRooms( + @Parameter(description = "The name of the MUC service for which to return all chat rooms.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "Room type-based filter: 'all' or 'public'", examples = { @ExampleObject(value = "public", description = "Only return rooms configured with 'List Room in Directory'"), @ExampleObject(value = "all", description = "Return all rooms")}, required = false) @DefaultValue(MUCChannelType.PUBLIC) @QueryParam("type") String channelType, + @Parameter(description = "Search/Filter by room name.\nThis act like the wildcard search %String%", example = "conference", required = false) @QueryParam("search") String roomSearch, + @Parameter(description = "For all groups defined in owners, admins, members and outcasts, list individual members instead of the group name.", required = false) @DefaultValue("false") @QueryParam("expandGroups") Boolean expand) { return MUCRoomController.getInstance().getChatRooms(serviceName, channelType, roomSearch, expand); } @GET @Path("/{roomName}") + @Operation( summary = "Get chat room", + description = "Get information of a specific multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "The chat room", content = @Content(schema = @Schema(implementation = MUCRoomEntity.class))), + @ApiResponse(responseCode = "404", description = "The chat room can not be found") + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public MUCRoomEntity getMUCRoomJSON2(@PathParam("roomName") String roomName, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - @DefaultValue("false") @QueryParam("expandGroups") Boolean expand) throws ServiceException { + public MUCRoomEntity getMUCRoomJSON2( + @Parameter(description = "The name of the MUC room to return.", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The name of the MUC service for which to return a chat room.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @Parameter(description = "For all groups defined in owners, admins, members and outcasts, list individual members instead of the group name.", required = false) @DefaultValue("false") @QueryParam("expandGroups") Boolean expand) + throws ServiceException + { return MUCRoomController.getInstance().getChatRoom(roomName, serviceName, expand); } - + @Operation( summary = "Delete chat room", + description = "Removes an existing multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Room deleted."), + @ApiResponse(responseCode = "404", description = "Room not found.") + }) @DELETE @Path("/{roomName}") - public Response deleteMUCRoom(@PathParam("roomName") String roomName, - @DefaultValue("conference") @QueryParam("servicename") String serviceName) throws ServiceException { + public Response deleteMUCRoom( + @Parameter(description = "The name of the MUC room to delete.", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The name of the MUC service from which to delete a chat room.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName) + throws ServiceException + { MUCRoomController.getInstance().deleteChatRoom(roomName, serviceName); return Response.status(Status.OK).build(); } @POST - public Response createMUCRoom(@DefaultValue("conference") @QueryParam("servicename") String serviceName, - MUCRoomEntity mucRoomEntity) throws ServiceException { + @Operation( summary = "Create chat room", + description = "Create a new multi-user chat room.", + responses = { + @ApiResponse(responseCode = "201", description = "Room created.") + }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response createMUCRoom( + @Parameter(description = "The name of the MUC service in which to create a chat room.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @RequestBody(description = "The MUC room that needs to be created.", required = true) MUCRoomEntity mucRoomEntity) + throws ServiceException + { MUCRoomController.getInstance().createChatRoom(serviceName, mucRoomEntity); return Response.status(Status.CREATED).build(); } @PUT @Path("/{roomName}") - public Response udpateMUCRoom(@PathParam("roomName") String roomName, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, MUCRoomEntity mucRoomEntity) - throws ServiceException { + @Operation( summary = "Update chat room", + description = "Updates an existing multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Room updated.") + }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response updateMUCRoom( + @Parameter(description = "The name of the chat room that needs to be updated", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The name of the MUC service in which to update a chat room.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @RequestBody(description = "The new MUC room definition that needs to overwrite the old definition.", required = true) MUCRoomEntity mucRoomEntity) + throws ServiceException + { MUCRoomController.getInstance().updateChatRoom(roomName, serviceName, mucRoomEntity); return Response.status(Status.OK).build(); } @GET @Path("/{roomName}/participants") + @Operation( summary = "Get room participants", + description = "Get all participants of a specific multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "The chat room participants", content = @Content(schema = @Schema(implementation = ParticipantEntities.class))) + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public ParticipantEntities getMUCRoomParticipants(@PathParam("roomName") String roomName, - @DefaultValue("conference") @QueryParam("servicename") String serviceName) { + public ParticipantEntities getMUCRoomParticipants( + @Parameter(description = "The name of the chat room for which to return participants", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The name of the chat room's MUC service.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName) + throws ServiceException + { return MUCRoomController.getInstance().getRoomParticipants(roomName, serviceName); } @GET @Path("/{roomName}/occupants") + @Operation( summary = "Get room occupants", + description = "Get all occupants of a specific multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "The chat room participants", content = @Content(schema = @Schema(implementation = OccupantEntities.class))) + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public OccupantEntities getMUCRoomOccupants(@PathParam("roomName") String roomName, - @DefaultValue("conference") @QueryParam("servicename") String serviceName) { + public OccupantEntities getMUCRoomOccupants( + @Parameter(description = "The name of the chat room for which to return occupants", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The name of the chat room's MUC service.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName) + { return MUCRoomController.getInstance().getRoomOccupants(roomName, serviceName); } @GET @Path("/{roomName}/chathistory") + @Operation( summary = "Get room history", + description = "Get messages that have been exchanged in a specific multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "The chat room message history", content = @Content(schema = @Schema(implementation = MUCRoomMessageEntities.class))), + @ApiResponse(responseCode = "404", description = "Room not found.") + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public MUCRoomMessageEntities getMUCRoomHistory(@PathParam("roomName") String roomName, - @DefaultValue("conference") @QueryParam("servicename") String serviceName) throws ServiceException { + public MUCRoomMessageEntities getMUCRoomHistory( + @Parameter(description = "The name of the chat room for which to return message history", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The name of the chat room's MUC service.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName) + throws ServiceException + { return MUCRoomController.getInstance().getRoomHistory(roomName, serviceName); } @POST @Path("/{roomName}/invite/{jid}") - public Response inviteUserToMUCRoom(@PathParam("roomName") String roomName, @PathParam("jid") String jid, - @DefaultValue("conference") @QueryParam("servicename") String serviceName, - MUCInvitationEntity mucInvitationEntity) throws ServiceException { + @Operation( summary = "Invite user", + description = "Invites a user to join a specific multi-user chat room.", + responses = { + @ApiResponse(responseCode = "200", description = "Invitation sent") + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response inviteUserToMUCRoom( + @Parameter(description = "The name of the chat room in which to invite a user", example = "lobby", required = true) @PathParam("roomName") String roomName, + @Parameter(description = "The JID of the entity to invite into the room", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The name of the chat room's MUC service.", example = "conference", required = false) @DefaultValue("conference") @QueryParam("servicename") String serviceName, + @RequestBody(description = "The invitation message to send.", required = true) MUCInvitationEntity mucInvitationEntity) + throws ServiceException + { MUCRoomController.getInstance().inviteUser(serviceName, roomName, jid, mucInvitationEntity); return Response.status(Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MessageService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MessageService.java index c8fad0f51a..4b0def0e3b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MessageService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MessageService.java @@ -1,15 +1,22 @@ package org.jivesoftware.openfire.plugin.rest.service; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.MessageController; +import org.jivesoftware.openfire.plugin.rest.entity.MessageEntity; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + import javax.annotation.PostConstruct; +import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.jivesoftware.openfire.plugin.rest.controller.MessageController; -import org.jivesoftware.openfire.plugin.rest.entity.MessageEntity; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; - @Path("restapi/v1/messages") +@Tag(name = "Message", description = "Sending (chat) messages to users.") public class MessageService { private MessageController messageController; @@ -21,7 +28,16 @@ public void init() { @POST @Path("/users") - public Response sendBroadcastMessage(MessageEntity messageEntity) throws ServiceException { + @Operation( summary = "Broadcast", + description = "Sends a message to all users that are currently online.", + responses = { + @ApiResponse(responseCode = "201", description = "Message is sent."), + @ApiResponse(responseCode = "400", description = "The message content is empty or missing."), + }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response sendBroadcastMessage(@RequestBody(description = "The message that is to be broadcast.", required = true) MessageEntity messageEntity) + throws ServiceException + { messageController.sendBroadcastMessage(messageEntity); return Response.status(Response.Status.CREATED).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MsgArchiveService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MsgArchiveService.java index 3b068f41bf..249e618bc3 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MsgArchiveService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MsgArchiveService.java @@ -1,5 +1,11 @@ package org.jivesoftware.openfire.plugin.rest.service; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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.tags.Tag; import org.jivesoftware.openfire.plugin.rest.controller.MsgArchiveController; import org.jivesoftware.openfire.plugin.rest.entity.MsgArchiveEntity; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; @@ -13,6 +19,7 @@ import javax.ws.rs.core.MediaType; @Path("restapi/v1/archive/messages/unread/{jid}") +@Tag(name = "Message Archive", description = "Server-sided storage of chat messages.") public class MsgArchiveService { private MsgArchiveController archive; @@ -23,8 +30,15 @@ public void init() { } @GET + @Operation( summary = "Unread message count", + description = "Gets a count of messages that haven't been delivered to the user yet.", + responses = { + @ApiResponse(responseCode = "200", description = "A message count", content = @Content(schema = @Schema(implementation = MsgArchiveEntity.class))) + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public MsgArchiveEntity getUnReadMessagesCount(@PathParam("jid") String jidStr) throws ServiceException { + public MsgArchiveEntity getUnReadMessagesCount(@Parameter(description = "The (bare) JID of the user for which the unread message count needs to be fetched.", example = "john@example.org", required = true) @PathParam("jid") String jidStr) + throws ServiceException + { JID jid = new JID(jidStr); int msgCount = archive.getUnReadMessagesCount(jid); return new MsgArchiveEntity(jidStr, msgCount); diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/RestAPIService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/RestAPIService.java index a259c99f1b..e5c23ca54b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/RestAPIService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/RestAPIService.java @@ -1,22 +1,24 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.jivesoftware.openfire.plugin.rest.RESTServicePlugin; import org.jivesoftware.openfire.plugin.rest.entity.SystemProperties; import org.jivesoftware.openfire.plugin.rest.entity.SystemProperty; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + @Path("restapi/v1/system/properties") +@Tag(name = "System", description = "Managing Openfire system configuration") public class RestAPIService { private RESTServicePlugin plugin; @@ -27,34 +29,78 @@ public void init() { } @GET + @Operation( summary = "Get system properties", + description = "Get all Openfire system properties.", + responses = { + @ApiResponse(responseCode = "200", description = "The system properties.", content = @Content(schema = @Schema(implementation = SystemProperties.class))), + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public SystemProperties getSystemProperties() { return plugin.getSystemProperties(); } - + @GET @Path("/{propertyKey}") + @Operation( summary = "Get system property", + description = "Get a specific Openfire system property.", + responses = { + @ApiResponse(responseCode = "200", description = "The requested system property.", content = @Content(schema = @Schema(implementation = SystemProperty.class))), + @ApiResponse(responseCode = "404", description = "The system property could not be found.") + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public SystemProperty getSystemProperty(@PathParam("propertyKey") String propertyKey) throws ServiceException { + public SystemProperty getSystemProperty( + @Parameter(description = "The name of the system property to return.", example = "foo.bar.xyz", required = true) @PathParam("propertyKey") String propertyKey) + throws ServiceException + { return plugin.getSystemProperty(propertyKey); } @POST - public Response createSystemProperty(SystemProperty systemProperty) throws ServiceException { + @Operation( summary = "Create system property", + description = "Create a new Openfire system property. Will overwrite a pre-existing system property that uses the same name.", + responses = { + @ApiResponse(responseCode = "201", description = "The system property is created."), + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response createSystemProperty( + @RequestBody(description = "The system property to create.", required = true) SystemProperty systemProperty) + throws ServiceException + { plugin.createSystemProperty(systemProperty); return Response.status(Response.Status.CREATED).build(); } @PUT @Path("/{propertyKey}") - public Response updateUser(@PathParam("propertyKey") String propertyKey, SystemProperty systemProperty) throws ServiceException { + @Operation( summary = "Update system property", + description = "Updates an existing Openfire system property.", + responses = { + @ApiResponse(responseCode = "200", description = "The system property is updated."), + @ApiResponse(responseCode = "400", description = "The provided system property does not match the name in the URL."), + @ApiResponse(responseCode = "404", description = "The system property could not be found.") + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response updateSystemProperty( + @Parameter(description = "The name of the system property to update.", example = "foo.bar.xyz", required = true) @PathParam("propertyKey") String propertyKey, + @RequestBody(description = "The new system property definition that replaced an existing definition.", required = true) SystemProperty systemProperty) + throws ServiceException + { plugin.updateSystemProperty(propertyKey, systemProperty); return Response.status(Response.Status.OK).build(); } @DELETE @Path("/{propertyKey}") - public Response deleteUser(@PathParam("propertyKey") String propertyKey) throws ServiceException { + @Operation( summary = "Remove system property", + description = "Removes an existing Openfire system property.", + responses = { + @ApiResponse(responseCode = "200", description = "The system property is deleted."), + @ApiResponse(responseCode = "404", description = "The system property could not be found.") + }) + public Response deleteSystemProperty( + @Parameter(description = "The name of the system property to delete.", example = "foo.bar.xyz", required = true) @PathParam("propertyKey") String propertyKey) + throws ServiceException + { plugin.deleteSystemProperty(propertyKey); return Response.status(Response.Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/SecurityAuditLogService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/SecurityAuditLogService.java index 9a730f3e26..da4c4f0a48 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/SecurityAuditLogService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/SecurityAuditLogService.java @@ -1,18 +1,21 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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.tags.Tag; import org.jivesoftware.openfire.plugin.rest.controller.SecurityAuditLogController; import org.jivesoftware.openfire.plugin.rest.entity.SecurityAuditLogs; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + @Path("restapi/v1/logs/security") +@Tag(name = "Security Audit Log", description = "Inspecting the security audit log.") public class SecurityAuditLogService { private SecurityAuditLogController securityAuditLogController; @@ -23,12 +26,21 @@ public void init() { } @GET + @Operation( summary = "Get log entries", + description = "Retrieve entries from the security audit log.", + responses = { + @ApiResponse(responseCode = "200", description = "The requested log entries.", content = @Content(schema = @Schema(implementation = SecurityAuditLogs.class))), + @ApiResponse(responseCode = "403", description = "The audit log is not readable (configured to be write-only).") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public SecurityAuditLogs getSecurityAuditLogs(@QueryParam("username") String username, - @QueryParam("offset") int offset, - @DefaultValue("100") @QueryParam("limit") int limit, @QueryParam("startTime") long startTime, - @QueryParam("endTime") long endTime) throws ServiceException { - + public SecurityAuditLogs getSecurityAuditLogs( + @Parameter(description = "The name of a user for which to filter events.", example = "admin", required = false) @QueryParam("username") String username, + @Parameter(description = "Number of log entries to skip.", example = "0", required = false) @QueryParam("offset") int offset, + @Parameter(description = "Number of log entries to retrieve.", example = "100", required = false) @DefaultValue("100") @QueryParam("limit") int limit, + @Parameter(description = "Oldest timestamp of range of logs to retrieve. 0 for 'forever'.", required = false) @QueryParam("startTime") long startTime, + @Parameter(description = "Most recent timestamp of range of logs to retrieve. 0 for 'now'.", required = false) @QueryParam("endTime") long endTime) + throws ServiceException + { return securityAuditLogController.getSecurityAuditLogs(username, offset, limit, startTime, endTime); } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/SessionService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/SessionService.java index fd81c077ad..eccb0ebb06 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/SessionService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/SessionService.java @@ -1,19 +1,22 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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.tags.Tag; import org.jivesoftware.openfire.plugin.rest.controller.SessionController; import org.jivesoftware.openfire.plugin.rest.entity.SessionEntities; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + @Path("restapi/v1/sessions") +@Tag(name = "Client Sessions", description = "Managing live client sessions.") public class SessionService { private SessionController sessionController; @@ -24,6 +27,11 @@ public void init() { } @GET + @Operation( summary = "Get all sessions", + description = "Retrieve all live client sessions.", + responses = { + @ApiResponse(responseCode = "200", description = "The client sessions currently active in Openfire.", content = @Content(schema = @Schema(implementation = SessionEntities.class))), + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public SessionEntities getAllSessions() throws ServiceException { return sessionController.getAllSessions(); @@ -31,14 +39,30 @@ public SessionEntities getAllSessions() throws ServiceException { @GET @Path("/{username}") + @Operation( summary = "Get user sessions", + description = "Retrieve all live client sessions for a particular user.", + responses = { + @ApiResponse(responseCode = "200", description = "The client sessions for one particular user that are currently active in Openfire.", content = @Content(schema = @Schema(implementation = SessionEntities.class))), + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public SessionEntities getUserSessions(@PathParam("username") String username) throws ServiceException { + public SessionEntities getUserSessions( + @Parameter(description = "The name of a user for which to return client sessions.", required = true, example = "johndoe") @PathParam("username") String username) + throws ServiceException + { return sessionController.getUserSessions(username); } @DELETE + @Operation( summary = "Kick user sessions", + description = "Close/disconnect all live client sessions for a particular user.", + responses = { + @ApiResponse(responseCode = "200", description = "The client sessions for one particular user have been closed."), + }) @Path("/{username}") - public Response kickSession(@PathParam("username") String username) throws ServiceException { + public Response kickSession( + @Parameter(description = "The name of a user for which to drop all client sessions.", required = true, example = "johndoe") @PathParam("username") String username) + throws ServiceException + { sessionController.removeUserSessions(username); return Response.status(Response.Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/StatisticsService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/StatisticsService.java index 3e6fd843cf..e1d41c6b4c 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/StatisticsService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/StatisticsService.java @@ -1,16 +1,22 @@ package org.jivesoftware.openfire.plugin.rest.service; +import io.swagger.v3.oas.annotations.Operation; +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.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.StatisticsController; +import org.jivesoftware.openfire.plugin.rest.entity.SessionsCount; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + import javax.annotation.PostConstruct; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import org.jivesoftware.openfire.plugin.rest.controller.StatisticsController; -import org.jivesoftware.openfire.plugin.rest.entity.SessionsCount; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; - @Path("restapi/v1/system/statistics") +@Tag(name = "Statistics", description = "Inspecting Openfire statistics.") public class StatisticsService { private StatisticsController controller; @@ -22,6 +28,11 @@ public void init() { @GET @Path("/sessions") + @Operation( summary = "Get client session counts", + description = "Retrieve statistics on the amount of client sessions.", + responses = { + @ApiResponse(responseCode = "200", description = "The requested statistics.", content = @Content(schema = @Schema(implementation = SessionsCount.class))), + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public SessionsCount getCCS() throws ServiceException { return controller.getConcurentSessions(); diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserGroupService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserGroupService.java index 9de6da5210..3a2e406c9c 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserGroupService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserGroupService.java @@ -1,20 +1,23 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.jivesoftware.openfire.plugin.rest.controller.UserServiceController; import org.jivesoftware.openfire.plugin.rest.entity.UserGroupsEntity; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + @Path("restapi/v1/users/{username}/groups") +@Tag(name = "Users", description = "Managing Openfire users.") public class UserGroupService { private UserServiceController plugin; @@ -25,37 +28,81 @@ public void init() { } @GET + @Operation( summary = "Get user's groups", + description = "Retrieve names of all groups that a particular user is in.", + responses = { + @ApiResponse(responseCode = "200", description = "The names of the groups that the user is in.", content = @Content(schema = @Schema(implementation = UserGroupsEntity.class))), + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public UserGroupsEntity getUserGroups(@PathParam("username") String username) throws ServiceException { + public UserGroupsEntity getUserGroups( + @Parameter(description = "The username for user for which to return group names.", required = true) @PathParam("username") String username) + throws ServiceException + { return new UserGroupsEntity(plugin.getUserGroups(username)); } @POST - public Response addUserToGroups(@PathParam("username") String username, UserGroupsEntity userGroupsEntity) - throws ServiceException { + @Operation( summary = "Add user to groups", + description = "Add a particular user to a collection of groups.", + responses = { + @ApiResponse(responseCode = "201", description = "The user was added to all groups."), + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response addUserToGroups( + @Parameter(description = "The username of the user that is to be added to groups.", required = true) @PathParam("username") String username, + @RequestBody(description = "A collection of names for groups that the user is to be added to.", required = true) UserGroupsEntity userGroupsEntity) + throws ServiceException + { plugin.addUserToGroups(username, userGroupsEntity); return Response.status(Response.Status.CREATED).build(); } @POST @Path("/{groupName}") - public Response addUserToGroup(@PathParam("username") String username, @PathParam("groupName") String groupName) - throws ServiceException { + @Operation( summary = "Add user to group", + description = "Add a particular user to a particular group.", + responses = { + @ApiResponse(responseCode = "201", description = "The user was added to the groups."), + }) + public Response addUserToGroup( + @Parameter(description = "The username of the user that is to be added to a group.", required = true) @PathParam("username") String username, + @Parameter(description = "The name of the group that the user is to be added to.", required = true) @PathParam("groupName") String groupName) + throws ServiceException + { plugin.addUserToGroup(username, groupName); return Response.status(Response.Status.CREATED).build(); } @DELETE @Path("/{groupName}") - public Response deleteUserFromGroup(@PathParam("username") String username, @PathParam("groupName") String groupName) - throws ServiceException { + @Operation( summary = "Delete user from group", + description = "Removes a user from a group.", + responses = { + @ApiResponse(responseCode = "200", description = "The user was taken out of the group."), + @ApiResponse(responseCode = "404", description = "The group could not be found."), + }) + public Response deleteUserFromGroup( + @Parameter(description = "The username of the user that is to be removed from a group.", required = true) @PathParam("username") String username, + @Parameter(description = "The name of the group that the user is to be removed from.", required = true) @PathParam("groupName") String groupName) + throws ServiceException + { plugin.deleteUserFromGroup(username, groupName); return Response.status(Response.Status.OK).build(); } @DELETE - public Response deleteUserFromGroups(@PathParam("username") String username, UserGroupsEntity userGroupsEntity) - throws ServiceException { + @Operation( summary = "Delete user from groups", + description = "Removes a user from a collection of groups.", + responses = { + @ApiResponse(responseCode = "200", description = "The user was taken out of the group."), + @ApiResponse(responseCode = "404", description = "One or more groups could not be found."), + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response deleteUserFromGroups( + @Parameter(description = "The username of the user that is to be removed from a group.", required = true) @PathParam("username") String username, + @RequestBody(description = "A collection of names for groups from which the user is to be removed.", required = true) UserGroupsEntity userGroupsEntity) + throws ServiceException + { plugin.deleteUserFromGroups(username, userGroupsEntity); return Response.status(Response.Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserLockoutService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserLockoutService.java index 78e14decfa..ffc46a6a3d 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserLockoutService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserLockoutService.java @@ -1,5 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.service; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.controller.UserServiceController; +import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; + import javax.annotation.PostConstruct; import javax.ws.rs.DELETE; import javax.ws.rs.POST; @@ -7,10 +14,8 @@ import javax.ws.rs.PathParam; import javax.ws.rs.core.Response; -import org.jivesoftware.openfire.plugin.rest.controller.UserServiceController; -import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; - @Path("restapi/v1/lockouts") +@Tag(name = "Users", description = "Managing Openfire users.") public class UserLockoutService { private UserServiceController plugin; @@ -22,14 +27,32 @@ public void init() { @POST @Path("/{username}") - public Response disableUser(@PathParam("username") String username) throws ServiceException { + @Operation( summary = "Lock user out", + description = "Lockout / ban the user from the chat server. The user will be kicked if the user is online.", + responses = { + @ApiResponse(responseCode = "201", description = "The user was locked out."), + @ApiResponse(responseCode = "404", description = "No user of with this username exists.") + }) + public Response disableUser( + @Parameter(description = "The username of the user that is to be locked out.", required = true) @PathParam("username") String username) + throws ServiceException + { plugin.disableUser(username); return Response.status(Response.Status.CREATED).build(); } @DELETE @Path("/{username}") - public Response enableUser(@PathParam("username") String username) throws ServiceException { + @Operation( summary = "Unlock user", + description = "Removes a previously applied lockout / ban of a user.", + responses = { + @ApiResponse(responseCode = "200", description = "User is unlocked."), + @ApiResponse(responseCode = "404", description = "No user of with this username exists.") + }) + public Response enableUser( + @Parameter(description = "The username of the user for which the lockout is to be undone.", required = true) @PathParam("username") String username) + throws ServiceException + { plugin.enableUser(username); return Response.status(Response.Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserRosterService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserRosterService.java index cb8067460a..b252027cf3 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserRosterService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserRosterService.java @@ -1,16 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.jivesoftware.openfire.SharedGroupException; import org.jivesoftware.openfire.plugin.rest.controller.UserServiceController; import org.jivesoftware.openfire.plugin.rest.entity.RosterEntities; @@ -20,7 +16,13 @@ import org.jivesoftware.openfire.user.UserAlreadyExistsException; import org.jivesoftware.openfire.user.UserNotFoundException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + @Path("restapi/v1/users/{username}/roster") +@Tag(name = "Users", description = "Managing Openfire users.") public class UserRosterService { private static final String COULD_NOT_UPDATE_THE_ROSTER = "Could not update the roster"; @@ -35,14 +37,32 @@ public void init() { } @GET + @Operation( summary = "Retrieve user roster", + description = "Get a list of all roster entries (buddies / contact list) of a particular user.", + responses = { + @ApiResponse(responseCode = "200", description = "All roster entries", content = @Content(schema = @Schema(implementation = RosterEntities.class))), + @ApiResponse(responseCode = "404", description = "No user of with this username exists.") + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public RosterEntities getUserRoster(@PathParam("username") String username) throws ServiceException { + public RosterEntities getUserRoster(@Parameter(description = "The username of the user for which the retrieve the roster entries.", required = true) @PathParam("username") String username) throws ServiceException { return plugin.getRosterEntities(username); } @POST - public Response createRoster(@PathParam("username") String username, RosterItemEntity rosterItemEntity) - throws ServiceException { + @Operation( summary = "Create roster entry", + description = "Add a roster entry to the roster (buddies / contact list) of a particular user.", + responses = { + @ApiResponse(responseCode = "201", description = "The entry was added to the roster."), + @ApiResponse(responseCode = "400", description = "A roster entry cannot be added to a 'shared group' (try removing group names from the roster entry and try again)."), + @ApiResponse(responseCode = "404", description = "No user of with this username exists."), + @ApiResponse(responseCode = "409", description = "A roster entry already exists for the provided contact JID.") + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response createRoster( + @Parameter(description = "The username of the user for which the add a roster entry.", required = true) @PathParam("username") String username, + @RequestBody(description = "The definition of the roster entry that is to be added.", required = true) RosterItemEntity rosterItemEntity) + throws ServiceException + { try { plugin.addRosterItem(username, rosterItemEntity); } catch (UserNotFoundException e) { @@ -60,8 +80,18 @@ public Response createRoster(@PathParam("username") String username, RosterItemE @DELETE @Path("/{rosterJid}") - public Response deleteRoster(@PathParam("username") String username, @PathParam("rosterJid") String rosterJid) - throws ServiceException { + @Operation( summary = "Remove roster entry", + description = "Removes one of the roster entries (contacts) of a particular user.", + responses = { + @ApiResponse(responseCode = "200", description = "Entry removed"), + @ApiResponse(responseCode = "400", description = "A roster entry cannot be removed from a 'shared group'."), + @ApiResponse(responseCode = "404", description = "No user of with this username exists, or its roster did not contain this entry.") + }) + public Response deleteRoster( + @Parameter(description = "The username of the user for which the remove a roster entry.", required = true) @PathParam("username") String username, + @Parameter(description = "The JID of the entry/contact to remove.", required = true) @PathParam("rosterJid") String rosterJid) + throws ServiceException + { try { plugin.deleteRosterItem(username, rosterJid); } catch (SharedGroupException e) { @@ -73,8 +103,21 @@ public Response deleteRoster(@PathParam("username") String username, @PathParam( @PUT @Path("/{rosterJid}") - public Response updateRoster(@PathParam("username") String username, @PathParam("rosterJid") String rosterJid, - RosterItemEntity rosterItemEntity) throws ServiceException { + @Operation( summary = "Update roster entry", + description = "Changes a roster entry on the roster (buddies / contact list) of a particular user.", + responses = { + @ApiResponse(responseCode = "200", description = "The roster entry was updated."), + @ApiResponse(responseCode = "400", description = "A roster entry cannot be added with a 'shared group'."), + @ApiResponse(responseCode = "404", description = "No user of with this username exists."), + @ApiResponse(responseCode = "409", description = "A roster entry already exists for the provided contact JID.") + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response updateRoster( + @Parameter(description = "The username of the user for which the update a roster entry.", required = true) @PathParam("username") String username, + @Parameter(description = "The JID of the entry/contact to update.", required = true) @PathParam("rosterJid") String rosterJid, + @RequestBody(description = "The updated definition of the roster entry.", required = true) RosterItemEntity rosterItemEntity) + throws ServiceException + { try { plugin.updateRosterItem(username, rosterJid, rosterItemEntity); } catch (UserNotFoundException e) { diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserService.java index c99a63a18f..d05dc844e7 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserService.java @@ -1,23 +1,24 @@ package org.jivesoftware.openfire.plugin.rest.service; -import javax.annotation.PostConstruct; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.jivesoftware.openfire.plugin.rest.controller.UserServiceController; import org.jivesoftware.openfire.plugin.rest.entity.UserEntities; import org.jivesoftware.openfire.plugin.rest.entity.UserEntity; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import javax.annotation.PostConstruct; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + @Path("restapi/v1/users") +@Tag(name = "Users", description = "Managing Openfire users.") public class UserService { private UserServiceController plugin; @@ -28,36 +29,77 @@ public void init() { } @GET + @Operation( summary = "Get users", + description = "Retrieve all users defined in Openfire (with optional filtering).", + responses = { + @ApiResponse(responseCode = "200", description = "A list of Openfire users.", content = @Content(schema = @Schema(implementation = UserEntities.class))), + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public UserEntities getUsers(@QueryParam("search") String userSearch, - @QueryParam("propertyKey") String propertyKey, @QueryParam("propertyValue") String propertyValue) - throws ServiceException { + public UserEntities getUsers( + @Parameter(description = "Search/Filter by username. This act like the wildcard search %String%", required = false) @QueryParam("search") String userSearch, + @Parameter(description = "Filter by a user property name.", required = false) @QueryParam("propertyKey") String propertyKey, + @Parameter(description = "Filter by user property value. Note: This can only be used in combination with a property name parameter", required = false) @QueryParam("propertyValue") String propertyValue) + throws ServiceException + { return plugin.getUserEntities(userSearch, propertyKey, propertyValue); } @POST - public Response createUser(UserEntity userEntity) throws ServiceException { + @Operation( summary = "Create user", + description = "Add a new user to Openfire.", + responses = { + @ApiResponse(responseCode = "201", description = "The user was created."), + }) + @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response createUser( + @RequestBody(description = "The definition of the user to create.", required = true) UserEntity userEntity) + throws ServiceException + { plugin.createUser(userEntity); return Response.status(Response.Status.CREATED).build(); } @GET @Path("/{username}") + @Operation( summary = "Get user", + description = "Retrieve a user that is defined in Openfire.", + responses = { + @ApiResponse(responseCode = "200", description = "A list of Openfire users.", content = @Content(schema = @Schema(implementation = UserEntity.class))), + @ApiResponse(responseCode = "404", description = "No user with that username was found."), + }) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public UserEntity getUser(@PathParam("username") String username) throws ServiceException { + public UserEntity getUser( + @Parameter(description = "The username of the user to return.", required = true) @PathParam("username") String username) + throws ServiceException + { return plugin.getUserEntity(username); } @PUT @Path("/{username}") - public Response updateUser(@PathParam("username") String username, UserEntity userEntity) throws ServiceException { + @Operation( summary = "Update user", + description = "Update an existing user in Openfire.", + responses = { + @ApiResponse(responseCode = "200", description = "The user was updated."), + }) + public Response updateUser( + @Parameter(description = "The username of the user to update.", required = true) @PathParam("username") String username, + @RequestBody(description = "The definition update of the user.", required = true) UserEntity userEntity) + throws ServiceException + { plugin.updateUser(username, userEntity); return Response.status(Response.Status.OK).build(); } @DELETE @Path("/{username}") - public Response deleteUser(@PathParam("username") String username) throws ServiceException { + @Operation( summary = "Delete user", + description = "Remove an existing user from Openfire.", + responses = { + @ApiResponse(responseCode = "200", description = "The user was removed."), + @ApiResponse(responseCode = "404", description = "No user with that username was found."), + }) + public Response deleteUser(@Parameter(description = "The username of the user to remove.", required = true) @PathParam("username") String username) throws ServiceException { plugin.deleteUser(username); return Response.status(Response.Status.OK).build(); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserServiceLegacy.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserServiceLegacy.java index fa7b653b82..7c6d6338cd 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserServiceLegacy.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserServiceLegacy.java @@ -1,19 +1,7 @@ package org.jivesoftware.openfire.plugin.rest.service; import gnu.inet.encoding.Stringprep; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; - +import io.swagger.v3.oas.annotations.tags.Tag; import org.jivesoftware.openfire.SharedGroupException; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.plugin.rest.RESTServicePlugin; @@ -24,7 +12,19 @@ import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.PrintWriter; + @Path("restapi/v1") +@Tag(name = "UserService (deprecated)", description = "Undocumented UserService endpoint, retained for backwards compatibility.") public class UserServiceLegacy { private static Logger LOG = LoggerFactory.getLogger(UserServiceLegacy.class);