From b00697b71a0c72bf392c98a623ef556b6819863c Mon Sep 17 00:00:00 2001 From: Emiel van der Herberg Date: Mon, 25 Jul 2022 12:01:41 +0200 Subject: [PATCH 1/4] #127 Create multiple chat rooms in one request --- .../rest/controller/MUCRoomController.java | 36 ++++++++++- .../rest/entity/RoomCreationResultEntity.java | 62 +++++++++++++++++++ .../rest/entity/RoomsCreationResult.java | 42 +++++++++++++ .../plugin/rest/service/MUCRoomService.java | 20 ++++++ 4 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java create mode 100644 src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java index 04e0dca61..b1261366a 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java @@ -38,7 +38,6 @@ import org.xmpp.packet.Presence; import javax.annotation.Nonnull; -import javax.servlet.ServletException; import javax.ws.rs.core.Response; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -258,6 +257,41 @@ public void createChatRoom(String serviceName, MUCRoomEntity mucRoomEntity) thro } } + /** + * Creates multiple chat rooms. + * + * @param serviceName + * the service name + * @param mucRoomEntities + * the chat rooms to create + * @return + * a report detailing which creates were successful and which weren't + * @throws ServiceException + * the service exception + */ + public RoomsCreationResult createMultipleChatRooms(String serviceName, MUCRoomEntities mucRoomEntities) throws ServiceException { + List roomsToCreate = mucRoomEntities.getMucRooms(); + log("Create " + roomsToCreate.size() + " chat rooms"); + List results = new ArrayList<>(); + for (MUCRoomEntity roomToCreate : roomsToCreate) { + RoomCreationResultEntity result = new RoomCreationResultEntity(); + result.setRoomName(roomToCreate.getRoomName()); + try { + createRoom(roomToCreate, serviceName); + result.setResultType(RoomCreationResultEntity.RoomCreationResultType.Success); + result.setMessage("Room was successfully created"); + } catch (AlreadyExistsException e) { + result.setResultType(RoomCreationResultEntity.RoomCreationResultType.Success); + result.setMessage("Room already existed and therefore not created again"); + } catch (NotAllowedException | ForbiddenException | ConflictException e) { + result.setResultType(RoomCreationResultEntity.RoomCreationResultType.Failure); + result.setMessage("Room creation failed due to " + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + results.add(result); + } + return new RoomsCreationResult(results); + } + /** * Update chat room. * diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java new file mode 100644 index 000000000..96059f750 --- /dev/null +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.openfire.plugin.rest.entity; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +@XmlRootElement(name = "result") +@XmlType(propOrder = { "roomName", "resultType", "message"}) +public class RoomCreationResultEntity { + + public enum RoomCreationResultType { + Success, Failure + } + + String roomName; + RoomCreationResultType resultType; + String message; + + @XmlElement + public String getRoomName() { + return roomName; + } + + public void setRoomName(String roomName) { + this.roomName = roomName; + } + + @XmlElement + public RoomCreationResultType getResultType() { + return resultType; + } + + public void setResultType(RoomCreationResultType resultType) { + this.resultType = resultType; + } + + @XmlElement + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java new file mode 100644 index 000000000..ca4753a7c --- /dev/null +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.openfire.plugin.rest.entity; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "results") +public class RoomsCreationResult { + List results; + + public RoomsCreationResult() { + } + + public RoomsCreationResult(List results) { + this.results = results; + } + + @XmlElement(name = "result") + public List getResults() { + return results; + } + + public void setResults(List results) { + this.results = results; + } +} 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 c01c24f13..5797914ac 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java @@ -118,6 +118,26 @@ public Response createMUCRoom( return Response.status(Status.CREATED).build(); } + @POST + @Path("/bulk") + @Operation( summary = "Create multiple chat rooms", + description = "Create a number of new multi-user chat rooms.", + responses = { + @ApiResponse(responseCode = "200", description = "Request has been processed. Results are reported in the response.", content = @Content(schema = @Schema(implementation = RoomsCreationResult.class))), + @ApiResponse(responseCode = "401", description = "Web service authentication failed.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "404", description = "MUC Service does not exist or is not accessible.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "500", description = "Unexpected, generic error condition.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public RoomsCreationResult createMUCRooms( + @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 rooms that need to be created.", required = true) MUCRoomEntities mucRoomEntities) + throws ServiceException + { + return MUCRoomController.getInstance().createMultipleChatRooms(serviceName, mucRoomEntities); + } + @PUT @Path("/{roomName}") @Operation( summary = "Update chat room", From ca7f902e5a2d9483affc6472241dfa1875156948 Mon Sep 17 00:00:00 2001 From: Emiel van der Herberg Date: Mon, 25 Jul 2022 12:26:50 +0200 Subject: [PATCH 2/4] #127 Improved naming consistency --- .../openfire/plugin/rest/controller/MUCRoomController.java | 4 ++-- ...sCreationResult.java => RoomCreationResultEntities.java} | 6 +++--- .../openfire/plugin/rest/service/MUCRoomService.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/java/org/jivesoftware/openfire/plugin/rest/entity/{RoomsCreationResult.java => RoomCreationResultEntities.java} (87%) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java index b1261366a..378466f63 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java @@ -269,7 +269,7 @@ public void createChatRoom(String serviceName, MUCRoomEntity mucRoomEntity) thro * @throws ServiceException * the service exception */ - public RoomsCreationResult createMultipleChatRooms(String serviceName, MUCRoomEntities mucRoomEntities) throws ServiceException { + public RoomCreationResultEntities createMultipleChatRooms(String serviceName, MUCRoomEntities mucRoomEntities) throws ServiceException { List roomsToCreate = mucRoomEntities.getMucRooms(); log("Create " + roomsToCreate.size() + " chat rooms"); List results = new ArrayList<>(); @@ -289,7 +289,7 @@ public RoomsCreationResult createMultipleChatRooms(String serviceName, MUCRoomEn } results.add(result); } - return new RoomsCreationResult(results); + return new RoomCreationResultEntities(results); } /** diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java similarity index 87% rename from src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java rename to src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java index ca4753a7c..554d7a857 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomsCreationResult.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java @@ -21,13 +21,13 @@ import java.util.List; @XmlRootElement(name = "results") -public class RoomsCreationResult { +public class RoomCreationResultEntities { List results; - public RoomsCreationResult() { + public RoomCreationResultEntities() { } - public RoomsCreationResult(List results) { + public RoomCreationResultEntities(List results) { this.results = results; } 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 5797914ac..1f63d42f7 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java @@ -123,14 +123,14 @@ public Response createMUCRoom( @Operation( summary = "Create multiple chat rooms", description = "Create a number of new multi-user chat rooms.", responses = { - @ApiResponse(responseCode = "200", description = "Request has been processed. Results are reported in the response.", content = @Content(schema = @Schema(implementation = RoomsCreationResult.class))), + @ApiResponse(responseCode = "200", description = "Request has been processed. Results are reported in the response.", content = @Content(schema = @Schema(implementation = RoomCreationResultEntities.class))), @ApiResponse(responseCode = "401", description = "Web service authentication failed.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), @ApiResponse(responseCode = "404", description = "MUC Service does not exist or is not accessible.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), @ApiResponse(responseCode = "500", description = "Unexpected, generic error condition.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) }) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public RoomsCreationResult createMUCRooms( + public RoomCreationResultEntities createMUCRooms( @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 rooms that need to be created.", required = true) MUCRoomEntities mucRoomEntities) throws ServiceException From 9298c1a453acbcdaeae05ee46d27bd2fbc18ee4c Mon Sep 17 00:00:00 2001 From: Emiel van der Herberg Date: Sat, 30 Jul 2022 22:43:43 +0200 Subject: [PATCH 3/4] #127 Added the required documentation --- changelog.html | 5 + readme.md | 112 +++++++++++++++++- .../rest/entity/RoomCreationResultEntity.java | 5 + 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/changelog.html b/changelog.html index 4e25edc69..bf90312ac 100644 --- a/changelog.html +++ b/changelog.html @@ -44,6 +44,11 @@

REST API Plugin Changelog

+

Next Release at some date

+
    +
  • [#127] - AAdd endpoint that allows for more than one MUC room to be created with one request
  • +
+

1.8.3 July 19, 2022

  • [#124] - Update dependency-check-maven to 7.1.1
  • diff --git a/readme.md b/readme.md index f6247223b..3b6d9e201 100644 --- a/readme.md +++ b/readme.md @@ -732,7 +732,7 @@ Endpoint to get the chat message history of a specified room. | roomname | @Path | Exact room name | | | servicename | @QueryParam | The name of the Group Chat Service | conference | -## Create a chat room +## Create a chat room Endpoint to create a new chat room. >**POST** /chatrooms @@ -917,6 +917,116 @@ Endpoint to create a new chat room. } ``` + + + + + + +## Create multiple chat room +Endpoint to create multiple new chat rooms at once. +>**POST** /chatrooms/bulk + +**Payload:** Chatrooms + +**Return value:** Result list, ordered by successes and failures +```xml + + + + + room1 + Success + Room was successfully created + + + room2 + Success + Room was successfully created + + + + + +``` + +```json +{ + "success": [ + { + "roomName": "room1", + "resultType": "Success", + "message": "Room was successfully created" + }, + { + "roomName": "room2", + "resultType": "Success", + "message": "Room was successfully created" + } + ], + "failure": [], + "other": [] +} +``` +### Possible parameters + +| Parameter | Parameter Type | Description | Default value | +|-------------|-----------------|-------------------------------------|---------------| +| servicename | @QueryParam | The name of the Group Chat Service | conference | + +### XML Examples + +>**Header:** Authorization: Basic YWRtaW46MTIzNDU= +> +>**Header:** Content-Type: application/xml +> +>**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/bulk + +**Payload Example:** +```xml + + + + room1 + description1 + + + room2 + description1 + + +``` + +For more examples, with more parameters, see the [create a chat room](#create-a-chat-room) endpoint. + +### JSON Examples + +>**Header:** Authorization: Basic YWRtaW46MTIzNDU= +> +>**Header:** Content-Type: application/json +> +>**POST** http://example.org:9090/plugins/restapi/v1/chatrooms + +**Payload Example 1 (required parameters):** +```json +{ + "chatRooms": [ + { "roomName": "room1", "description": "description1" }, + { "roomName": "room2", "description": "description2" } + ] +} +``` + +For more examples, with more parameters, see the [create a chat room](#create-a-chat-room) endpoint. + + + + + + + + + ## Delete a chat room Endpoint to delete a chat room. >**DELETE** /chatrooms/{roomName} diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java index 96059f750..a272475e9 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java @@ -16,6 +16,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @@ -34,6 +36,7 @@ public enum RoomCreationResultType { String message; @XmlElement + @Schema(description = "The name of the room that was to be created", example = "open_chat") public String getRoomName() { return roomName; } @@ -43,6 +46,7 @@ public void setRoomName(String roomName) { } @XmlElement + @Schema(description = "The result of creating the room", example = "Failure") public RoomCreationResultType getResultType() { return resultType; } @@ -52,6 +56,7 @@ public void setResultType(RoomCreationResultType resultType) { } @XmlElement + @Schema(description = "A message describing the result", example = "Room already existed and therefore not created again") public String getMessage() { return message; } From 74f2414ff9355818e6670aefe9704b57284bbe83 Mon Sep 17 00:00:00 2001 From: Emiel van der Herberg Date: Sat, 30 Jul 2022 22:44:36 +0200 Subject: [PATCH 4/4] #127 Improved RoomCreationResultEntities to be sorted by type of result --- .../entity/RoomCreationResultEntities.java | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java index 554d7a857..db18e0662 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java @@ -16,27 +16,74 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; import java.util.List; @XmlRootElement(name = "results") +@XmlType(propOrder = { "successResults", "failureResults", "otherResults" }) public class RoomCreationResultEntities { - List results; + List successResults; + List failureResults; + + // This last list is for if a new result type is defined, but no extra result list is added here - a "catch all" + List otherResults; public RoomCreationResultEntities() { + this.successResults = new ArrayList<>(); + this.failureResults = new ArrayList<>(); + this.otherResults = new ArrayList<>(); } public RoomCreationResultEntities(List results) { - this.results = results; + this(); + addResults(results); + } + + public void addResults(List resultsToAdd) { + resultsToAdd.forEach(this::addResult); + } + + public void addResult(RoomCreationResultEntity resultToAdd) { + switch (resultToAdd.getResultType()) { + case Success: + this.successResults.add(resultToAdd); + break; + case Failure: + this.failureResults.add(resultToAdd); + break; + default: + this.otherResults.add(resultToAdd); + } } @XmlElement(name = "result") - public List getResults() { - return results; + @XmlElementWrapper(name = "success") + @JsonProperty(value = "success") + @Schema(description = "All creation results of type success") + public List getSuccessResults() { + return successResults; } - public void setResults(List results) { - this.results = results; + @XmlElement(name = "result") + @XmlElementWrapper(name = "failure") + @JsonProperty(value = "failure") + @Schema(description = "All creation results of type failure") + public List getFailureResults() { + return failureResults; + } + + @XmlElement(name = "result") + @XmlElementWrapper(name = "other") + @JsonProperty(value = "other") + @Schema(description = "All creation results of a type other than success or failure") + public List getOtherResults() { + return otherResults; } }